Document SX rendering pipeline, add missing sx_docs mount, loud error on missing component
- CLAUDE.md: add SX rendering pipeline overview, service sx/ vs sxc/ convention, dev container mount convention - docker-compose.dev.yml: add missing ./sx/sx:/app/sx bind mount for sx_docs (root cause of "Unknown component: ~sx-layout-full") - async_eval.py: add evaluation modes table to module docstring; log error when async_eval_slot_to_sx can't find a component instead of silently falling through to client-side serialization - helpers.py: remove debug logging from render_to_sx_with_env Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,28 @@ control flow (``if``, ``let``, ``map``, ``when``). The sync
|
||||
collect-then-substitute resolver can't handle data dependencies between
|
||||
I/O results and control flow, so handlers need inline async evaluation.
|
||||
|
||||
Evaluation modes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The same component AST can be evaluated in different modes depending on
|
||||
where the rendering boundary is drawn (server vs client). Five modes
|
||||
exist across the codebase:
|
||||
|
||||
Function Expands components? Output Used for
|
||||
-------------------- ------------------- -------------- ----------------------------
|
||||
_eval (sync) Yes Python values register_components, Jinja sx()
|
||||
_arender (async) Yes HTML render_to_html
|
||||
_aser (async) No — serializes SX wire format render_to_sx
|
||||
_aser_component Yes, one level SX wire format render_to_sx_with_env (layouts)
|
||||
sx.js renderDOM Yes DOM nodes Client-side
|
||||
|
||||
_aser deliberately does NOT expand ~component calls — it serializes them
|
||||
as SX wire format so the client can render them. But layout components
|
||||
(used by render_to_sx_with_env) need server-side expansion because they
|
||||
depend on Python context (auth state, fragments, etc.). That's what
|
||||
_aser_component / async_eval_slot_to_sx provides: expand the top-level
|
||||
component body server-side, then serialize its children for the client.
|
||||
|
||||
Usage::
|
||||
|
||||
from shared.sx.async_eval import async_render
|
||||
@@ -1021,6 +1043,16 @@ async def async_eval_slot_to_sx(
|
||||
if result is None or result is NIL:
|
||||
return ""
|
||||
return serialize(result)
|
||||
else:
|
||||
import logging
|
||||
logging.getLogger("sx.eval").error(
|
||||
"async_eval_slot_to_sx: component %s not found in env "
|
||||
"(will fall through to _aser and serialize unexpanded — "
|
||||
"client will see 'Unknown component'). "
|
||||
"Check that the .sx file is loaded and the service's sx/ "
|
||||
"directory is bind-mounted in docker-compose.dev.yml.",
|
||||
head.name,
|
||||
)
|
||||
# Fall back to normal async_eval_to_sx
|
||||
result = await _aser(expr, env, ctx)
|
||||
if isinstance(result, SxExpr):
|
||||
|
||||
Reference in New Issue
Block a user