feat: replace the ssr/client data blocks with apis blocks
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -567,29 +567,6 @@ function compileIfExpr(node: IfNode): string {
|
||||
return "${" + expr + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect every server-control expression in a view (recursively): `{#each}` list
|
||||
* expressions and `{#if}` conditions. Used to wrn up raw SSR data consts.
|
||||
*/
|
||||
function collectControlExprs(nodes: ViewNode[], out: string[] = []): string[] {
|
||||
for (const node of nodes) {
|
||||
if (node.type === "text") continue;
|
||||
if (node.type === "each") {
|
||||
out.push(node.list);
|
||||
collectControlExprs(node.body, out);
|
||||
collectControlExprs(node.empty, out);
|
||||
} else if (node.type === "if") {
|
||||
for (const b of node.branches) {
|
||||
if (b.cond) out.push(b.cond);
|
||||
collectControlExprs(b.body, out);
|
||||
}
|
||||
} else if (node.type === "element") {
|
||||
collectControlExprs(node.children, out);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function renderNode(
|
||||
node: ViewNode,
|
||||
ssrBindings: SsrBinding[],
|
||||
@@ -741,39 +718,32 @@ function renderNode(
|
||||
const csrText = attrValue(node.attrs, "csrText");
|
||||
|
||||
const csrId =
|
||||
apiBinding?.mode === "client"
|
||||
? csrMarker(csrBindings, renderBinding(apiBinding))
|
||||
: csrGet && csrText
|
||||
? csrMarker(csrBindings, {
|
||||
method: "GET",
|
||||
path: apiRoutePath(csrGet),
|
||||
body: expressionBody(csrText),
|
||||
helpers: "",
|
||||
})
|
||||
: undefined;
|
||||
csrGet && csrText
|
||||
? csrMarker(csrBindings, {
|
||||
method: "GET",
|
||||
path: apiRoutePath(csrGet),
|
||||
body: expressionBody(csrText),
|
||||
helpers: "",
|
||||
})
|
||||
: undefined;
|
||||
|
||||
// Void elements (<br>, <img>, …) have no closing tag and no children.
|
||||
if (VOID_ELEMENTS.has(node.tag.toLowerCase())) {
|
||||
return `<${node.tag}${renderAttrs(node.attrs, csrId, reactive, loops)}>`;
|
||||
}
|
||||
|
||||
const inner =
|
||||
apiBinding?.mode === "ssr"
|
||||
? ssrMarker(ssrBindings, renderBinding(apiBinding))
|
||||
: apiBinding?.mode === "any"
|
||||
? apiCallMarker(loops, parsedApi!.name, parsedApi!.args)
|
||||
: ssrGet && ssrText
|
||||
? ssrMarker(ssrBindings, {
|
||||
method: "GET",
|
||||
path: apiRoutePath(ssrGet),
|
||||
body: expressionBody(ssrText),
|
||||
helpers: "",
|
||||
})
|
||||
: node.children
|
||||
.map((child) =>
|
||||
renderNode(child, ssrBindings, csrBindings, apiBindings, loops, reactive),
|
||||
)
|
||||
.join("");
|
||||
const inner = apiBinding
|
||||
? apiCallMarker(loops, parsedApi!.name, parsedApi!.args)
|
||||
: ssrGet && ssrText
|
||||
? ssrMarker(ssrBindings, {
|
||||
method: "GET",
|
||||
path: apiRoutePath(ssrGet),
|
||||
body: expressionBody(ssrText),
|
||||
helpers: "",
|
||||
})
|
||||
: node.children
|
||||
.map((child) => renderNode(child, ssrBindings, csrBindings, apiBindings, loops, reactive))
|
||||
.join("");
|
||||
|
||||
return `<${node.tag}${renderAttrs(node.attrs, csrId, reactive, loops)}>${inner}</${node.tag}>`;
|
||||
}
|
||||
@@ -912,16 +882,6 @@ function csrMarker(bindings: CsrBinding[], binding: RenderBinding): string {
|
||||
return id;
|
||||
}
|
||||
|
||||
function renderBinding(binding: NamedDataBinding): RenderBinding {
|
||||
return {
|
||||
method: binding.method,
|
||||
path: binding.path,
|
||||
body: binding.body,
|
||||
helpers: binding.helpers,
|
||||
...(binding.errorBody ? { errorBody: binding.errorBody } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function hasClientBehavior(nodes: ViewNode[]): boolean {
|
||||
return nodes.some((node) => {
|
||||
// `{t:key}` is i18n sugar resolved server-side — not client reactivity.
|
||||
@@ -963,18 +923,6 @@ function dataBody(source: string): string {
|
||||
return /\breturn\b/.test(trimmed) ? trimmed : expressionBody(trimmed);
|
||||
}
|
||||
|
||||
function modeHelpers(ast: PageAst, mode: DataMode, sharedHelpers: string): string {
|
||||
return [
|
||||
sharedHelpers,
|
||||
...ast.modeFunctions
|
||||
.filter((block) => block.mode === mode)
|
||||
.map((block) => block.body.trim())
|
||||
.filter(Boolean),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
function apiBindingMap(ast: PageAst, sharedHelpers: string): Map<string, NamedDataBinding> {
|
||||
const bindings = new Map<string, NamedDataBinding>();
|
||||
|
||||
@@ -997,7 +945,7 @@ function apiBindingMap(ast: PageAst, sharedHelpers: string): Map<string, NamedDa
|
||||
// legacy blocks and sectioned blocks without `error` keep failures
|
||||
// propagating exactly as before.
|
||||
...(errorSection ? { errorBody: errorSection } : {}),
|
||||
helpers: modeHelpers(ast, block.mode, sharedHelpers),
|
||||
helpers: sharedHelpers,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1664,22 +1612,7 @@ function generateInner(ast: PageAst): string {
|
||||
}
|
||||
}
|
||||
|
||||
// Server loops iterate raw SSR data. Declare a named const for every `ssr` data
|
||||
// binding a loop references, so `{#each <name> as …}` can iterate the real value.
|
||||
const loopConsts: string[] = [];
|
||||
if (loops.length > 0) {
|
||||
const lists = collectControlExprs(ast.view);
|
||||
for (const [name, binding] of apiBindings) {
|
||||
if (binding.mode !== "ssr") continue;
|
||||
if (!lists.some((expr) => new RegExp(`\\b${name}\\b`).test(expr))) continue;
|
||||
const errorBodyProp = binding.errorBody
|
||||
? `, errorBody: ${JSON.stringify(binding.errorBody)}`
|
||||
: "";
|
||||
loopConsts.push(
|
||||
` const ${name} = await __wrnexusResolveApiBinding({ path: ${JSON.stringify(binding.path)}, method: ${JSON.stringify(binding.method)}, body: ${JSON.stringify(binding.body)}, helpers: ${JSON.stringify(binding.helpers)}${errorBodyProp} }, ctx);`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const needsSsrRuntime = ssrBindings.length > 0 || loops.length > 0 || runtimeStateNames.size > 0;
|
||||
const needsRuntimeHelpers = needsSsrRuntime || hasServerApis;
|
||||
|
||||
Reference in New Issue
Block a user