release: WRNexusJS 0.2.44
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.44",
|
||||
"version": "0.2.45",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -524,7 +524,13 @@ function hasClientBehavior(nodes: ViewNode[]): boolean {
|
||||
// `{t:key}` is i18n sugar resolved server-side — not client reactivity.
|
||||
if (node.type === "text") return /\{(?!t:)[^{}]+\}/.test(node.value);
|
||||
// Server control blocks render on the server; they don't add client reactivity.
|
||||
if (node.type === "each" || node.type === "if") return false;
|
||||
if (node.type === "each") {
|
||||
return hasClientBehavior(node.body) || hasClientBehavior(node.empty);
|
||||
}
|
||||
|
||||
if (node.type === "if") {
|
||||
return node.branches.some((branch) => hasClientBehavior(branch.body));
|
||||
}
|
||||
return (
|
||||
node.attrs.some((attr) => attr.event || attr.name === "csrGet" || attr.name === "csrText") ||
|
||||
hasClientBehavior(node.children)
|
||||
@@ -1112,6 +1118,16 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
return renderNestedComponentInvocation(node, ctx);
|
||||
}
|
||||
|
||||
const loopVariables = loopVarsOf(node);
|
||||
|
||||
const elementContext =
|
||||
loopVariables.length > 0
|
||||
? {
|
||||
...ctx,
|
||||
loopVars: new Set([...(ctx.loopVars ?? []), ...loopVariables]),
|
||||
}
|
||||
: ctx;
|
||||
|
||||
let bindIndex = 0;
|
||||
const staticClasses: string[] = [];
|
||||
const conditionalClasses: Array<{
|
||||
@@ -1143,9 +1159,15 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
return ` ${a.name}`;
|
||||
}
|
||||
|
||||
const rendered = ` ${a.name}="${compileAttrValue(a.value, ctx)}"`;
|
||||
const rendered = ` ${a.name}="${compileAttrValue(a.value, elementContext)}"`;
|
||||
|
||||
if (!a.value.includes("{") || !exprRefsState(a.value, ctx.stateNames)) {
|
||||
const referencesState = exprRefsState(a.value, ctx.stateNames);
|
||||
|
||||
const referencesLoopVariable = elementContext.loopVars
|
||||
? exprRefsState(a.value, elementContext.loopVars)
|
||||
: false;
|
||||
|
||||
if (!a.value.includes("{") || (!referencesState && !referencesLoopVariable)) {
|
||||
return rendered;
|
||||
}
|
||||
|
||||
@@ -1157,6 +1179,16 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
|
||||
const initialConditionalClasses = conditionalClasses
|
||||
.map(({ className, expression }) => {
|
||||
const referencesLoopVariable = elementContext.loopVars
|
||||
? exprRefsState(expression, elementContext.loopVars)
|
||||
: false;
|
||||
|
||||
// data-for variables do not exist during
|
||||
// initial server rendering.
|
||||
if (referencesLoopVariable) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `\${(${ctx.resolveExpr(expression)}) ? ${JSON.stringify(` ${className}`)} : ""}`;
|
||||
})
|
||||
.join("");
|
||||
@@ -1168,7 +1200,7 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
|
||||
const classAttribute =
|
||||
staticClasses.length > 0 || conditionalClasses.length > 0
|
||||
? ` class="${compileAttrValue(staticClassValue, ctx)}${initialConditionalClasses}"`
|
||||
? ` class="${compileAttrValue(staticClassValue, elementContext)}${initialConditionalClasses}"`
|
||||
: "";
|
||||
|
||||
const classReactiveBinding = classHasReactiveExpression
|
||||
@@ -1186,11 +1218,6 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
})
|
||||
.join("");
|
||||
|
||||
// A `data-for` element introduces loop variables for its subtree.
|
||||
const loops = loopVarsOf(node);
|
||||
const childCtx =
|
||||
loops.length > 0 ? { ...ctx, loopVars: new Set([...(ctx.loopVars ?? []), ...loops]) } : ctx;
|
||||
|
||||
const allAttrs =
|
||||
`${classAttribute}` + `${classReactiveBinding}` + `${classBindings}` + `${attrs}`;
|
||||
|
||||
@@ -1198,7 +1225,7 @@ function renderComponentNode(node: ViewNode, ctx: CompCtx): string {
|
||||
return `<${node.tag}${allAttrs}>`;
|
||||
}
|
||||
|
||||
const inner = node.children.map((child) => renderComponentNode(child, childCtx)).join("");
|
||||
const inner = node.children.map((child) => renderComponentNode(child, elementContext)).join("");
|
||||
|
||||
return `<${node.tag}${allAttrs}>${inner}</${node.tag}>`;
|
||||
}
|
||||
|
||||
@@ -839,3 +839,37 @@ component FAQAccordion {
|
||||
|
||||
expect(output).toContain("isOpen(index)");
|
||||
});
|
||||
test("data-for conditional classes do not evaluate loop variables during SSR", () => {
|
||||
const output = generate(
|
||||
parse(`
|
||||
component Accordion {
|
||||
props {
|
||||
items = []
|
||||
}
|
||||
|
||||
state openIndexes = []
|
||||
|
||||
functions {
|
||||
function isOpen(index) {
|
||||
return openIndexes.includes(index)
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<article
|
||||
data-for="item, index in items"
|
||||
class:open="isOpen(index)"
|
||||
>
|
||||
{item.title}
|
||||
</article>
|
||||
}
|
||||
}
|
||||
`),
|
||||
);
|
||||
|
||||
expect(output).toContain('data-for="item, index in items"');
|
||||
|
||||
expect(output).toContain("data-wrn-class-0");
|
||||
|
||||
expect(output).not.toContain('${(isOpen(index)) ? " open" : ""}');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user