Fix sx.js component kwarg evaluation: distinguish expressions from data

Three issues with the eager kwarg evaluation in renderComponentDOM and
renderStrComponent:

1. Data arrays (e.g. tags list of dicts) were being passed to sxEval
   which tried to call a dict as a function — causing blank pages.
   Fix: only evaluate arrays with a Symbol head (actual expressions);
   pass data arrays through as-is.

2. Expression arrays like (get t "src") inside map lambdas lost their
   scope when deferred — causing "get,t,src" URLs. Fix: eagerly evaluate
   these Symbol-headed expressions in the caller's env.

3. Bare symbol `t` used as boolean in editor.sx threw "Undefined symbol".
   Fix: use `true` literal instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 14:51:07 +00:00
parent e7d5c6734b
commit b7d95a8b4e
2 changed files with 27 additions and 11 deletions

View File

@@ -38,7 +38,7 @@
(div :class "flex items-center gap-[16px] mt-[32px] pt-[16px] border-t border-stone-200"
(select :name "status"
:class "text-[14px] rounded-[4px] border border-stone-200 px-[8px] py-[6px] bg-white text-stone-600"
(option :value "draft" :selected t "Draft")
(option :value "draft" :selected true "Draft")
(option :value "published" "Published"))
(button :type "submit"
:class "px-[20px] py-[6px] bg-stone-700 text-white text-[14px] rounded-[8px] hover:bg-stone-800 transition-colors cursor-pointer" create-label))))

View File

@@ -827,15 +827,23 @@
if (isKw(args[i]) && i + 1 < args.length) {
// Evaluate kwarg values eagerly in the caller's env so expressions
// like (get t "src") resolve while lambda params are still bound.
// Render-only forms (HTML tags, <>, ~comp) go through renderDOM instead.
var v = args[i + 1];
if (typeof v === "string" || typeof v === "number" ||
typeof v === "boolean" || isNil(v)) {
typeof v === "boolean" || isNil(v) || isKw(v)) {
kwargs[args[i].name] = v;
} else if (_isRenderExpr(v)) {
kwargs[args[i].name] = renderDOM(v, env);
} else {
} else if (isSym(v)) {
kwargs[args[i].name] = sxEval(v, env);
} else if (Array.isArray(v) && v.length && isSym(v[0])) {
// Expression with Symbol head — evaluate in caller's env.
// Render-only forms go through renderDOM; data exprs through sxEval.
if (_isRenderExpr(v)) {
kwargs[args[i].name] = renderDOM(v, env);
} else {
kwargs[args[i].name] = sxEval(v, env);
}
} else {
// Data arrays, dicts, etc — pass through as-is
kwargs[args[i].name] = v;
}
i += 2;
} else {
@@ -1115,15 +1123,23 @@
if (isKw(args[i]) && i + 1 < args.length) {
// Evaluate kwarg values eagerly in the caller's env so expressions
// like (get t "src") resolve while lambda params are still bound.
// Render-only forms (HTML tags, <>, ~comp) go through renderStr.
var v = args[i + 1];
if (typeof v === "string" || typeof v === "number" ||
typeof v === "boolean" || isNil(v)) {
typeof v === "boolean" || isNil(v) || isKw(v)) {
kwargs[args[i].name] = v;
} else if (_isRenderExpr(v)) {
kwargs[args[i].name] = new RawHTML(renderStr(v, env));
} else {
} else if (isSym(v)) {
kwargs[args[i].name] = sxEval(v, env);
} else if (Array.isArray(v) && v.length && isSym(v[0])) {
// Expression with Symbol head — evaluate in caller's env.
// Render-only forms go through renderStr; data exprs through sxEval.
if (_isRenderExpr(v)) {
kwargs[args[i].name] = new RawHTML(renderStr(v, env));
} else {
kwargs[args[i].name] = sxEval(v, env);
}
} else {
// Data arrays, dicts, etc — pass through as-is
kwargs[args[i].name] = v;
}
i += 2;
} else { children.push(args[i]); i++; }