74 lines
2.4 KiB
Python
74 lines
2.4 KiB
Python
"""Build the content-addressed browser shell served by the dashboard."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import gzip
|
|
import hashlib
|
|
import re
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
|
|
SCRIPT_TAG = re.compile(r'^<script src="(static/[^"?]+\.js)"></script>$', re.MULTILINE)
|
|
WORKER_RUNTIME_SOURCE = "static/background-issue-sync.js"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class FrontendBuild:
|
|
dashboard_html: str
|
|
runtime_bytes: bytes
|
|
runtime_gzip_bytes: bytes
|
|
runtime_digest: str
|
|
runtime_name: str
|
|
page_sources: tuple[str, ...]
|
|
service_worker_source: str
|
|
|
|
|
|
def _bundle(frontend_dir: Path, sources: tuple[str, ...]) -> bytes:
|
|
chunks = []
|
|
for source in sources:
|
|
path = frontend_dir / source.removeprefix("static/")
|
|
chunks.append(f"/* {source} */\n".encode() + path.read_bytes() + b"\n;\n")
|
|
return b"".join(chunks)
|
|
|
|
|
|
def build_frontend(frontend_dir: Path) -> FrontendBuild:
|
|
"""Return one internally consistent HTML, runtime, and offline-worker build."""
|
|
source_html = (frontend_dir / "index.html").read_text()
|
|
sources = tuple(SCRIPT_TAG.findall(source_html))
|
|
if not sources:
|
|
raise ValueError("dashboard entry document has no page scripts")
|
|
|
|
runtime = _bundle(frontend_dir, sources)
|
|
runtime_gzip = gzip.compress(runtime, compresslevel=6, mtime=0)
|
|
digest = hashlib.sha256(runtime).hexdigest()[:16]
|
|
runtime_name = f"runtime-{digest}.js"
|
|
dashboard_html = SCRIPT_TAG.sub("", source_html)
|
|
dashboard_html = dashboard_html.replace(
|
|
"</body>", f'<script src="{runtime_name}"></script>\n</body>'
|
|
)
|
|
|
|
worker = (frontend_dir / "service-worker.js").read_text()
|
|
for source in sources:
|
|
if source != WORKER_RUNTIME_SOURCE:
|
|
worker = worker.replace(f" BASE + '{source}',\n", "")
|
|
worker = worker.replace(
|
|
" BASE + 'static/dashboard.css',\n",
|
|
f" BASE + 'static/dashboard.css',\n BASE + '{runtime_name}',\n",
|
|
)
|
|
worker = re.sub(
|
|
r"const CACHE = 'stackchain-dashboard-shell-v\d+';",
|
|
f"const CACHE = 'stackchain-dashboard-shell-{digest}';",
|
|
worker,
|
|
count=1,
|
|
)
|
|
|
|
return FrontendBuild(
|
|
dashboard_html=dashboard_html,
|
|
runtime_bytes=runtime,
|
|
runtime_gzip_bytes=runtime_gzip,
|
|
runtime_digest=digest,
|
|
runtime_name=runtime_name,
|
|
page_sources=sources,
|
|
service_worker_source=worker,
|
|
) |