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()