fix(compiler): render server loader attributes without client state
This commit is contained in:
1 parent
6acf78a72c
commit
aec1dc6056
4 files changed
+40
-5
No files matched your search
@@ -320,7 +320,7 @@
|
|||||||
},
|
},
|
||||||
"packages/compiler": {
|
"packages/compiler": {
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.8.22",
|
"version": "0.8.23",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/csr": "workspace:*",
|
"@wrnexus/csr": "workspace:*",
|
||||||
"@wrnexus/store": "workspace:*",
|
"@wrnexus/store": "workspace:*",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.8.22",
|
"version": "0.8.23",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -344,10 +344,16 @@ function renderAttrs(
|
|||||||
exprRefsState(expression, reactive.runtimeStateNames) &&
|
exprRefsState(expression, reactive.runtimeStateNames) &&
|
||||||
dynamicExpressions
|
dynamicExpressions
|
||||||
) {
|
) {
|
||||||
|
const marker = exprRefsState(expression, reactive.stateNames)
|
||||||
|
? ` data-wrn-bind-${bindIndex++}="${attrEscape(JSON.stringify([attr.name, attr.value]))}"`
|
||||||
|
: "";
|
||||||
|
if (isHtmlBooleanAttribute(attr.name)) {
|
||||||
|
dynamicExpressions.push(`\${(${expression}) ? ${JSON.stringify(` ${attr.name}`)} : ""}`);
|
||||||
|
return `\x00WRNEACH${dynamicExpressions.length - 1}\x00${marker}`;
|
||||||
|
}
|
||||||
dynamicExpressions.push(`\${__wrnexusPropAttr(${expression})}`);
|
dynamicExpressions.push(`\${__wrnexusPropAttr(${expression})}`);
|
||||||
const sentinel = `\x00WRNEACH${dynamicExpressions.length - 1}\x00`;
|
const sentinel = `\x00WRNEACH${dynamicExpressions.length - 1}\x00`;
|
||||||
const marker = JSON.stringify([attr.name, attr.value]);
|
return ` ${attr.name}="${sentinel}"${marker}`;
|
||||||
return ` ${attr.name}="${sentinel}" data-wrn-bind-${bindIndex++}="${attrEscape(marker)}"`;
|
|
||||||
}
|
}
|
||||||
const initial = reactiveAttrValue(attr.value, reactive);
|
const initial = reactiveAttrValue(attr.value, reactive);
|
||||||
if (initial === null) return base;
|
if (initial === null) return base;
|
||||||
@@ -1603,7 +1609,7 @@ function generateInner(ast: PageAst): string {
|
|||||||
];
|
];
|
||||||
const runtimeStateNames = runtimeComputedNames(orderedStates, orderedComputed, runtimeRoots);
|
const runtimeStateNames = runtimeComputedNames(orderedStates, orderedComputed, runtimeRoots);
|
||||||
const reactive: PageReactive | null =
|
const reactive: PageReactive | null =
|
||||||
reactiveNames.length > 0
|
reactiveNames.length > 0 || runtimeStateNames.size > 0
|
||||||
? { stateNames: new Set(reactiveNames), runtimeStateNames, scope: seedScope }
|
? { stateNames: new Set(reactiveNames), runtimeStateNames, scope: seedScope }
|
||||||
: null;
|
: null;
|
||||||
const loops: string[] = [];
|
const loops: string[] = [];
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { expect, test } from "bun:test";
|
||||||
|
import { generate, parse } from "../src/index.ts";
|
||||||
|
|
||||||
|
test("server-only loader data renders native input attributes without client state", async () => {
|
||||||
|
const code = generate(
|
||||||
|
parse(`page Profile {
|
||||||
|
load server account { return {} }
|
||||||
|
view {
|
||||||
|
<input value="{account.name}" readonly="{account.readonly}" />
|
||||||
|
<select><option value="light" selected="{account.theme === 'light'}">Light</option><option value="dark" selected="{account.theme === 'dark'}">Dark</option></select>
|
||||||
|
<div data-component="ProfileShell" identity="{account.identity}"></div>
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
);
|
||||||
|
const javascript = new Bun.Transpiler({ loader: "ts" }).transformSync(code);
|
||||||
|
const page = await import(
|
||||||
|
`data:text/javascript;base64,${Buffer.from(javascript).toString("base64")}`
|
||||||
|
);
|
||||||
|
const html = await page.default({
|
||||||
|
account: { name: 'A "<User>"', readonly: false, theme: "dark", identity: { name: "Alice" } },
|
||||||
|
});
|
||||||
|
expect(html).toContain('value="A "<User>""');
|
||||||
|
expect(html).not.toContain(" readonly");
|
||||||
|
expect(html).toContain('<option value="light">Light</option>');
|
||||||
|
expect(html).toContain('<option value="dark" selected>Dark</option>');
|
||||||
|
expect(html).toContain('identity="{"name":"Alice"}"');
|
||||||
|
expect(html).not.toContain("data-wrn-bind");
|
||||||
|
expect(html).not.toContain("{account.");
|
||||||
|
});
|
||||||
Reference in new issue
Block a user