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": {
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.22",
|
||||
"version": "0.8.23",
|
||||
"dependencies": {
|
||||
"@wrnexus/csr": "workspace:*",
|
||||
"@wrnexus/store": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.22",
|
||||
"version": "0.8.23",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -344,10 +344,16 @@ function renderAttrs(
|
||||
exprRefsState(expression, reactive.runtimeStateNames) &&
|
||||
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})}`);
|
||||
const sentinel = `\x00WRNEACH${dynamicExpressions.length - 1}\x00`;
|
||||
const marker = JSON.stringify([attr.name, attr.value]);
|
||||
return ` ${attr.name}="${sentinel}" data-wrn-bind-${bindIndex++}="${attrEscape(marker)}"`;
|
||||
return ` ${attr.name}="${sentinel}"${marker}`;
|
||||
}
|
||||
const initial = reactiveAttrValue(attr.value, reactive);
|
||||
if (initial === null) return base;
|
||||
@@ -1603,7 +1609,7 @@ function generateInner(ast: PageAst): string {
|
||||
];
|
||||
const runtimeStateNames = runtimeComputedNames(orderedStates, orderedComputed, runtimeRoots);
|
||||
const reactive: PageReactive | null =
|
||||
reactiveNames.length > 0
|
||||
reactiveNames.length > 0 || runtimeStateNames.size > 0
|
||||
? { stateNames: new Set(reactiveNames), runtimeStateNames, scope: seedScope }
|
||||
: null;
|
||||
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