Fix aser server-affinity expansion: keyword values, OOB wrapper, page helpers
Three bugs in aser-expand-component (adapter-sx.sx):
- Keyword values were eval'd (eval-expr can't handle <>, HTML tags);
now asered, matching the aser's rendering capabilities
- Missing default nil binding for unset &key params (caused
"Undefined symbol" errors for optional params like header-rows)
- aserCall string-quoted keyword values that were already serialized
SX — now inlines values starting with "(" directly
Server-affinity annotations for layout/nav shells:
- ~shared:layout/app-body, ~shared:layout/oob-sx — page structure
- ~layouts/nav-sibling-row, ~layouts/nav-children — server-side data
- ~layouts/doc already had :affinity :server
- ~cssx/flush marked :affinity :client (browser-only state)
Navigation fix: restore oob_page_sx wrapper for HTMX responses
so #main-panel section exists for sx-select/sx-swap targeting.
OCaml bridge: lazy page helper injection into kernel via IO proxy
(define name (fn (...) (helper "name" ...))) — enables aser_slot
to evaluate highlight/component-source etc. via coroutine bridge.
Playwright tests: added pageerror listener to test_no_console_errors,
new test_navigate_from_home_to_geography for HTMX nav regression.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -71,13 +71,30 @@
|
||||
(= name "<>")
|
||||
(aser-fragment args env)
|
||||
|
||||
;; Component call — expand server-affinity, serialize others
|
||||
;; raw! — pass through as serialized call
|
||||
(= name "raw!")
|
||||
(aser-call "raw!" args env)
|
||||
|
||||
;; Component call — expand if server-affinity or expand-components? is set.
|
||||
;; expand-components? is a platform primitive (like eval-expr, trampoline);
|
||||
;; adapter-async.sx uses the same pattern at line 684.
|
||||
;; Guard with env-has? for backward compat with older kernels.
|
||||
(starts-with? name "~")
|
||||
(let ((comp (if (env-has? env name) (env-get env name) nil)))
|
||||
(if (and comp (component? comp)
|
||||
(= (component-affinity comp) "server"))
|
||||
(aser-expand-component comp args env)
|
||||
(aser-call name args env)))
|
||||
(let ((comp (if (env-has? env name) (env-get env name) nil))
|
||||
(expand-all (if (env-has? env "expand-components?")
|
||||
(expand-components?) false)))
|
||||
(cond
|
||||
(and comp (macro? comp))
|
||||
(aser (expand-macro comp args env) env)
|
||||
(and comp (component? comp)
|
||||
(or expand-all
|
||||
(= (component-affinity comp) "server"))
|
||||
;; :affinity :client components are never expanded
|
||||
;; server-side — they depend on browser-only state.
|
||||
(not (= (component-affinity comp) "client")))
|
||||
(aser-expand-component comp args env)
|
||||
:else
|
||||
(aser-call name args env)))
|
||||
|
||||
;; Lake — serialize (server-morphable slot)
|
||||
(= name "lake")
|
||||
@@ -175,7 +192,14 @@
|
||||
(let ((val (aser (nth args (inc i)) env)))
|
||||
(when (not (nil? val))
|
||||
(append! attr-parts (str ":" (keyword-name arg)))
|
||||
(append! attr-parts (serialize val)))
|
||||
;; If the aser result is already serialized SX (starts
|
||||
;; with "("), inline it directly — don't re-serialize
|
||||
;; which would quote it as a string literal.
|
||||
(if (and (= (type-of val) "string")
|
||||
(> (string-length val) 0)
|
||||
(starts-with? val "("))
|
||||
(append! attr-parts val)
|
||||
(append! attr-parts (serialize val))))
|
||||
(set! skip true)
|
||||
(set! i (inc i)))
|
||||
(let ((val (aser arg env)))
|
||||
@@ -232,19 +256,24 @@
|
||||
(i 0)
|
||||
(skip false)
|
||||
(children (list)))
|
||||
;; Parse keyword args and positional children from args
|
||||
;; Keyword values are eval'd (they're data). Children are NOT eval'd
|
||||
;; (they may contain HTML tags that only the aser can handle).
|
||||
;; Default all keyword params to nil (same as the CEK evaluator)
|
||||
(for-each (fn (p) (env-bind! local p nil)) params)
|
||||
;; Parse keyword args and positional children from args.
|
||||
;; Keyword values are ASERED (not eval'd) — they may contain
|
||||
;; rendering constructs (<>, HTML tags) that eval-expr can't
|
||||
;; handle. The aser result is a string/value that the body's
|
||||
;; aser will inline correctly (strings starting with "(" are
|
||||
;; recognized as serialized SX by aserCall).
|
||||
(for-each
|
||||
(fn (arg)
|
||||
(if skip
|
||||
(do (set! skip false) (set! i (inc i)))
|
||||
(if (and (= (type-of arg) "keyword")
|
||||
(< (inc i) (len args)))
|
||||
;; Keyword arg: bind name = eval'd next arg
|
||||
;; Keyword arg: bind name = aser'd next arg
|
||||
(do
|
||||
(env-bind! local (keyword-name arg)
|
||||
(trampoline (eval-expr (nth args (inc i)) env)))
|
||||
(aser (nth args (inc i)) env))
|
||||
(set! skip true)
|
||||
(set! i (inc i)))
|
||||
;; Positional child: keep as unevaluated AST for aser
|
||||
|
||||
Reference in New Issue
Block a user