diff --git a/.publish/ai/package.json b/.publish/ai/package.json
index 06b78053..30d53aa0 100644
--- a/.publish/ai/package.json
+++ b/.publish/ai/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
"license": "MIT",
diff --git a/.publish/authz/package.json b/.publish/authz/package.json
index f2434602..a5be7595 100644
--- a/.publish/authz/package.json
+++ b/.publish/authz/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/authz — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/cli/package.json b/.publish/cli/package.json
index a7d990dc..e38d70e8 100644
--- a/.publish/cli/package.json
+++ b/.publish/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/cli — part of the WrNexus framework.",
"license": "MIT",
@@ -28,16 +28,16 @@
"wrnexus": "./dist/index.js"
},
"dependencies": {
- "@wrnexus/core": "^0.2.44",
- "@wrnexus/router": "^0.2.44",
- "@wrnexus/csr": "^0.2.44",
- "@wrnexus/compiler": "^0.2.44",
- "@wrnexus/styles": "^0.2.44",
- "@wrnexus/dev-server": "^0.2.44",
- "@wrnexus/ui": "^0.2.44",
- "@wrnexus/validation": "^0.2.44",
- "@wrnexus/i18n": "^0.2.44",
- "@wrnexus/db": "^0.2.44"
+ "@wrnexus/core": "^0.2.45",
+ "@wrnexus/router": "^0.2.45",
+ "@wrnexus/csr": "^0.2.45",
+ "@wrnexus/compiler": "^0.2.45",
+ "@wrnexus/styles": "^0.2.45",
+ "@wrnexus/dev-server": "^0.2.45",
+ "@wrnexus/ui": "^0.2.45",
+ "@wrnexus/validation": "^0.2.45",
+ "@wrnexus/i18n": "^0.2.45",
+ "@wrnexus/db": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json
index cd7cfc57..ceb4a6dc 100644
--- a/.publish/compiler/package.json
+++ b/.publish/compiler/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/compiler — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/core/package.json b/.publish/core/package.json
index 07068c9c..da8cb0dc 100644
--- a/.publish/core/package.json
+++ b/.publish/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/core — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/csr/package.json b/.publish/csr/package.json
index afd3da02..a425af47 100644
--- a/.publish/csr/package.json
+++ b/.publish/csr/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/csr — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/db/package.json b/.publish/db/package.json
index 7890767a..c5b2be21 100644
--- a/.publish/db/package.json
+++ b/.publish/db/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/db — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/dev-server/package.json b/.publish/dev-server/package.json
index 39094898..88ab3dca 100644
--- a/.publish/dev-server/package.json
+++ b/.publish/dev-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
"license": "MIT",
@@ -25,18 +25,18 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44",
- "@wrnexus/router": "^0.2.44",
- "@wrnexus/ssr": "^0.2.44",
- "@wrnexus/csr": "^0.2.44",
- "@wrnexus/compiler": "^0.2.44",
- "@wrnexus/styles": "^0.2.44",
- "@wrnexus/ui": "^0.2.44",
- "@wrnexus/validation": "^0.2.44",
- "@wrnexus/i18n": "^0.2.44",
- "@wrnexus/db": "^0.2.44",
- "@wrnexus/pubsub": "^0.2.44",
- "@wrnexus/uploader": "^0.2.44"
+ "@wrnexus/core": "^0.2.45",
+ "@wrnexus/router": "^0.2.45",
+ "@wrnexus/ssr": "^0.2.45",
+ "@wrnexus/csr": "^0.2.45",
+ "@wrnexus/compiler": "^0.2.45",
+ "@wrnexus/styles": "^0.2.45",
+ "@wrnexus/ui": "^0.2.45",
+ "@wrnexus/validation": "^0.2.45",
+ "@wrnexus/i18n": "^0.2.45",
+ "@wrnexus/db": "^0.2.45",
+ "@wrnexus/pubsub": "^0.2.45",
+ "@wrnexus/uploader": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json
index 8af727a2..da0a8133 100644
--- a/.publish/encryption/package.json
+++ b/.publish/encryption/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/encryption — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/helpers/package.json b/.publish/helpers/package.json
index b3f84f70..da9da07f 100644
--- a/.publish/helpers/package.json
+++ b/.publish/helpers/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json
index 1c20950e..65785b3a 100644
--- a/.publish/i18n/package.json
+++ b/.publish/i18n/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/i18n — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json
index e992a33d..ce9aa3cc 100644
--- a/.publish/jwt/package.json
+++ b/.publish/jwt/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/jwt — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/mobile/package.json b/.publish/mobile/package.json
index 4d5f4036..c12e8ddf 100644
--- a/.publish/mobile/package.json
+++ b/.publish/mobile/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/mobile — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/native": "^0.2.44"
+ "@wrnexus/native": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/native/package.json b/.publish/native/package.json
index cdaca091..896ed00c 100644
--- a/.publish/native/package.json
+++ b/.publish/native/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/native — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/oauth/package.json b/.publish/oauth/package.json
index 9d21778a..c0496ec4 100644
--- a/.publish/oauth/package.json
+++ b/.publish/oauth/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/oauth — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json
index 158ff274..7269163d 100644
--- a/.publish/pubsub/package.json
+++ b/.publish/pubsub/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/queue/package.json b/.publish/queue/package.json
index 02edb973..7cc6bf49 100644
--- a/.publish/queue/package.json
+++ b/.publish/queue/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/queue — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/reactive/package.json b/.publish/reactive/package.json
index 741374c3..2d78c021 100644
--- a/.publish/reactive/package.json
+++ b/.publish/reactive/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/reactive — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/router/package.json b/.publish/router/package.json
index 1fdca5b0..4f52efd0 100644
--- a/.publish/router/package.json
+++ b/.publish/router/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/router — part of the WrNexus framework.",
"license": "MIT",
@@ -21,8 +21,8 @@
}
},
"dependencies": {
- "@wrnexus/compiler": "^0.2.44",
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/compiler": "^0.2.45",
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json
index 3f1f33c8..2b5d23d4 100644
--- a/.publish/ssr/package.json
+++ b/.publish/ssr/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/ssr — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/styles/package.json b/.publish/styles/package.json
index 8835aec5..272b3498 100644
--- a/.publish/styles/package.json
+++ b/.publish/styles/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/styles — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/uploader": "^0.2.44"
+ "@wrnexus/uploader": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/test/package.json b/.publish/test/package.json
index c917f03e..7858e971 100644
--- a/.publish/test/package.json
+++ b/.publish/test/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/test — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/tracking/package.json b/.publish/tracking/package.json
index b3bbcba3..b4d472e7 100644
--- a/.publish/tracking/package.json
+++ b/.publish/tracking/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/tracking — part of the WrNexus framework.",
"license": "MIT",
diff --git a/.publish/ui/package.json b/.publish/ui/package.json
index a90b761f..5d36a943 100644
--- a/.publish/ui/package.json
+++ b/.publish/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/ui — part of the WrNexus framework.",
"license": "MIT",
@@ -22,7 +22,7 @@
"./ui.css": "./ui.css"
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist",
diff --git a/.publish/uploader/package.json b/.publish/uploader/package.json
index cdf37689..adeba5c4 100644
--- a/.publish/uploader/package.json
+++ b/.publish/uploader/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/uploader",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/uploader — part of the WrNexus framework.",
"license": "MIT",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@wrnexus/core": "^0.2.44"
+ "@wrnexus/core": "^0.2.45"
},
"files": [
"dist"
diff --git a/.publish/validation/package.json b/.publish/validation/package.json
index ceda1201..ca2918a2 100644
--- a/.publish/validation/package.json
+++ b/.publish/validation/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/validation",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"description": "@wrnexus/validation — part of the WrNexus framework.",
"license": "MIT",
diff --git a/packages/ai/package.json b/packages/ai/package.json
index a16c2019..c4905d3c 100644
--- a/packages/ai/package.json
+++ b/packages/ai/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
diff --git a/packages/authz/package.json b/packages/authz/package.json
index 16f1b330..7e0920cc 100644
--- a/packages/authz/package.json
+++ b/packages/authz/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 4551fea1..3ca6f19f 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts
index 73fa01a4..ce15cbf1 100644
--- a/packages/cli/src/update.ts
+++ b/packages/cli/src/update.ts
@@ -474,6 +474,15 @@ const MIGRATIONS: Migration[] = [
// No generated application files require automatic migration.
},
},
+ {
+ version: "0.2.45",
+ id: "conditions-bug",
+ description:
+ "Adds server-rendered component each/if blocks and fixes dynamic component rendering.",
+ apply() {
+ // No generated application files require automatic migration.
+ },
+ },
];
/** Release tooling uses this to require an explicit migration entry per version. */
diff --git a/packages/compiler/package.json b/packages/compiler/package.json
index f0f88cb0..52ba1e9c 100644
--- a/packages/compiler/package.json
+++ b/packages/compiler/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/compiler/src/codegen.ts b/packages/compiler/src/codegen.ts
index f29e7725..e78119e3 100644
--- a/packages/compiler/src/codegen.ts
+++ b/packages/compiler/src/codegen.ts
@@ -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}>`;
}
diff --git a/packages/compiler/test/compiler.test.ts b/packages/compiler/test/compiler.test.ts
index 8e459e9c..1d3b7f4e 100644
--- a/packages/compiler/test/compiler.test.ts
+++ b/packages/compiler/test/compiler.test.ts
@@ -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 {
+
+ {item.title}
+
+ }
+}
+`),
+ );
+
+ expect(output).toContain('data-for="item, index in items"');
+
+ expect(output).toContain("data-wrn-class-0");
+
+ expect(output).not.toContain('${(isOpen(index)) ? " open" : ""}');
+});
diff --git a/packages/core/package.json b/packages/core/package.json
index 86828062..2f86541d 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/csr/package.json b/packages/csr/package.json
index e56626bb..dc6ad0c5 100644
--- a/packages/csr/package.json
+++ b/packages/csr/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/csr/src/reactive-runtime.ts b/packages/csr/src/reactive-runtime.ts
index 17dc2733..eb8f22b7 100644
--- a/packages/csr/src/reactive-runtime.ts
+++ b/packages/csr/src/reactive-runtime.ts
@@ -335,6 +335,59 @@ export const REACTIVE_RUNTIME = String.raw`
return run;
}
function renderAll() { renderers.forEach(function (f) { f(); }); }
+ function decodeLoopLocals(node) {
+ var owner =
+ node &&
+ node.closest &&
+ node.closest(
+ "[data-wrn-loop-locals]",
+ );
+
+ if (!owner) {
+ return {};
+ }
+
+ var encoded =
+ owner.getAttribute(
+ "data-wrn-loop-locals",
+ );
+
+ if (!encoded) {
+ return {};
+ }
+
+ try {
+ var binary =
+ window.atob(encoded);
+
+ var bytes =
+ new Uint8Array(
+ binary.length,
+ );
+
+ for (
+ var index = 0;
+ index < binary.length;
+ index++
+ ) {
+ bytes[index] =
+ binary.charCodeAt(index);
+ }
+
+ return JSON.parse(
+ new TextDecoder(
+ "utf-8",
+ ).decode(bytes),
+ );
+ } catch (error) {
+ console.error(
+ "[wrnexus] failed to decode loop locals",
+ error,
+ );
+
+ return {};
+ }
+ }
var behaviorFunctions = {};
var stateWatchers = {};
@@ -537,44 +590,338 @@ export const REACTIVE_RUNTIME = String.raw`
try { return String(itemEval(e)); } catch (err) { return ""; }
});
}
- function hydrateItem(root, locals) {
+ function hydrateItem(
+ root,
+ locals,
+ ) {
function localRead(name) {
- return Object.prototype.hasOwnProperty.call(locals, name) ? locals[name] : peekScope(name);
- }
- function itemEval(expr) { return evaluateExpression(expr, localRead); }
+ if (
+ Object.prototype.hasOwnProperty.call(
+ locals,
+ name,
+ )
+ ) {
+ return locals[name];
+ }
- var els = [root];
- if (root.querySelectorAll) Array.prototype.push.apply(els, root.querySelectorAll("*"));
- els.forEach(function (n) {
- if (n.nodeType !== 1) return;
- Array.prototype.slice.call(n.attributes).forEach(function (attr) {
- if (attr.name === "data-text") {
- try { n.textContent = String(itemEval(attr.value)); } catch (e) { /* ignore */ }
- } else if (attr.name.indexOf("data-on-") === 0) {
- var evt = attr.name.slice("data-on-".length);
- var stmt = attr.value;
- n.addEventListener(evt, function () {
+ return readScope(name);
+ }
+
+ function itemEval(expression) {
+ return evaluateExpression(
+ expression,
+ localRead,
+ );
+ }
+
+ function applyItemTemplate(
+ template,
+ ) {
+ return String(template).replace(
+ /\{\{\s*([^}]+?)\s*\}\}|\{([^{}]+)\}/g,
+ function (
+ _,
+ doubleExpression,
+ singleExpression,
+ ) {
+ var expression = (
+ doubleExpression ||
+ singleExpression
+ ).trim();
+
+ try {
+ var value =
+ itemEval(expression);
+
+ return value == null
+ ? ""
+ : String(value);
+ } catch (_) {
+ return "";
+ }
+ },
+ );
+ }
+
+ var elements = [root];
+
+ if (root.querySelectorAll) {
+ Array.prototype.push.apply(
+ elements,
+ root.querySelectorAll("*"),
+ );
+ }
+
+ elements.forEach(function (node) {
+ if (
+ !node ||
+ node.nodeType !== 1
+ ) {
+ return;
+ }
+
+ var attributes =
+ Array.prototype.slice.call(
+ node.attributes,
+ );
+
+ attributes.forEach(function (
+ attribute,
+ ) {
+ /*
+ * data-text must be handled using itemEval(),
+ * because item/index only exist in loop locals.
+ */
+ if (
+ attribute.name === "data-text"
+ ) {
+ try {
+ var textValue =
+ itemEval(attribute.value);
+
+ node.textContent =
+ textValue == null
+ ? ""
+ : String(textValue);
+ } catch (error) {
+ console.error(
+ "[wrnexus] data-for text binding failed for '" +
+ attribute.value +
+ "'",
+ error,
+ );
+
+ node.textContent = "";
+ }
+
+ return;
+ }
+
+ /*
+ * Conditional classes:
+ * data-wrn-class-*='["class","expression"]'
+ */
+ if (
+ attribute.name.indexOf(
+ "data-wrn-class-",
+ ) === 0
+ ) {
+ var classBinding;
+
+ try {
+ classBinding = JSON.parse(
+ attribute.value,
+ );
+ } catch (_) {
+ return;
+ }
+
+ if (
+ !classBinding ||
+ classBinding.length !== 2
+ ) {
+ return;
+ }
+
+ var className =
+ classBinding[0];
+
+ var classExpression =
+ classBinding[1];
+
+ var classEnabled = false;
+
+ try {
+ classEnabled = !!itemEval(
+ classExpression,
+ );
+ } catch (_) {
+ classEnabled = false;
+ }
+
+ node.classList.toggle(
+ className,
+ classEnabled,
+ );
+
+ return;
+ }
+
+ /*
+ * Reactive ordinary attributes:
+ * data-wrn-bind-*='["aria-expanded","{isOpen(index)}"]'
+ */
+ if (
+ attribute.name.indexOf(
+ "data-wrn-bind-",
+ ) === 0
+ ) {
+ var binding;
+
+ try {
+ binding = JSON.parse(
+ attribute.value,
+ );
+ } catch (_) {
+ return;
+ }
+
+ if (
+ !binding ||
+ binding.length !== 2
+ ) {
+ return;
+ }
+
+ var attributeName =
+ binding[0];
+
+ var attributeTemplate =
+ binding[1];
+
+ var exact =
+ /^\{([^{}]+)\}$/.exec(
+ attributeTemplate,
+ );
+
+ var rawValue;
+
+ if (exact) {
try {
- splitTopLevel(stmt, ";").forEach(function (part) {
- runStatement(part, itemEval, localRead, function (name, value) {
- if (Object.prototype.hasOwnProperty.call(locals, name)) locals[name] = value;
- else if (!signals[name]) { signals[name] = signal(value); renderAll(); }
- else signals[name].set(value);
- });
+ rawValue = itemEval(
+ exact[1].trim(),
+ );
+ } catch (_) {
+ return;
+ }
+ } else {
+ rawValue =
+ attributeTemplate.replace(
+ /\{([^{}]+)\}/g,
+ function (
+ _,
+ expression,
+ ) {
+ try {
+ var value =
+ itemEval(
+ expression.trim(),
+ );
+
+ return value == null
+ ? ""
+ : String(value);
+ } catch (_) {
+ return "";
+ }
+ },
+ );
+ }
+
+ applyReactiveAttribute(
+ node,
+ attributeName,
+ rawValue,
+ );
+
+ return;
+ }
+
+ /*
+ * Event handlers inside data-for.
+ */
+ if (
+ attribute.name.indexOf(
+ "data-on-",
+ ) === 0
+ ) {
+ var eventName =
+ attribute.name.slice(
+ "data-on-".length,
+ );
+
+ var statement =
+ attribute.value;
+
+ node.addEventListener(
+ eventName,
+ function (event) {
+ var eventLocals = {};
+
+ Object.keys(
+ locals,
+ ).forEach(function (key) {
+ eventLocals[key] =
+ locals[key];
});
- } catch (e) { console.error("[wrnexus] data-for handler error", e); }
- });
- } else if (attr.value.indexOf("{") !== -1) {
- attr.value = fillMustache(attr.value, itemEval);
+
+ eventLocals.event = event;
+ eventLocals.$event = event;
+
+ try {
+ runStmt(
+ statement,
+ eventLocals,
+ );
+ } catch (error) {
+ console.error(
+ "[wrnexus] data-for handler error in '" +
+ statement +
+ "'",
+ error,
+ );
+ }
+ },
+ );
+
+ return;
+ }
+
+ /*
+ * Static attributes containing interpolation.
+ */
+ if (
+ attribute.value &&
+ attribute.value.indexOf("{") !== -1
+ ) {
+ node.setAttribute(
+ attribute.name,
+ applyItemTemplate(
+ attribute.value,
+ ),
+ );
}
});
});
- var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null);
- var tn;
- while ((tn = walker.nextNode())) {
- if (tn.nodeValue.indexOf("{") === -1) continue;
- tn.nodeValue = fillMustache(tn.nodeValue, itemEval);
+ /*
+ * Text interpolation inside loop content:
+ *
+ * {item.title}
+ */
+ var walker =
+ document.createTreeWalker(
+ root,
+ NodeFilter.SHOW_TEXT,
+ null,
+ );
+
+ var textNode;
+
+ while (
+ (textNode = walker.nextNode())
+ ) {
+ var template =
+ textNode.nodeValue || "";
+
+ if (
+ template.indexOf("{") === -1
+ ) {
+ continue;
+ }
+
+ textNode.nodeValue =
+ applyItemTemplate(template);
}
}
@@ -658,7 +1005,10 @@ export const REACTIVE_RUNTIME = String.raw`
var enabled = false;
try {
- enabled = !!evalExpr(expression);
+ enabled = !!evalExpr(
+ expression,
+ decodeLoopLocals(node),
+ );
} catch (e) {
enabled = false;
}
@@ -671,32 +1021,97 @@ export const REACTIVE_RUNTIME = String.raw`
// Reactive ordinary attributes emitted by the compiler. Each marker stores
// [attributeName, originalTemplate], preserving an SSR value while allowing
// state changes to update type, aria-*, class, href, and other attributes.
- var bindNodes = [el].concat(Array.prototype.slice.call(el.querySelectorAll("*")));
+ var bindNodes = [el].concat(
+ Array.prototype.slice.call(
+ el.querySelectorAll("*"),
+ ),
+ );
+
bindNodes.forEach(function (node) {
if (!owns(node)) return;
- Array.prototype.slice.call(node.attributes).forEach(function (marker) {
- if (marker.name.indexOf("data-wrn-bind-") !== 0) return;
- var binding;
- try { binding = JSON.parse(marker.value); } catch (e) { return; }
- if (!binding || binding.length !== 2) return;
- var name = binding[0];
- var template = binding[1];
- reactive(function () {
- var exact = /^\{([^{}]+)\}$/.exec(template);
- var raw;
- if (exact) {
- try { raw = evalExpr(exact[1].trim()); } catch (e) { return; }
- } else {
- raw = template.replace(/\{([^{}]+)\}/g, function (_, expr) {
- try {
- var value = evalExpr(expr.trim());
- return value == null ? "" : String(value);
- } catch (e) { return ""; }
- });
+
+ Array.prototype.slice
+ .call(node.attributes)
+ .forEach(function (marker) {
+ if (
+ marker.name.indexOf(
+ "data-wrn-bind-",
+ ) !== 0
+ ) {
+ return;
}
- applyReactiveAttribute(node, name, raw);
+
+ var binding;
+
+ try {
+ binding = JSON.parse(
+ marker.value,
+ );
+ } catch (error) {
+ return;
+ }
+
+ if (
+ !binding ||
+ binding.length !== 2
+ ) {
+ return;
+ }
+
+ var name = binding[0];
+ var template = binding[1];
+
+ reactive(function () {
+ var locals =
+ decodeLoopLocals(node);
+
+ var exact =
+ /^\{([^{}]+)\}$/.exec(
+ template,
+ );
+
+ var raw;
+
+ if (exact) {
+ try {
+ raw = evalExpr(
+ exact[1].trim(),
+ locals,
+ );
+ } catch (error) {
+ return;
+ }
+ } else {
+ raw = template.replace(
+ /\{([^{}]+)\}/g,
+ function (
+ _,
+ expression,
+ ) {
+ try {
+ var value =
+ evalExpr(
+ expression.trim(),
+ locals,
+ );
+
+ return value == null
+ ? ""
+ : String(value);
+ } catch (error) {
+ return "";
+ }
+ },
+ );
+ }
+
+ applyReactiveAttribute(
+ node,
+ name,
+ raw,
+ );
+ });
});
- });
});
// {{expr}} / {expr} interpolation in text nodes
@@ -737,7 +1152,15 @@ export const REACTIVE_RUNTIME = String.raw`
var stmt = attr.value;
var listener = function (event) {
- try { runStmt(stmt, { event: event, $event: event }); } catch (error) {
+ try {
+ var locals =
+ decodeLoopLocals(node);
+
+ locals.event = event;
+ locals.$event = event;
+
+ runStmt(stmt, locals);
+ } catch (error) {
console.error("[wrnexus] handler error in '" + stmt + "'", error);
}
};
diff --git a/packages/db/package.json b/packages/db/package.json
index 0d95a900..80fdfb8c 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json
index 7c1c5770..b7d98be4 100644
--- a/packages/dev-server/package.json
+++ b/packages/dev-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/encryption/package.json b/packages/encryption/package.json
index 7ad20d0c..9ccfb3a5 100644
--- a/packages/encryption/package.json
+++ b/packages/encryption/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/helpers/package.json b/packages/helpers/package.json
index 4aad4c0d..5e1d7407 100644
--- a/packages/helpers/package.json
+++ b/packages/helpers/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index 72cc214c..40c93bc8 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/jwt/package.json b/packages/jwt/package.json
index e277c15e..1ef2e274 100644
--- a/packages/jwt/package.json
+++ b/packages/jwt/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/mobile/package.json b/packages/mobile/package.json
index 844a312a..5f0a36ba 100644
--- a/packages/mobile/package.json
+++ b/packages/mobile/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/native/package.json b/packages/native/package.json
index e95bad55..b366b8d7 100644
--- a/packages/native/package.json
+++ b/packages/native/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/oauth/package.json b/packages/oauth/package.json
index 7ec38f05..49e5f0b1 100644
--- a/packages/oauth/package.json
+++ b/packages/oauth/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json
index 4e6197a1..0fc033ee 100644
--- a/packages/pubsub/package.json
+++ b/packages/pubsub/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/queue/package.json b/packages/queue/package.json
index 900849ea..f90066c1 100644
--- a/packages/queue/package.json
+++ b/packages/queue/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/reactive/package.json b/packages/reactive/package.json
index 18b20292..0e004123 100644
--- a/packages/reactive/package.json
+++ b/packages/reactive/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/router/package.json b/packages/router/package.json
index 14656bc4..e196a498 100644
--- a/packages/router/package.json
+++ b/packages/router/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/ssr/package.json b/packages/ssr/package.json
index c88ca85b..19ce1104 100644
--- a/packages/ssr/package.json
+++ b/packages/ssr/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/styles/package.json b/packages/styles/package.json
index 289c147e..b7f532ff 100644
--- a/packages/styles/package.json
+++ b/packages/styles/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
- "version": "0.2.44",
+ "version": "0.2.45",
"type": "module",
"main": "src/index.ts",
"exports": {
diff --git a/packages/test/package.json b/packages/test/package.json
index 8b9f7b3c..8be81288 100644
--- a/packages/test/package.json
+++ b/packages/test/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/tracking/package.json b/packages/tracking/package.json
index 735cbef4..720f4fe1 100644
--- a/packages/tracking/package.json
+++ b/packages/tracking/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 58a48e6a..ab220cab 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ui",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/uploader/package.json b/packages/uploader/package.json
index 871a1de7..3c277faa 100644
--- a/packages/uploader/package.json
+++ b/packages/uploader/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/uploader",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/packages/validation/package.json b/packages/validation/package.json
index 0b53c28f..e1c75aaa 100644
--- a/packages/validation/package.json
+++ b/packages/validation/package.json
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/validation",
- "version": "0.2.44",
+ "version": "0.2.45",
"private": true,
"type": "module",
"main": "src/index.ts",
diff --git a/update-package-versions.mjs b/update-package-versions.mjs
index 16463980..315a7290 100644
--- a/update-package-versions.mjs
+++ b/update-package-versions.mjs
@@ -1,7 +1,7 @@
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
-const version = "0.2.44";
+const version = "0.2.45";
const dependencyGroups = [
"dependencies",