Add component deps to page registry, check before client routing
Each page entry now includes a deps list of component names needed. Client checks all deps are loaded before attempting eval — if any are missing, falls through to server fetch with a clear log message. No bundle bloat: server sends components for the current page only. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -644,12 +644,14 @@ def _build_pages_sx(service: str) -> str:
|
||||
|
||||
Returns SX dict literals (one per page) parseable by the client's
|
||||
``parse`` function. Each dict has keys: name, path, auth, has-data,
|
||||
content, closure.
|
||||
content, closure, deps.
|
||||
"""
|
||||
import logging
|
||||
_log = logging.getLogger("sx.pages")
|
||||
from .pages import get_all_pages
|
||||
from .parser import serialize as sx_serialize
|
||||
from .deps import components_needed
|
||||
from .jinja_bridge import _COMPONENT_ENV
|
||||
|
||||
pages = get_all_pages(service)
|
||||
_log.debug("_build_pages_sx(%s): %d pages in registry", service, len(pages))
|
||||
@@ -666,6 +668,12 @@ def _build_pages_sx(service: str) -> str:
|
||||
auth = page_def.auth if isinstance(page_def.auth, str) else "custom"
|
||||
has_data = "true" if page_def.data_expr is not None else "false"
|
||||
|
||||
# Component deps needed to render this page client-side
|
||||
deps: set[str] = set()
|
||||
if content_src and page_def.data_expr is None:
|
||||
deps = components_needed(content_src, _COMPONENT_ENV)
|
||||
deps_sx = "(" + " ".join(_sx_literal(d) for d in sorted(deps)) + ")"
|
||||
|
||||
# Build closure as SX dict
|
||||
closure_parts: list[str] = []
|
||||
for k, v in page_def.closure.items():
|
||||
@@ -679,6 +687,7 @@ def _build_pages_sx(service: str) -> str:
|
||||
+ " :auth " + _sx_literal(auth)
|
||||
+ " :has-data " + has_data
|
||||
+ " :content " + _sx_literal(content_src)
|
||||
+ " :deps " + deps_sx
|
||||
+ " :closure " + closure_sx + "}"
|
||||
)
|
||||
entries.append(entry)
|
||||
@@ -702,6 +711,7 @@ def _sx_literal(v: object) -> str:
|
||||
return "nil"
|
||||
|
||||
|
||||
|
||||
def sx_page(ctx: dict, page_sx: str, *,
|
||||
meta_html: str = "") -> str:
|
||||
"""Return a minimal HTML shell that boots the page from sx source.
|
||||
|
||||
@@ -543,24 +543,42 @@
|
||||
;; Client-side routing
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define has-all-deps?
|
||||
(fn (deps)
|
||||
;; Check if all component deps are loaded in the client env.
|
||||
;; deps is a list of component name strings (e.g. "~essay-foo").
|
||||
(let ((loaded (loaded-component-names))
|
||||
(missing false))
|
||||
(for-each
|
||||
(fn (dep)
|
||||
(when (not (contains? loaded dep))
|
||||
(set! missing dep)))
|
||||
deps)
|
||||
(if missing
|
||||
(do (log-info (str "sx:route missing component " missing)) false)
|
||||
true))))
|
||||
|
||||
(define try-client-route
|
||||
(fn (pathname)
|
||||
;; Try to render a page client-side. Returns true if successful, false otherwise.
|
||||
;; Only works for pages without :data dependencies.
|
||||
;; Only works for pages without :data dependencies and with all deps loaded.
|
||||
(let ((match (find-matching-route pathname _page-routes)))
|
||||
(if (nil? match)
|
||||
(do (log-info (str "sx:route no match (" (len _page-routes) " routes) " pathname)) false)
|
||||
(if (get match "has-data")
|
||||
(do (log-info (str "sx:route server (has data) " pathname)) false)
|
||||
(let ((content-src (get match "content"))
|
||||
(deps (or (get match "deps") (list)))
|
||||
(closure (or (get match "closure") {}))
|
||||
(params (get match "params")))
|
||||
(if (or (nil? content-src) (empty? content-src))
|
||||
(do (log-warn (str "sx:route no content for " pathname)) false)
|
||||
(let ((env (merge closure params))
|
||||
(rendered (try-eval-content content-src env)))
|
||||
(if (nil? rendered)
|
||||
(do (log-warn (str "sx:route eval failed for " pathname)) false)
|
||||
(if (not (has-all-deps? deps))
|
||||
(do (log-info (str "sx:route server (missing deps) " pathname)) false)
|
||||
(let ((env (merge closure params))
|
||||
(rendered (try-eval-content content-src env)))
|
||||
(if (nil? rendered)
|
||||
(do (log-warn (str "sx:route eval failed for " pathname)) false)
|
||||
(let ((target (dom-query-by-id "main-panel")))
|
||||
(if (nil? target)
|
||||
(do (log-warn "sx:route #main-panel not found") false)
|
||||
@@ -571,7 +589,7 @@
|
||||
(process-elements target)
|
||||
(sx-hydrate-elements target)
|
||||
(log-info (str "sx:route client " pathname))
|
||||
true))))))))))))
|
||||
true)))))))))))))
|
||||
|
||||
|
||||
(define bind-client-route-link
|
||||
|
||||
Reference in New Issue
Block a user