From 09d06a4c87131a7f044a2048c929ce7051899679 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 7 Mar 2026 01:48:47 +0000 Subject: [PATCH] Filter data page deps by IO purity: only bundle pure component trees Pages whose component trees reference IO primitives (e.g. highlight) cannot render client-side, so exclude their deps from the client bundle. This prevents "Undefined symbol: highlight" errors on pages like bundle-analyzer while still allowing pure data pages like data-test to render client-side with caching. Co-Authored-By: Claude Opus 4.6 --- shared/sx/jinja_bridge.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/shared/sx/jinja_bridge.py b/shared/sx/jinja_bridge.py index 57860a8..35bff0c 100644 --- a/shared/sx/jinja_bridge.py +++ b/shared/sx/jinja_bridge.py @@ -349,14 +349,23 @@ def components_for_page(page_sx: str, service: str | None = None) -> tuple[str, needed = components_needed(page_sx, _COMPONENT_ENV) - # Include deps for all :data pages so the client can render them + # Include deps for :data pages whose component trees are fully pure + # (no IO refs). Pages with IO deps must render server-side. if service: from .pages import get_all_pages for page_def in get_all_pages(service).values(): if page_def.data_expr is not None and page_def.content_expr is not None: content_src = serialize(page_def.content_expr) data_deps = components_needed(content_src, _COMPONENT_ENV) - needed |= data_deps + # Check if any dep component has IO refs + has_io = False + for dep_name in data_deps: + comp = _COMPONENT_ENV.get(dep_name) + if isinstance(comp, Component) and comp.io_refs: + has_io = True + break + if not has_io: + needed |= data_deps if not needed: return "", "" @@ -407,7 +416,13 @@ def css_classes_for_page(page_sx: str, service: str | None = None) -> set[str]: for page_def in get_all_pages(service).values(): if page_def.data_expr is not None and page_def.content_expr is not None: content_src = serialize(page_def.content_expr) - needed |= components_needed(content_src, _COMPONENT_ENV) + data_deps = components_needed(content_src, _COMPONENT_ENV) + has_io = any( + isinstance(_COMPONENT_ENV.get(d), Component) and _COMPONENT_ENV.get(d).io_refs + for d in data_deps + ) + if not has_io: + needed |= data_deps classes: set[str] = set() for key, val in _COMPONENT_ENV.items():