This commit is contained in:
mark committed 2026-09-19 13:10:34 -04:00
1 parent ea1afaf833
commit e0db317146
12 files changed
+444 -56

No files matched your search

+15 -50
View File
@@ -3,9 +3,7 @@
from __future__ import annotations
import argparse
import hashlib
import io
import json
import os
import posixpath
import signal
@@ -13,59 +11,26 @@ import sys
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from worker import WORKER, stamp_worker, walk
NO_CACHE_NAMES = {"sw.js"}
NO_CACHE_EXTS = {".html", ".njk", ".json", ".webmanifest"}
BOOT_PAGE = "/boot.html"
WORKER = "sw.js"
_stamped = (None, None)
VERSION_LINE = "const VERSION = 'dev';"
LIBRARY_VERSION_LINE = "const LIBRARY_VERSION = 'dev';"
LIBRARY_LINE = "const LIBRARY = [];"
def walk(roots):
found = {}
for root in roots:
for dirpath, _dirs, names in os.walk(root):
for name in sorted(names):
path = os.path.join(dirpath, name)
rel = os.path.relpath(path, root).replace(os.sep, "/")
found.setdefault(rel, path)
return found
def digest(files):
out = hashlib.sha256()
for rel in sorted(files):
st = os.stat(files[rel])
out.update(("%s\0%d\0%d\0" % (rel, st.st_size, st.st_mtime_ns)).encode())
return out.hexdigest()[:12]
def page_url(rel):
if rel == "index.html":
return "./"
return "./" + (rel[:-len(".html")] if rel.endswith(".html") else rel)
def stamp_worker(roots):
chrome = walk(roots[:1])
library = {rel: path for rel, path in walk(roots).items() if rel not in chrome}
src = chrome.pop(WORKER, None)
if src is None:
return None
with open(src, encoding="utf-8") as fh:
body = fh.read()
for line, value in (
(VERSION_LINE, "const VERSION = '%s';" % digest(chrome)),
(LIBRARY_VERSION_LINE, "const LIBRARY_VERSION = '%s';" % digest(library)),
(LIBRARY_LINE, "const LIBRARY = %s;"
% json.dumps(sorted(page_url(rel) for rel in library), indent=2)),
):
if line not in body:
raise RuntimeError("%s: expected to find %r to stamp" % (WORKER, line))
body = body.replace(line, value, 1)
return body.encode("utf-8")
def cached_worker(roots):
# Stamping hashes every file the worker lists, 11 MB of them, and browsers
# ask for sw.js on almost every load. Stats are cheap, so redo the stamp
# only when one moves: often under compose, never in a baked image.
global _stamped
key = tuple((rel, st.st_size, st.st_mtime_ns)
for rel, st in ((rel, os.stat(path))
for rel, path in sorted(walk(roots).items())))
if _stamped[0] != key:
_stamped = (key, stamp_worker(roots))
return _stamped[1]
class Handler(SimpleHTTPRequestHandler):
extensions_map = {
@@ -106,7 +71,7 @@ class Handler(SimpleHTTPRequestHandler):
def send_worker(self):
try:
body = stamp_worker(self.roots)
body = cached_worker(self.roots)
except Exception as err:
self.log_message("stamp failed: %s", err)
self.send_error(500, "service worker unavailable")