Files
WRNexusJS/packages/dev-server/src/partial-build.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

74 lines
2.7 KiB
TypeScript

import { partialPrerender } from "@wrnexus/ssr";
import {
fillSlots,
normalizeComponentName,
parseComponentProps,
readElementBody,
} from "./runtime.ts";
export interface PartialBuildModule {
default?: unknown;
render?: (props?: Record<string, unknown>) => string | Promise<string>;
layout?: string | { name?: string; render?: (props?: Record<string, unknown>) => string };
__wrnexusBuildStaticShell?: (ctx?: Record<string, unknown>) => string | Promise<string>;
}
export interface PartialBuildEntry {
name: string;
mod: PartialBuildModule;
}
const MOUNT_OPEN_RE =
/<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-component="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;
/** Expand compiler component mounts at build time using only their pure render exports. */
export async function expandStaticComponents(
html: string,
components: readonly PartialBuildEntry[],
depth = 0,
): Promise<string> {
if (depth > 15) throw new Error("WRN-PARTIAL-STATIC-DEPTH: component nesting exceeds 15");
if (!html.includes("data-component=")) return html;
let output = "";
let cursor = 0;
for (;;) {
const match = MOUNT_OPEN_RE.exec(html.slice(cursor));
if (!match) return output + html.slice(cursor);
const start = cursor + match.index;
output += html.slice(cursor, start);
const [open, tag, attributes, name, selfClosing] = match;
const openEnd = start + open.length;
const body =
selfClosing === "/" ? { inner: "", end: openEnd } : readElementBody(html, tag!, openEnd);
const component = components.find(
(entry) => normalizeComponentName(entry.name) === normalizeComponentName(name!),
);
if (!component || typeof component.mod.render !== "function") {
throw new Error(`WRN-PARTIAL-STATIC-COMPONENT: '${name}' has no build-time renderer`);
}
const rendered = await component.mod.render(parseComponentProps(attributes!));
output += await expandStaticComponents(
fillSlots(String(rendered), body.inner),
components,
depth + 1,
);
cursor = body.end;
}
}
/** Produce the body shell stored in dist; dynamic region bodies are never evaluated here. */
export async function precomputePartialStaticShell(
page: PartialBuildModule,
components: readonly PartialBuildEntry[],
): Promise<{ shell: string; regions: number }> {
if (typeof page.__wrnexusBuildStaticShell !== "function") {
throw new Error("WRN-PARTIAL-STATIC-EXPORT: compiler did not emit a static-shell renderer");
}
const body = await expandStaticComponents(
String(await page.__wrnexusBuildStaticShell({})),
components,
);
const result = partialPrerender(body);
return { shell: result.shell, regions: result.regions.length };
}