docs: move component reference to showcase

This commit is contained in:
2026-07-29 13:31:10 +05:30
parent 19e20408db
commit 5a75e88f73
161 changed files with 17507 additions and 37637 deletions
+8 -87
View File
@@ -18,7 +18,6 @@ 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;
@@ -269,10 +268,10 @@ ${state}
<SkipLink label="Skip to content" href="#main" class="docs-skip-link" />
<header class="topbar">
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v${frameworkVersion}</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
</header>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
${content}
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS ${frameworkVersion}</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
<BackToTop />
@@ -333,7 +332,7 @@ for (const [name, category, summary] of catalog) {
const guide = markdown(readme);
const componentReference =
name === "ui" && uiReference
? `<section class="prose component-catalog-callout"><h2>Explore the component library</h2><p>The component reference now has a dedicated catalog with searchable cards and one complete page per component.</p><p><a class="primary" href="/components">Browse all ${uiReference.count} components →</a></p></section>`
? `<section class="prose component-catalog-callout"><h2>Explore the component library</h2><p>Browse interactive examples and complete component usage in the dedicated WRNexusJS component showcase.</p><p><a class="primary" href="https://component.wrnexusjs.dev/">Browse all ${uiReference.count} components →</a></p></section>`
: "";
const toc = [
{ id: "guide", title: "Guide", level: 2 },
@@ -353,90 +352,12 @@ for (const [name, category, summary] of catalog) {
writeFileSync(join(packagePages, `${name}.wrn`), shell(`@wrnexus/${name}`, summary, content));
}
if (!uiReference) throw new Error("Install @wrnexus/ui before generating component documentation");
const componentSlug = (name: string) =>
(name === "file-upload" ? "file-upload-primitive" : name)
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
.replace(/[^a-zA-Z0-9]+/g, "-")
.replace(/^-|-$/g, "")
.toLowerCase();
const componentCategories = [
...new Set(uiReference.components.map((item) => item.category)),
].sort();
const componentCards = uiReference.components
.map(
(item) =>
`<a class="component-card" href="/components/${componentSlug(item.name)}" data-show="(category === 'All' ? true : category === '${item.category}') ? (query === '' ? true : '${escape(`${item.name} ${item.mount} ${item.category} ${item.purpose}`.toLowerCase())}'.includes(query.toLowerCase())) : false"><span class="category">${escape(item.category)}</span><h2>${escape(item.name)}</h2><p>${escape(item.purpose)}</p><div class="component-card-meta"><span>${item.props.length} props</span><span>${item.slots.length} slots</span><span>${item.events.length} events</span></div><span class="card-link">View component →</span></a>`,
)
.join("\n");
const componentFilters = ["All", ...componentCategories]
.map(
(category) =>
`<button type="button" @click="category = '${category}'">${escape(category[0]!.toUpperCase() + category.slice(1))}<span>${category === "All" ? uiReference.count : uiReference.components.filter((item) => item.category === category).length}</span></button>`,
)
.join("");
writeFileSync(
join(pages, "components.wrn"),
shell(
"Components",
`Browse all ${uiReference.count} themeable WRNexusJS UI components, props, slots, events, and examples.`,
`<main id="main" class="page component-catalog-page"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><span aria-current="page">Components</span></nav><section class="hero compact"><span class="eyebrow">@wrnexus/ui · ${uiReference.count} components</span><h1>Component reference</h1><p>Production-ready, server-rendered components documented directly from the installed <code>.wrn</code> sources.</p><input class="search" type="search" placeholder="Search components, props, or categories…" @input="query = event.target.value" /></section><section class="category-filter" aria-label="Filter components by category"><div class="category-row">${componentFilters}</div><p>Showing <strong>{category}</strong> components</p></section><section class="component-grid">${componentCards}</section></main>`,
' state query = ""\n state category = "All"\n',
),
);
const currentComponentFiles = new Set<string>();
for (const item of uiReference.components) {
const slug = componentSlug(item.name);
currentComponentFiles.add(`${slug}.wrn`);
const sourcePath = join(root, "node_modules", "@wrnexus", "ui", item.source);
const source = existsSync(sourcePath) ? readFileSync(sourcePath, "utf8") : "";
const exampleAttributes = item.props
.filter((prop) => prop.required || !["class", "className"].includes(prop.name))
.slice(0, 5)
.map((prop) => {
const value = prop.required
? prop.type === "boolean"
? "true"
: prop.type === "number"
? "1"
: prop.type.includes("[]")
? "[]"
: prop.name
: (prop.default ?? "").replace(/^"|"$/g, "");
return `${prop.name}="${value || prop.name}"`;
})
.join("\n ");
const example = exampleAttributes
? `<${item.mount}\n ${exampleAttributes}\n/>`
: `<${item.mount} />`;
const formattedSource = source
.replace(/view \{\s*(?=<)/g, "view {\n ")
.replace(/>\s*</g, ">\n <")
.replace(/>\s*\}/g, ">\n }");
const propsRows = item.props.length
? item.props
.map(
(prop) =>
`<tr><td><code>${escape(prop.name)}</code></td><td><code>${escape(prop.type)}</code></td><td>${prop.required ? '<span class="required">Required</span>' : "Optional"}</td><td><code>${prop.default === null ? "—" : escape(prop.default)}</code></td></tr>`,
)
.join("")
: '<tr><td colspan="4">This component has no props.</td></tr>';
const content = `<main class="portal-main docs-layout component-detail-page"><article id="main" class="documentation prose standalone"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/components">Components</a><span>/</span><span aria-current="page">${escape(item.name)}</span></nav><section class="doc-intro"><span class="eyebrow">${escape(item.category)} component</span><h1>${escape(item.name)}</h1><p>${escape(item.purpose)}</p><div class="doc-meta"><span>@wrnexus/ui ${frameworkVersion}</span><span>${item.props.length} props</span><span>${item.slots.length} slots</span><span>${item.events.length} events</span></div></section><section id="usage"><h2>Usage</h2><p>Components are auto-discovered. Use the component directly in any <code>.wrn</code> view:</p><pre data-language="wrn"><code>${escape(example)}</code></pre><p>Legacy mount name: <code>data-component=&quot;${escape(item.mount)}&quot;</code>.</p></section><section id="props"><h2>Props and attributes</h2><div class="table-wrap"><table><thead><tr><th>Prop</th><th>Type</th><th>Requirement</th><th>Default</th></tr></thead><tbody>${propsRows}</tbody></table></div></section><section id="slots"><h2>Slots</h2>${item.slots.length ? `<ul>${item.slots.map((slot) => `<li><code>${escape(slot)}</code></li>`).join("")}</ul>` : "<p>This component does not declare a slot.</p>"}</section><section id="events"><h2>Events</h2>${item.events.length ? `<ul>${item.events.map((event) => `<li><code>${escape(event)}</code></li>`).join("")}</ul>` : "<p>This component does not declare a custom event.</p>"}</section><section id="source"><h2>Source contract</h2><p>Installed source: <code>${escape(item.source)}</code></p><pre data-language="wrn"><code>${escape(formattedSource)}</code></pre></section></article><aside class="on-this-page"><h2>On this page</h2><nav><a href="#usage">Usage</a><a href="#props">Props and attributes</a><a href="#slots">Slots</a><a href="#events">Events</a><a href="#source">Source contract</a></nav></aside></main>`;
writeFileSync(
join(componentPages, `${slug}.wrn`),
shell(`${item.name} component`, item.purpose, content),
);
}
for (const file of readdirSync(componentPages)) {
if (!file.endsWith(".wrn") || currentComponentFiles.has(file)) {
continue;
if (!uiReference) throw new Error("Install @wrnexus/ui before generating documentation");
if (existsSync(join(pages, "components.wrn"))) unlinkSync(join(pages, "components.wrn"));
if (existsSync(componentPages)) {
for (const file of readdirSync(componentPages).filter((name) => name.endsWith(".wrn"))) {
unlinkSync(join(componentPages, file));
}
unlinkSync(join(componentPages, file));
console.log(`Removed stale generated component page: ${file}`);
}
writeFileSync(
+40 -25
View File
@@ -23,23 +23,19 @@ const uiReference = existsSync(uiReferencePath)
name: string;
mount: string;
category: string;
purpose: string;
source: string;
props: Array<{ name: string; type: string; required: boolean; default: string | null }>;
slots: string[];
events: string[];
}>;
})
: undefined;
const componentSlug = (name: string) =>
(name === "file-upload" ? "file-upload-primitive" : name)
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
.replace(/[^a-zA-Z0-9]+/g, "-")
.replace(/^-|-$/g, "")
.toLowerCase();
const uiReferenceText = uiReference
? uiReference.components
.map(
(component) =>
`### ${component.name}\nMount: data-component="${component.mount}"\nCategory: ${component.category}\nProps: ${component.props.length ? component.props.map((prop) => `${prop.name}: ${prop.type}${prop.required ? " (required)" : ` = ${prop.default}`}`).join(", ") : "none"}\nSlots: ${component.slots.join(", ") || "none"}\nEvents: ${component.events.join(", ") || "none"}`,
`### ${component.name}\nShowcase: https://component.wrnexusjs.dev/\nMount: <${component.mount} /> (legacy: data-component="${component.mount}")\nCategory: ${component.category}\nPurpose: ${component.purpose}\nProps: ${component.props.length ? component.props.map((prop) => `${prop.name}: ${prop.type}${prop.required ? " (required)" : ` = ${prop.default}`}`).join(", ") : "none"}\nSlots: ${component.slots.join(", ") || "none"}\nEvents: ${component.events.join(", ") || "none"}`,
)
.join("\n\n")
: "UI component reference unavailable; install the release-aligned @wrnexus/ui package.";
@@ -55,7 +51,7 @@ const code = (s: string) =>
const navigation = [
["/getting-started", "Get started"],
["/packages", "Packages"],
["/components", "Components"],
["https://component.wrnexusjs.dev/", "Components"],
["/language", "Language"],
["/architecture", "Architecture"],
] as const;
@@ -126,8 +122,8 @@ function marketingShell(description: string, body: string) {
view {
<SkipLink label="Skip to content" href="#main" class="docs-skip-link" />
<div class="docs-shell marketing-shell">
<header class="topbar marketing-header"><a class="brand" href="/"><span>W</span> WRNexusJS</a><nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav><div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v${version}</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme">◐</button></div></header>
<div class="marketing-mobile-nav"><details><summary>Menu</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="/components">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/examples">Examples</a></nav></details></div>
<header class="topbar marketing-header"><a class="brand" href="/"><span>W</span> WRNexusJS</a><nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav><div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v${version}</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme">◐</button></div></header>
<div class="marketing-mobile-nav"><details><summary>Menu</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/examples">Examples</a></nav></details></div>
<main id="main" class="marketing-main">${marketingBody}</main>
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS ${version}</strong><span>Build from the server. Ship only what matters.</span></p></div><nav aria-label="Footer"><a href="/getting-started">Documentation</a><a href="/roadmap">Roadmap</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Bun-native · Created by <a href="https://workroot.in/">WorkRoot</a></p></footer>
<BackToTop />
@@ -471,7 +467,6 @@ const routes = [
"/architecture",
"/language",
"/packages",
"/components",
...Object.keys(guides).map((x) => `/guides/${x}`),
"/examples",
"/showcase",
@@ -485,13 +480,7 @@ const routes = [
"/search",
"/404",
].filter(Boolean);
const urls = [
...new Set([
...routes,
...packageNames.map((x) => `/packages/${x}`),
...(uiReference?.components.map((item) => `/components/${componentSlug(item.name)}`) ?? []),
]),
];
const urls = [...new Set([...routes, ...packageNames.map((x) => `/packages/${x}`)])];
writeFileSync(
join(pub, "robots.txt"),
`User-agent: *\nAllow: /\nSitemap: https://wrnexusjs.dev/sitemap.xml\n`,
@@ -523,19 +512,22 @@ await writeFormatted(
2,
),
);
writeFileSync(
join(pub, "llms-full.txt"),
`# WRNexusJS ${version} comprehensive documentation index\n\nStatus: Private Developer Preview. Runtime: Bun. UI language: .wrn.\nThis is the documentation application, not the framework monorepo.\n\n${urls.map((x) => `- https://wrnexusjs.dev${x}`).join("\n")}\n\nFor exact APIs, use installed package README files followed by exported declarations.\n\n# Complete @wrnexus/ui component reference\n\n${uiReferenceText}\n`,
);
const packageMarker = "# Installed package documentation";
const existingGuide = readFileSync(join(pub, "llms.txt"), "utf8");
let frameworkGuide = existingGuide.split(`\n${packageMarker}`)[0]!.trim();
let frameworkGuide = existingGuide
.split(
/\n# (?:Canonical documentation locations|Installed package index|UI component catalog|Installed package documentation)/,
)[0]!
.trim();
if (frameworkGuide.startsWith("# WRNexusJS documentation ")) {
frameworkGuide = frameworkGuide.replace(
/^# WRNexusJS documentation [^\n]+/,
`# WRNexusJS documentation ${version}`,
);
frameworkGuide = frameworkGuide.replace(
/^Status: Private Developer Preview\. This site documents \d+ release-aligned packages\.$/m,
`Status: Private Developer Preview. This site documents ${packageCount} release-aligned packages.`,
);
} else {
frameworkGuide = `# WRNexusJS documentation ${version}\n\nStatus: Private Developer Preview. This site documents ${packageCount} release-aligned packages.\n\n${frameworkGuide}`;
}
@@ -547,9 +539,32 @@ const installedPackageDocs = packageNames
return `## @wrnexus/${name}\n\nDocumentation URL: https://wrnexusjs.dev/packages/${name}\n\n${readme}\n\n### Exported TypeScript declarations\n\n\`\`\`ts\n${declarations}\n\`\`\``;
})
.join("\n\n---\n\n");
const packageIndex = packageNames
.map(
(name) =>
`- @wrnexus/${name} ${version}: https://wrnexusjs.dev/packages/${name} (README and complete exported TypeScript API are included in llms-full.txt)`,
)
.join("\n");
const componentSources =
uiReference?.components
.map((component) => {
const sourcePath = join(root, "node_modules", "@wrnexus", "ui", component.source);
const source = existsSync(sourcePath)
? readFileSync(sourcePath, "utf8").trim()
: "Source unavailable in the installed package.";
return `## ${component.name}\n\n${uiReferenceText
.split(`### ${component.name}\n`)[1]
?.split("\n\n### ")[0]
?.trim()}\n\n### Complete .wrn source contract\n\n\`\`\`wrn\n${source}\n\`\`\``;
})
.join("\n\n---\n\n") ?? "";
writeFileSync(
join(pub, "llms.txt"),
`${frameworkGuide}\n\n# UI component catalog\n\nThe installed @wrnexus/ui release contains ${uiReference?.count ?? 0} documented components. Every mount name, prop type, required/default status, slot, and event is included below and in llms-full.txt.\n\n${uiReferenceText}\n\n${packageMarker}\n\nThe following README files and declarations come from the installed private ${version} release.\n\n${installedPackageDocs}\n`,
`${frameworkGuide}\n\n# Canonical documentation locations\n\n- Framework and package documentation: https://wrnexusjs.dev/\n- Interactive UI component showcase and examples: https://component.wrnexusjs.dev/\n- Comprehensive AI reference: https://wrnexusjs.dev/llms-full.txt\n\n# Installed package index\n\n${packageIndex}\n\n# UI component catalog\n\nThe installed @wrnexus/ui ${version} release contains ${uiReference?.count ?? 0} documented components. The contracts below include every mount name, purpose, prop type, required/default status, slot, and event. Interactive examples live only on the dedicated component showcase.\n\n${uiReferenceText}\n`,
);
writeFileSync(
join(pub, "llms-full.txt"),
`${frameworkGuide}\n\n# Canonical documentation locations\n\n- Framework and package documentation: https://wrnexusjs.dev/\n- Interactive UI component showcase and examples: https://component.wrnexusjs.dev/\n\n${packageMarker}\n\nThe following README files and declarations come from the installed private ${version} release.\n\n${installedPackageDocs}\n\n# Complete @wrnexus/ui component reference and source contracts\n\n${componentSources}\n`,
);
const auditDocs: Record<string, string> = {