#!/usr/bin/env python3
"""Regenerate index.html CSS/script tags and service-worker.js from app_manifest.json.
This project intentionally stays buildless; this script is a lightweight manifest sync tool.
"""
from __future__ import annotations
from pathlib import Path
import json
import re
ROOT = Path(__file__).resolve().parents[1]
manifest = json.loads((ROOT / "app_manifest.json").read_text(encoding="utf-8"))
version = manifest["version"]
css_files = manifest["css"]
js_files = manifest["js"]
index = ROOT / "index.html"
s = index.read_text(encoding="utf-8")
css_block = "".join([f'\n ' for f in css_files])
s = re.sub(
r'\s*(?:\s*)*',
css_block,
s,
count=1,
)
script_block = "\n".join([f' ' for f in js_files])
s = re.sub(
r'\n\s*[\s\S]*?\n\s*',
"\n" + script_block,
s,
count=1,
)
index.write_text(s, encoding="utf-8")
core_names = [Path(f).stem for f in js_files]
css_entries = ",\n ".join([f'`./{f}?${{v}}`' for f in css_files])
js_names = ", ".join([json.dumps(n, ensure_ascii=False) for n in core_names])
(ROOT / "service-worker.js").write_text(f'''"use strict";\n\nconst APP_VERSION = "{version}";\nconst CACHE_NAME = `tarinai-colony-${{APP_VERSION}}`;\nconst v = `v=${{APP_VERSION}}`;\n// Generated from app_manifest.json. Run scripts/generate_app_files.py after changing static files.\nconst coreScriptNames = [\n {js_names}\n];\nconst CORE_ASSETS = [\n "./",\n "./index.html",\n {css_entries},\n ...coreScriptNames.map(name => `./js/${{name}}.js?${{v}}`),\n];\n\nself.addEventListener("install", (event) => {{\n event.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(CORE_ASSETS)).catch(() => undefined));\n self.skipWaiting();\n}});\n\nself.addEventListener("activate", (event) => {{\n event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key)))));\n self.clients.claim();\n}});\n\nfunction isStaticAsset(request) {{\n const url = new URL(request.url);\n return /\\.(?:js|css|webp|png|jpg|jpeg|gif|svg|mp3|wav|woff2?)$/i.test(url.pathname);\n}}\n\nfunction cacheableResponse(response) {{\n return response && response.status === 200 && response.type !== "opaque";\n}}\n\nfunction putCacheSafely(request, response) {{\n if (!cacheableResponse(response)) return Promise.resolve(false);\n return caches.open(CACHE_NAME).then(cache => cache.put(request, response.clone())).then(() => true).catch(() => false);\n}}\n\nself.addEventListener("fetch", (event) => {{\n const request = event.request;\n if (request.method !== "GET") return;\n const url = new URL(request.url);\n if (url.origin !== self.location.origin) return;\n if (/service-worker\\.js$/i.test(url.pathname)) return;\n if (request.mode === "navigate" || /index\\.html$/i.test(url.pathname)) {{\n event.respondWith(fetch(request).then(response => {{\n putCacheSafely(request, response);\n return response;\n }}).catch(() => caches.match(request).then(cached => cached || caches.match("./index.html"))));\n return;\n }}\n if (isStaticAsset(request)) {{\n event.respondWith(caches.match(request).then(cached => cached || fetch(request).then(response => {{\n putCacheSafely(request, response);\n return response;\n }})));\n }}\n}});\n''', encoding="utf-8")