import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync, } from "node:fs"; import { join, resolve } from "node:path"; const root = resolve(import.meta.dir, ".."); const projectPackage = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { wrnexus?: { version?: string }; }; const frameworkVersion = projectPackage.wrnexus?.version; if (!frameworkVersion) throw new Error("package.json is missing wrnexus.version"); const pages = join(root, "app", "pages"); const packagePages = join(pages, "packages"); const componentPages = join(pages, "components"); mkdirSync(packagePages, { recursive: true }); mkdirSync(componentPages, { recursive: true }); interface UiComponentReference { count: number; components: Array<{ name: string; mount: string; category: string; purpose: string; props: Array<{ name: string; type: string; required: boolean; default: string | null }>; slots: string[]; events: string[]; source: string; }>; } const uiReferencePath = join(root, "node_modules", "@wrnexus", "ui", "component-reference.json"); const uiReference: UiComponentReference | undefined = existsSync(uiReferencePath) ? JSON.parse(readFileSync(uiReferencePath, "utf8")) : undefined; const catalog = [ ["ai", "AI", "Server-side Anthropic client with generation and streaming."], ["authz", "Security", "Role, permission, policy, and authorization guards."], ["cli", "Tooling", "Create, develop, build, generate, test, and maintain WrNexus apps."], ["compiler", "Core", "Parser and code generators for the .wrn language."], ["core", "Core", "Contexts, middleware, security, sessions, caching, JSX, and realtime."], ["csr", "Frontend", "Reactive, navigation, and realtime browser runtimes."], ["db", "Data", "Database adapters, typed queries, models, migrations, and sessions."], ["dev-server", "Runtime", "Development and production servers, HMR, assets, and gateways."], ["encryption", "Security", "Hashing, HMAC, authenticated encryption, and key derivation."], ["helpers", "Tooling", "Safe Context URL helpers and forward-auth login redirects."], ["i18n", "Frontend", "Translation loading, locale resolution, and Intl formatting."], ["jwt", "Security", "HS256 JWT signing, verification, and bearer authentication."], ["mobile", "Native", "SSR-safe compatibility access to Capacitor plugins."], ["native", "Native", "Cross-platform browser and Capacitor capability registry."], ["oauth", "Security", "OAuth 2.0, PKCE, provider presets, and profile mapping."], ["pubsub", "Realtime", "In-process and Redis-backed publish/subscribe."], ["queue", "Data", "Background jobs with delay, concurrency, retry, and repetition."], ["reactive", "Frontend", "Small type-safe reactive signal primitives."], ["router", "Core", "Filesystem discovery, route matching, and typed route generation."], ["ssr", "Runtime", "Secure HTML document rendering and SEO metadata."], ["styles", "Frontend", "CSS pipeline, themes, fonts, profiles, and application config."], ["test", "Tooling", "WrNexus-aware component, route, and browser testing utilities."], ["tracking", "Runtime", "Error/event capture, middleware, filtering, and sinks."], ["ui", "Frontend", "Themeable server-rendered UI components and CSS."], ["uploader", "Data", "Validated local/S3 uploads and secure file serving."], ["validation", "Security", "Typed schemas, coercion, validation, and browser descriptors."], ] as const; const escape = (value: string) => value .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/\{/g, "{") .replace(/\}/g, "}"); function inline(value: string): string { return escape(value) .replace(/`([^`]+)`/g, "$1") .replace(/\*\*([^*]+)\*\*/g, "$1") .replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, '$1'); } interface DocHeading { id: string; title: string; level: number; } function markdown(source: string): { html: string; headings: DocHeading[] } { const lines = source.replace(/\r/g, "").split("\n"); const out: string[] = []; const headings: DocHeading[] = []; const usedIds = new Map(); let code: string[] | null = null; let language = ""; let list = false; let paragraph: string[] = []; let table: string[][] = []; const flushParagraph = () => { if (paragraph.length) out.push(`

${inline(paragraph.join(" "))}

`); paragraph = []; }; const closeList = () => { if (list) out.push(""); list = false; }; const flushTable = () => { if (!table.length) return; const separator = table[1]?.every((cell) => /^:?-{3,}:?$/.test(cell.trim())); const header = separator ? table[0]! : undefined; const rows = separator ? table.slice(2) : table; out.push('
'); if (header) { out.push( `${header.map((cell) => ``).join("")}`, ); } out.push( `${rows.map((row) => `${row.map((cell) => ``).join("")}`).join("")}
${inline(cell.trim())}
${inline(cell.trim())}
`, ); table = []; }; for (const line of lines) { if (/^\|.*\|\s*$/.test(line)) { flushParagraph(); closeList(); table.push(line.slice(1, line.lastIndexOf("|")).split("|")); continue; } flushTable(); const fence = /^```(.*)$/.exec(line); if (fence) { flushParagraph(); closeList(); if (code) { out.push( `
${escape(code.join("\n"))}
`, ); code = null; } else { code = []; language = fence[1]!.trim(); } continue; } if (code) { code.push(line); continue; } const heading = /^(#{1,4})\s+(.+)$/.exec(line); if (heading) { flushParagraph(); closeList(); const level = Math.min(4, heading[1]!.length + 1); const title = heading[2]!.replace(/[`*_]/g, "").trim(); const base = title .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-|-$/g, "") || "section"; const occurrence = usedIds.get(base) ?? 0; usedIds.set(base, occurrence + 1); const id = occurrence ? `${base}-${occurrence + 1}` : base; headings.push({ id, title, level }); out.push(`${inline(heading[2]!)}`); continue; } const item = /^[-*]\s+(.+)$/.exec(line); if (item) { flushParagraph(); if (!list) out.push("