release: WRNexusJS 0.8.0
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user