ssrSource
uses node:path
The server-rendering entry for a component app: streams its HTML. The shell (everything outside <Suspense>
boundaries still waiting) goes out first, the rest as it's ready. Server components render through the host
(see renderPage).
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import type { AppState } from "#codebase/tcmts/cli/web"; import { exportId } from "#codebase/tcmts/core/hash"; import path from "node:path"; export function ssrSource(cb: Codebase, appName: string, s: AppState, cacheRel: string): string { const rec = cb.store.getDef(s.hash)!; return [ `// generated by tcm: server rendering for ${appName} ${rec.hash}`, `import { Writable } from "node:stream";`, `import { createElement } from "${s.react!.react}";`, `import { renderToPipeableStream, resumeToPipeableStream } from "${s.react!.dom}/server";`, `import { prerenderToNodeStream } from "${s.react!.dom}/static";`, `import { ${exportId(rec.hash)} as App } from "${cacheRel}/${path.basename(cb.modulePath(rec.componentHash))}";`, `/** Resolves once the shell is written; \`done\` once all of it is. Rejects if the shell can't render. */`, `export function render(write: (html: string) => void, onError: (e: unknown) => void, onHeaders?: (headers: Headers) => void): Promise<{ done: Promise<void> }> {`, ` return new Promise((shell, fail) => {`, ` let finished!: () => void;`, ` const done = new Promise<void>((r) => (finished = r));`, ` const sink = new Writable({ write(chunk, _e, next) { write(String(chunk)); next(); }, final(next) { finished(); next(); } });`, ` const stream = renderToPipeableStream(createElement(App), {`, ` onShellReady() { stream.pipe(sink); shell({ done }); },`, ` onShellError: fail,`, ` onError,`, ` ...(onHeaders ? { onHeaders } : {}),`, ` });`, ` });`, `}`, `/** The page's shell, rendered ahead: what's rendered until \`signal\` aborts, and what's left (\`postponed\`, null if nothing). */`, `export async function prerenderShell(signal: AbortSignal, onError: (e: unknown) => void): Promise<{ html: string; postponed: unknown }> {`, ` const { prelude, postponed } = await prerenderToNodeStream(createElement(App), { signal, onError });`, ` let html = "";`, ` for await (const chunk of prelude) html += String(chunk);`, ` return { html, postponed: postponed ?? null };`, `}`, `/** What a shell left, rendered for this request: as \`render\` streams, after the shell. */`, `export function resume(postponed: unknown, write: (html: string) => void, onError: (e: unknown) => void): Promise<{ done: Promise<void> }> {`, ` return new Promise((shell, fail) => {`, ` let finished!: () => void;`, ` const done = new Promise<void>((r) => (finished = r));`, ` const sink = new Writable({ write(chunk, _e, next) { write(String(chunk)); next(); }, final(next) { finished(); next(); } });`, ` const stream = resumeToPipeableStream(createElement(App), postponed as never, {`, ` onShellReady() { stream.pipe(sink); shell({ done }); },`, ` onShellError: fail,`, ` onError,`, ` });`, ` });`, `}`, "", ].join("\n"); }