diff --git a/.publish/ai/package.json b/.publish/ai/package.json index 68b7b4f3..0e740363 100644 --- a/.publish/ai/package.json +++ b/.publish/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.3.1", + "version": "0.3.2", "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 c7600ef3..c9cee6f5 100644 --- a/.publish/authz/package.json +++ b/.publish/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.3.1", + "version": "0.3.2", "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 f23758ca..5b78e238 100644 --- a/.publish/cli/package.json +++ b/.publish/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/cli — part of the WrNexus framework.", "license": "MIT", @@ -28,18 +28,18 @@ "wrnexus": "./dist/index.js" }, "dependencies": { - "@wrnexus/core": "^0.3.1", - "@wrnexus/router": "^0.3.1", - "@wrnexus/csr": "^0.3.1", - "@wrnexus/compiler": "^0.3.1", - "@wrnexus/styles": "^0.3.1", - "@wrnexus/dev-server": "^0.3.1", - "@wrnexus/ui": "^0.3.1", - "@wrnexus/validation": "^0.3.1", - "@wrnexus/i18n": "^0.3.1", - "@wrnexus/db": "^0.3.1", - "@wrnexus/plugin": "^0.3.1", - "@wrnexus/syntax": "^0.3.1" + "@wrnexus/core": "^0.3.2", + "@wrnexus/router": "^0.3.2", + "@wrnexus/csr": "^0.3.2", + "@wrnexus/compiler": "^0.3.2", + "@wrnexus/styles": "^0.3.2", + "@wrnexus/dev-server": "^0.3.2", + "@wrnexus/ui": "^0.3.2", + "@wrnexus/validation": "^0.3.2", + "@wrnexus/i18n": "^0.3.2", + "@wrnexus/db": "^0.3.2", + "@wrnexus/plugin": "^0.3.2", + "@wrnexus/syntax": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json index 986ed4dc..7e399b9b 100644 --- a/.publish/compiler/package.json +++ b/.publish/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/compiler — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/syntax": "^0.3.1" + "@wrnexus/syntax": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/core/package.json b/.publish/core/package.json index 7d451c88..14ceec83 100644 --- a/.publish/core/package.json +++ b/.publish/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.3.1", + "version": "0.3.2", "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 a0807e04..be1b3f62 100644 --- a/.publish/csr/package.json +++ b/.publish/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/csr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/db/package.json b/.publish/db/package.json index c50af377..e923e530 100644 --- a/.publish/db/package.json +++ b/.publish/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.3.1", + "version": "0.3.2", "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 ef3dcb0d..62ea29ee 100644 --- a/.publish/dev-server/package.json +++ b/.publish/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/dev-server — part of the WrNexus framework.", "license": "MIT", @@ -25,20 +25,20 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1", - "@wrnexus/dev-toolbar": "^0.3.1", - "@wrnexus/router": "^0.3.1", - "@wrnexus/ssr": "^0.3.1", - "@wrnexus/csr": "^0.3.1", - "@wrnexus/compiler": "^0.3.1", - "@wrnexus/styles": "^0.3.1", - "@wrnexus/ui": "^0.3.1", - "@wrnexus/validation": "^0.3.1", - "@wrnexus/i18n": "^0.3.1", - "@wrnexus/db": "^0.3.1", - "@wrnexus/pubsub": "^0.3.1", - "@wrnexus/uploader": "^0.3.1", - "@wrnexus/plugin": "^0.3.1" + "@wrnexus/core": "^0.3.2", + "@wrnexus/dev-toolbar": "^0.3.2", + "@wrnexus/router": "^0.3.2", + "@wrnexus/ssr": "^0.3.2", + "@wrnexus/csr": "^0.3.2", + "@wrnexus/compiler": "^0.3.2", + "@wrnexus/styles": "^0.3.2", + "@wrnexus/ui": "^0.3.2", + "@wrnexus/validation": "^0.3.2", + "@wrnexus/i18n": "^0.3.2", + "@wrnexus/db": "^0.3.2", + "@wrnexus/pubsub": "^0.3.2", + "@wrnexus/uploader": "^0.3.2", + "@wrnexus/plugin": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/dev-toolbar/package.json b/.publish/dev-toolbar/package.json index 43a4c6e8..47417b07 100644 --- a/.publish/dev-toolbar/package.json +++ b/.publish/dev-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-toolbar", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/dev-toolbar — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json index 05246b51..0ded4bd6 100644 --- a/.publish/encryption/package.json +++ b/.publish/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.3.1", + "version": "0.3.2", "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 7168e223..29668556 100644 --- a/.publish/helpers/package.json +++ b/.publish/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json index 563b407c..6578ee00 100644 --- a/.publish/i18n/package.json +++ b/.publish/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/i18n — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json index 5c6fcd46..9a43e657 100644 --- a/.publish/jwt/package.json +++ b/.publish/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.3.1", + "version": "0.3.2", "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 e54060fb..6563b82e 100644 --- a/.publish/mobile/package.json +++ b/.publish/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/mobile — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/native": "^0.3.1" + "@wrnexus/native": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/native/package.json b/.publish/native/package.json index 91bbe052..33fb2199 100644 --- a/.publish/native/package.json +++ b/.publish/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.3.1", + "version": "0.3.2", "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 51ce2a1f..65078a05 100644 --- a/.publish/oauth/package.json +++ b/.publish/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/oauth — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/plugin/package.json b/.publish/plugin/package.json index cdb9d4c2..962679ba 100644 --- a/.publish/plugin/package.json +++ b/.publish/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/plugin", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/plugin — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/syntax": "^0.3.1" + "@wrnexus/syntax": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json index 25da172d..a3ddba3e 100644 --- a/.publish/pubsub/package.json +++ b/.publish/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.3.1", + "version": "0.3.2", "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 953843bd..aa28e9eb 100644 --- a/.publish/queue/package.json +++ b/.publish/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.3.1", + "version": "0.3.2", "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 c56539b9..a60bf804 100644 --- a/.publish/reactive/package.json +++ b/.publish/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.3.1", + "version": "0.3.2", "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 cd3a9831..0971030c 100644 --- a/.publish/router/package.json +++ b/.publish/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/router — part of the WrNexus framework.", "license": "MIT", @@ -21,8 +21,8 @@ } }, "dependencies": { - "@wrnexus/compiler": "^0.3.1", - "@wrnexus/core": "^0.3.1" + "@wrnexus/compiler": "^0.3.2", + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json index 1a6c1c53..77f280bb 100644 --- a/.publish/ssr/package.json +++ b/.publish/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/ssr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/styles/package.json b/.publish/styles/package.json index 493e4683..69499203 100644 --- a/.publish/styles/package.json +++ b/.publish/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/styles — part of the WrNexus framework.", "license": "MIT", @@ -21,9 +21,9 @@ } }, "dependencies": { - "@wrnexus/uploader": "^0.3.1", - "@wrnexus/core": "^0.3.1", - "@wrnexus/plugin": "^0.3.1" + "@wrnexus/uploader": "^0.3.2", + "@wrnexus/core": "^0.3.2", + "@wrnexus/plugin": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/syntax/package.json b/.publish/syntax/package.json index 0933c2c9..7fdeecd4 100644 --- a/.publish/syntax/package.json +++ b/.publish/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/syntax", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/syntax — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/test/package.json b/.publish/test/package.json index 1985493b..0d196dfc 100644 --- a/.publish/test/package.json +++ b/.publish/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.3.1", + "version": "0.3.2", "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 d2cefa12..960ef664 100644 --- a/.publish/tracking/package.json +++ b/.publish/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.3.1", + "version": "0.3.2", "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 9f72c245..60af84b9 100644 --- a/.publish/ui/package.json +++ b/.publish/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/ui — part of the WrNexus framework.", "license": "MIT", @@ -25,7 +25,7 @@ "./ui.css": "./ui.css" }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist", diff --git a/.publish/uploader/package.json b/.publish/uploader/package.json index 88587df6..9877b64f 100644 --- a/.publish/uploader/package.json +++ b/.publish/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/uploader — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.3.1" + "@wrnexus/core": "^0.3.2" }, "files": [ "dist" diff --git a/.publish/validation/package.json b/.publish/validation/package.json index 13ab440d..74c3159c 100644 --- a/.publish/validation/package.json +++ b/.publish/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "description": "@wrnexus/validation — part of the WrNexus framework.", "license": "MIT", diff --git a/editors/vscode/src/compiler.cjs b/editors/vscode/src/compiler.cjs index bbb04e3d..bf678afe 100644 --- a/editors/vscode/src/compiler.cjs +++ b/editors/vscode/src/compiler.cjs @@ -602,10 +602,11 @@ function generate(ast) { seedScope[entry.name] = undefined; } } - const reactiveNames = [...ast.states.map((entry) => entry.name), ...ast.computed.map((entry) => entry.name)]; - const reactive = reactiveNames.length > 0 - ? { stateNames: new Set(reactiveNames), scope: seedScope } - : null; + const reactiveNames = [ + ...ast.states.map((entry) => entry.name), + ...ast.computed.map((entry) => entry.name), + ]; + const reactive = reactiveNames.length > 0 ? { stateNames: new Set(reactiveNames), scope: seedScope } : null; const loops = []; let html = ast.view .map((node) => renderNode(node, ssrBindings, csrBindings, apiBindings, loops, reactive)) @@ -613,7 +614,10 @@ function generate(ast) { const styles = ast.styles.map((body) => body.trim()).filter(Boolean); const pageBehavior = ast.runtime === "server" ? null : componentBehavior(ast); const needsClientRuntime = ast.runtime !== "server" && - (ast.states.length > 0 || ast.computed.length > 0 || hasClientBehavior(ast.view) || pageBehavior !== null); + (ast.states.length > 0 || + ast.computed.length > 0 || + hasClientBehavior(ast.view) || + pageBehavior !== null); if (needsClientRuntime) { const scopePlaceholder = "__WRNEXUS_DYNAMIC_SCOPE__"; html = `
${html}
`; @@ -721,7 +725,9 @@ function generate(ast) { out.push(`export async function __wrnexusLoad(ctx: any) {\n${serverLoads.map((entry) => entry.body).join("\n")}\n}`); } if (clientLoads.length > 0) { - out.push(`export const __wrnexusClientLoad = ${JSON.stringify(clientLoads.map((entry) => entry.body))};`); + out.push(`export async function __wrnexusClientLoad(ctx: any) { +${clientLoads.map((entry) => entry.body).join("\n")} +}`); } } if (ast.actions.length > 0) { @@ -1200,7 +1206,10 @@ function generateComponent(ast) { // no JavaScript at all. const behavior = componentBehavior(ast); const needsScope = ast.runtime !== "server" && - (ast.states.length > 0 || ast.computed.length > 0 || viewHasEvents(ast.view) || behavior !== null); + (ast.states.length > 0 || + ast.computed.length > 0 || + viewHasEvents(ast.view) || + behavior !== null); const scopeKeys = [ ...effectiveProps.map((prop) => prop.name), ...ast.states.map((state) => state.name), @@ -1220,7 +1229,11 @@ function generateComponent(ast) { decls.push(` const ${nameRefs.get(entry.name)} = (${resolveExpr(entry.expr)});`); } const returnExpr = needsScope - ? "`" + styleTag + `
` + viewCode + "
`" + ? "`" + + styleTag + + `
` + + viewCode + + "
`" : "`" + styleTag + viewCode + "`"; const scopeLine = needsScope && scopeKeys.length > 0 ? ` const __scope = __wrnexusScopeDecl({ ${scopeKeys @@ -1948,7 +1961,7 @@ function astDiagnostics(ast, options) { code: spec_ts_1.WRN_DIAGNOSTIC_CODES.accessibility, severity: "warning", message: "Image is missing an alt attribute.", - hint: "Add alt text, or alt=\"\" for a decorative image.", + hint: 'Add alt text, or alt="" for a decorative image.', file: options.file, }); } @@ -1958,7 +1971,7 @@ function astDiagnostics(ast, options) { code: spec_ts_1.WRN_DIAGNOSTIC_CODES.serverInteractive, severity: "error", message: "A server-only WRN root cannot contain client state, effects, watches, or event handlers.", - hint: "Use runtime = \"universal\" or remove interactive behavior.", + hint: 'Use runtime = "universal" or remove interactive behavior.', file: options.file, }); } @@ -1989,8 +2002,14 @@ function isRuntimeTarget(value) { function formatDiagnostic(source, diagnostic) { const location = diagnostic.position ? `${diagnostic.file ?? ""}:${diagnostic.position.line}:${diagnostic.position.column}` - : diagnostic.file ?? ""; - const lines = [`${diagnostic.code} ${diagnostic.severity.toUpperCase()}`, "", diagnostic.message, "", location]; + : (diagnostic.file ?? ""); + const lines = [ + `${diagnostic.code} ${diagnostic.severity.toUpperCase()}`, + "", + diagnostic.message, + "", + location, + ]; if (diagnostic.position) { const sourceLine = source.split(/\r?\n/)[diagnostic.position.line - 1] ?? ""; lines.push("", sourceLine, `${" ".repeat(Math.max(0, diagnostic.position.column - 1))}^`); @@ -2829,13 +2848,7 @@ exports.WRN_ROOT_MEMBERS = [ "style", "functions", ]; -exports.WRN_HYDRATION_STRATEGIES = [ - "load", - "idle", - "visible", - "interaction", - "none", -]; +exports.WRN_HYDRATION_STRATEGIES = ["load", "idle", "visible", "interaction", "none"]; exports.WRN_RUNTIME_TARGETS = ["server", "client", "universal"]; exports.WRN_DIAGNOSTIC_CODES = { parse: "WRN-PARSE-001", diff --git a/packages/ai/package.json b/packages/ai/package.json index 616037b3..73fdab0a 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.3.1", + "version": "0.3.2", "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 1691b570..b3f23f1a 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index 55344190..d70d9a88 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts index f23082a1..9ea465b9 100644 --- a/packages/cli/src/update.ts +++ b/packages/cli/src/update.ts @@ -961,6 +961,14 @@ const MIGRATIONS: Migration[] = [ // CLI-only transaction fix. No generated application files require migration. }, }, + { + version: "0.3.2", + id: "ssr-page-state-control-scope", + description: "Makes page state variables available to server-rendered if and each expressions.", + apply() { + // Compiler-only fix. Existing WRN source files require no 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 004568d8..2756cb03 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/compiler/src/codegen.ts b/packages/compiler/src/codegen.ts index 0fb5d366..0277471c 100644 --- a/packages/compiler/src/codegen.ts +++ b/packages/compiler/src/codegen.ts @@ -675,6 +675,20 @@ function hydrationId(ast: PageAst): string { return `${ast.name}:${stableHash(shape)}`; } +function isSafeGeneratedIdentifier(name: string): boolean { + return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name); +} + +function generateSsrStateAliases(stateNames: string[]): string { + const names = [...new Set(stateNames)].filter(isSafeGeneratedIdentifier); + + if (!names.length) { + return ""; + } + + return `const { ${names.join(", ")} } = __state;\n`; +} + function hydrationAttribute(ast: PageAst): string { const strategy = ast.hydrate ?? "load"; return ` data-wrn-hydration="${attrEscape(hydrationId(ast))}" data-wrn-hydrate="${attrEscape(strategy)}" data-wrn-runtime="${attrEscape(ast.runtime ?? "universal")}"`; @@ -776,6 +790,9 @@ export function generate(ast: PageAst): string { .map((state) => `${JSON.stringify(state.name)}: ${state.valueType ?? "unknown"}`) .join("; ")} }` : "Record"; + + const ssrStateAliases = generateSsrStateAliases(ast.states.map((state) => state.name)); + loops.forEach((code, idx) => { body = body.replace(`\x00WRNEACH${idx}\x00`, () => code); }); @@ -806,7 +823,7 @@ export function generate(ast: PageAst): string { `export default async function ${ast.name}(ctx: any) { ${decls} const __state: ${stateType} = { ${dynamicStateScope} }; - + ${ssrStateAliases} const __scopeValue = Object.entries(__state) .map(([key, value]) => { const encoded = @@ -834,7 +851,7 @@ export function generate(ast: PageAst): string { out.push( `export default function ${ast.name}(ctx: any) { const __state: ${stateType} = { ${dynamicStateScope} }; - + ${ssrStateAliases} const __scopeValue = Object.entries(__state) .map(([key, value]) => { const encoded = diff --git a/packages/compiler/test/compiler.test.ts b/packages/compiler/test/compiler.test.ts index 9d44d6a6..7ee0ec2f 100644 --- a/packages/compiler/test/compiler.test.ts +++ b/packages/compiler/test/compiler.test.ts @@ -1,5 +1,5 @@ import { test, expect } from "bun:test"; -import { writeFileSync, mkdirSync } from "node:fs"; +import { writeFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; @@ -1105,3 +1105,86 @@ test("WRN 0.3 metadata, computed values, loaders, and actions compile additively expect(output).toContain("export const __wrnexusActions"); expect(output).toContain('data-wrn-hydrate="idle"'); }); +test("page state is available inside server-rendered if blocks", () => { + const source = ` +page Pricing { + state billingCycle = "monthly" + + view { + {#if billingCycle === "annual"} +

Annual

+ {:else} +

Monthly

+ {/if} + } +} +`; + + const ast = parse(source); + const code = generate(ast); + + expect(code).toContain("const { billingCycle } = __state;"); + expect(code).toContain('billingCycle === "annual"'); +}); +test("page state SSR condition renders without ReferenceError", async () => { + const root = mkdtempSync(join(tmpdir(), "wrnexus-page-state-")); + const generatedFile = join(root, "pricing.generated.ts"); + + try { + const source = ` +page Pricing { + state billingCycle = "monthly" + + view { + {#if billingCycle === "annual"} +

Annual

+ {:else} +

Monthly

+ {/if} + } +} +`; + + const code = generate(parse(source)); + + expect(code).toContain("const { billingCycle } = __state;"); + expect(code).toContain('billingCycle === "annual"'); + + writeFileSync(generatedFile, code, "utf8"); + + const moduleUrl = pathToFileURL(generatedFile).href + `?test=${Date.now()}-${Math.random()}`; + + const generatedModule = await import(moduleUrl); + + const html = await generatedModule.default({}); + + expect(html).toContain("

Monthly

"); + expect(html).not.toContain("

Annual

"); + } finally { + rmSync(root, { + recursive: true, + force: true, + }); + } +}); +test("page state is declared for server-rendered control blocks", () => { + const source = ` +page Pricing { + state billingCycle = "monthly" + + view { + {#if billingCycle === "annual"} +

Annual

+ {:else} +

Monthly

+ {/if} + } +} +`; + + const code = generate(parse(source)); + + expect(code).toContain("const { billingCycle } = __state;"); + + expect(code).toContain('billingCycle === "annual"'); +}); diff --git a/packages/core/package.json b/packages/core/package.json index 2638d37a..66c618d8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/package.json b/packages/csr/package.json index c62ab515..77848b94 100644 --- a/packages/csr/package.json +++ b/packages/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/db/package.json b/packages/db/package.json index 60da766e..ae36d03b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 76a3b853..59e8c744 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/dev-toolbar/package.json b/packages/dev-toolbar/package.json index eb185f60..61a7201c 100644 --- a/packages/dev-toolbar/package.json +++ b/packages/dev-toolbar/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-toolbar", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "sideEffects": false, diff --git a/packages/encryption/package.json b/packages/encryption/package.json index c50c73d6..20c620ed 100644 --- a/packages/encryption/package.json +++ b/packages/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/helpers/package.json b/packages/helpers/package.json index b049bb82..614f853b 100644 --- a/packages/helpers/package.json +++ b/packages/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.3.1", + "version": "0.3.2", "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 0256066c..66d2db1a 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/jwt/package.json b/packages/jwt/package.json index 90c76d9e..0080be30 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 6de197f4..8d4fd0d0 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/native/package.json b/packages/native/package.json index 3bf2f0e2..f670aa50 100644 --- a/packages/native/package.json +++ b/packages/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/oauth/package.json b/packages/oauth/package.json index 936294fd..bc37eb12 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/plugin/package.json b/packages/plugin/package.json index a5ca6e5f..cf673c19 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/plugin", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index 17fd7f3b..521dfa55 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/queue/package.json b/packages/queue/package.json index d98e9a3f..cb303eeb 100644 --- a/packages/queue/package.json +++ b/packages/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/reactive/package.json b/packages/reactive/package.json index 59fa2b74..a64ddb45 100644 --- a/packages/reactive/package.json +++ b/packages/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/router/package.json b/packages/router/package.json index ec06fc8a..1a1bca6d 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/ssr/package.json b/packages/ssr/package.json index 379de43b..639924e0 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/styles/package.json b/packages/styles/package.json index ed7d6f95..f2c98050 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/syntax/package.json b/packages/syntax/package.json index b95cc54e..25b8c76f 100644 --- a/packages/syntax/package.json +++ b/packages/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/syntax", - "version": "0.3.1", + "version": "0.3.2", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/test/package.json b/packages/test/package.json index d6f9a585..f57dd12a 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/tracking/package.json b/packages/tracking/package.json index 515739ef..cf239367 100644 --- a/packages/tracking/package.json +++ b/packages/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/ui/package.json b/packages/ui/package.json index edfe7bfb..27f98fa4 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/uploader/package.json b/packages/uploader/package.json index 7b9e580d..2947b1b6 100644 --- a/packages/uploader/package.json +++ b/packages/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/validation/package.json b/packages/validation/package.json index cfbc5ac9..3576911b 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.3.1", + "version": "0.3.2", "private": true, "type": "module", "main": "src/index.ts", diff --git a/update-package-versions.mjs b/update-package-versions.mjs index 36f19c1a..d5a17ffd 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.3.1"; +const version = "0.3.2"; const dependencyGroups = [ "dependencies",