Cache sx component definitions in localStorage across page loads
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m30s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m30s
Server computes SHA-256 hash of all component source at startup. Client signals its cached hash via cookie (sx-comp-hash). On full page load: cookie match → server sends empty script tag with just the hash; mismatch → sends full source. Client loads from localStorage on hit, parses inline + caches on miss. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ Setup::
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
@@ -37,12 +38,42 @@ from .html import render as html_render, _render_component
|
||||
# definition files or calling register_components().
|
||||
_COMPONENT_ENV: dict[str, Any] = {}
|
||||
|
||||
# SHA-256 hash (12 hex chars) of all component definitions — used for
|
||||
# client-side localStorage caching.
|
||||
_COMPONENT_HASH: str = ""
|
||||
|
||||
|
||||
def get_component_env() -> dict[str, Any]:
|
||||
"""Return the shared component environment."""
|
||||
return _COMPONENT_ENV
|
||||
|
||||
|
||||
def get_component_hash() -> str:
|
||||
"""Return the current component definitions hash."""
|
||||
return _COMPONENT_HASH
|
||||
|
||||
|
||||
def _compute_component_hash() -> None:
|
||||
"""Recompute _COMPONENT_HASH from all registered Component definitions."""
|
||||
global _COMPONENT_HASH
|
||||
from .parser import serialize
|
||||
parts = []
|
||||
for key in sorted(_COMPONENT_ENV):
|
||||
val = _COMPONENT_ENV[key]
|
||||
if isinstance(val, Component):
|
||||
param_strs = ["&key"] + list(val.params)
|
||||
if val.has_children:
|
||||
param_strs.extend(["&rest", "children"])
|
||||
params_sx = "(" + " ".join(param_strs) + ")"
|
||||
body_sx = serialize(val.body)
|
||||
parts.append(f"(defcomp ~{val.name} {params_sx} {body_sx})")
|
||||
if parts:
|
||||
digest = hashlib.sha256("\n".join(parts).encode()).hexdigest()[:12]
|
||||
_COMPONENT_HASH = digest
|
||||
else:
|
||||
_COMPONENT_HASH = ""
|
||||
|
||||
|
||||
def load_sx_dir(directory: str) -> None:
|
||||
"""Load all .sx files from a directory and register components."""
|
||||
for filepath in sorted(
|
||||
@@ -132,6 +163,8 @@ def register_components(sx_source: str) -> None:
|
||||
all_classes = scan_classes_from_sx(sx_source)
|
||||
val.css_classes = set(all_classes)
|
||||
|
||||
_compute_component_hash()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# sx() — render s-expression from Jinja template
|
||||
|
||||
Reference in New Issue
Block a user