From 1c64d1f431e4ce0b37ed697aa23fc04233087ff8 Mon Sep 17 00:00:00 2001 From: Daniel Fichtinger Date: Sun, 13 Jul 2025 16:54:04 -0400 Subject: [PATCH] make injection script template a data file --- src/zona/data/server/inject.js | 6 ++++++ src/zona/server.py | 21 ++++++++++--------- src/zona/util.py | 37 +++++++++++++++++++++++++++++----- 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 src/zona/data/server/inject.js diff --git a/src/zona/data/server/inject.js b/src/zona/data/server/inject.js new file mode 100644 index 0000000..550830f --- /dev/null +++ b/src/zona/data/server/inject.js @@ -0,0 +1,6 @@ +const ws = new WebSocket("__SOCKET_ADDRESS_"); +ws.onmessage = event => { + if (event.data === "reload") { + location.reload(); + } +}; diff --git a/src/zona/server.py b/src/zona/server.py index fa0c033..b2e0a64 100644 --- a/src/zona/server.py +++ b/src/zona/server.py @@ -13,6 +13,7 @@ from rich import print from watchdog.events import FileSystemEvent, FileSystemEventHandler from watchdog.observers import Observer +from zona import util from zona.builder import ZonaBuilder from zona.log import get_logger from zona.websockets import WebSocketServer @@ -22,16 +23,16 @@ logger = get_logger() def make_reload_script(host: str, port: int) -> str: """Generates the JavaScript that must be injected into HTML pages for the live reloading to work.""" - return f""" - -""" + js = util.get_resource("server/inject.js").contents + js = util.minify_js(js) + address = f"ws://{host}:{port}" + for placeholder, value in (("__SOCKET_ADDRESS_", address),): + if placeholder not in js: + raise ValueError( + f"{placeholder} missing from reload script template!" + ) + js = js.replace(placeholder, value) + return f"" def make_handler_class(script: str): diff --git a/src/zona/util.py b/src/zona/util.py index 7e80500..2d5c514 100644 --- a/src/zona/util.py +++ b/src/zona/util.py @@ -1,4 +1,5 @@ import fnmatch +import re import string from importlib import resources from importlib.resources.abc import Traversable @@ -12,6 +13,15 @@ class ZonaResource(NamedTuple): contents: str +def get_resource(path: str) -> ZonaResource: + """Load the packaged resource in data/path""" + file = resources.files("zona").joinpath("data", path) + if file.is_file(): + return ZonaResource(name=path, contents=file.read_text()) + else: + raise FileNotFoundError(f"{path} is not a valid Zona resource!") + + def get_resources(subdir: str) -> list[ZonaResource]: """Load the packaged resources in data/subdir""" out: list[ZonaResource] = [] @@ -65,11 +75,28 @@ def normalize_url(url: str) -> str: return url -def should_ignore( - path: Path, patterns: list[str], base: Path -) -> bool: +def should_ignore(path: Path, patterns: list[str], base: Path) -> bool: rel_path = path.relative_to(base) return any( - fnmatch.fnmatch(str(rel_path), pattern) - for pattern in patterns + fnmatch.fnmatch(str(rel_path), pattern) for pattern in patterns ) + + +MINIFY_JS_PATTERN = re.compile( + r""" + //.*?$ | + /\*.*?\*/ | + \s+ + """, + re.MULTILINE | re.DOTALL | re.VERBOSE, +) + + +def minify_js(js: str) -> str: + """Naively minifies JavaScript by stripping comments and whitespace.""" + return MINIFY_JS_PATTERN.sub( + # replace whitespace with single space, + # strip comments + lambda m: " " if m.group(0).isspace() else "", + js, + ).strip()