@openpkg-ts/doc-generator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1160 @@
1
+ import {
2
+ buildSignatureString,
3
+ formatParameters,
4
+ formatReturnType,
5
+ formatSchema,
6
+ getMethods,
7
+ getProperties,
8
+ sortByName
9
+ } from "./chunk-taeg9090.js";
10
+
11
+ // src/render/html.ts
12
+ var defaultCSS = `
13
+ :root {
14
+ --text: #1a1a1a;
15
+ --text-muted: #666;
16
+ --bg: #fff;
17
+ --bg-code: #f5f5f5;
18
+ --border: #e5e5e5;
19
+ --accent: #0066cc;
20
+ --font-mono: 'SF Mono', Monaco, 'Cascadia Code', monospace;
21
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
22
+ }
23
+ @media (prefers-color-scheme: dark) {
24
+ :root {
25
+ --text: #e5e5e5;
26
+ --text-muted: #999;
27
+ --bg: #1a1a1a;
28
+ --bg-code: #2a2a2a;
29
+ --border: #333;
30
+ --accent: #4da6ff;
31
+ }
32
+ }
33
+ * { box-sizing: border-box; margin: 0; padding: 0; }
34
+ body {
35
+ font-family: var(--font-sans);
36
+ line-height: 1.6;
37
+ color: var(--text);
38
+ background: var(--bg);
39
+ max-width: 900px;
40
+ margin: 0 auto;
41
+ padding: 2rem;
42
+ }
43
+ h1, h2, h3, h4 { margin-top: 2rem; margin-bottom: 1rem; }
44
+ h1 { font-size: 2rem; border-bottom: 2px solid var(--border); padding-bottom: 0.5rem; }
45
+ h2 { font-size: 1.5rem; color: var(--text-muted); }
46
+ h3 { font-size: 1.1rem; }
47
+ p { margin-bottom: 1rem; }
48
+ code {
49
+ font-family: var(--font-mono);
50
+ background: var(--bg-code);
51
+ padding: 0.2em 0.4em;
52
+ border-radius: 4px;
53
+ font-size: 0.9em;
54
+ }
55
+ pre {
56
+ background: var(--bg-code);
57
+ padding: 1rem;
58
+ border-radius: 8px;
59
+ overflow-x: auto;
60
+ margin: 1rem 0;
61
+ }
62
+ pre code { background: none; padding: 0; }
63
+ .signature { font-family: var(--font-mono); font-size: 0.95rem; }
64
+ .deprecated { opacity: 0.6; }
65
+ .deprecated::before { content: "[Deprecated] "; color: #cc6600; }
66
+ .badge {
67
+ display: inline-block;
68
+ padding: 0.2em 0.5em;
69
+ font-size: 0.75rem;
70
+ border-radius: 4px;
71
+ background: var(--accent);
72
+ color: white;
73
+ margin-left: 0.5rem;
74
+ }
75
+ .param-table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
76
+ .param-table th, .param-table td {
77
+ text-align: left;
78
+ padding: 0.75rem;
79
+ border-bottom: 1px solid var(--border);
80
+ }
81
+ .param-table th { font-weight: 600; color: var(--text-muted); }
82
+ .kind-section { margin-top: 3rem; }
83
+ .export-card {
84
+ border: 1px solid var(--border);
85
+ border-radius: 8px;
86
+ padding: 1.5rem;
87
+ margin: 1rem 0;
88
+ }
89
+ .export-card h3 { margin-top: 0; }
90
+ a { color: var(--accent); text-decoration: none; }
91
+ a:hover { text-decoration: underline; }
92
+ nav { margin-bottom: 2rem; }
93
+ nav ul { list-style: none; display: flex; flex-wrap: wrap; gap: 1rem; }
94
+ nav a { padding: 0.5rem 1rem; background: var(--bg-code); border-radius: 4px; }
95
+ `;
96
+ function escapeHTML(str) {
97
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
98
+ }
99
+ function renderSignature(exp) {
100
+ const sig = buildSignatureString(exp);
101
+ return `<pre class="signature"><code>${escapeHTML(sig)}</code></pre>`;
102
+ }
103
+ function renderParameters(sig) {
104
+ if (!sig?.parameters?.length)
105
+ return "";
106
+ const rows = sig.parameters.map((p) => {
107
+ const type = escapeHTML(formatSchema(p.schema));
108
+ const required = p.required !== false ? "required" : "optional";
109
+ const desc = p.description ? escapeHTML(p.description) : "-";
110
+ const rest = p.rest ? "..." : "";
111
+ return `
112
+ <tr>
113
+ <td><code>${rest}${escapeHTML(p.name)}</code></td>
114
+ <td><code>${type}</code></td>
115
+ <td>${required}</td>
116
+ <td>${desc}</td>
117
+ </tr>`;
118
+ }).join("");
119
+ return `
120
+ <h3>Parameters</h3>
121
+ <table class="param-table">
122
+ <thead>
123
+ <tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th></tr>
124
+ </thead>
125
+ <tbody>${rows}</tbody>
126
+ </table>`;
127
+ }
128
+ function renderReturns(sig) {
129
+ if (!sig?.returns)
130
+ return "";
131
+ const type = escapeHTML(formatSchema(sig.returns.schema));
132
+ const desc = sig.returns.description ? `<p>${escapeHTML(sig.returns.description)}</p>` : "";
133
+ return `
134
+ <h3>Returns</h3>
135
+ <p><code>${type}</code></p>
136
+ ${desc}`;
137
+ }
138
+ function renderExamples(examples) {
139
+ if (!examples?.length)
140
+ return "";
141
+ const blocks = examples.map((ex) => {
142
+ if (typeof ex === "string") {
143
+ return `<pre><code>${escapeHTML(ex)}</code></pre>`;
144
+ }
145
+ const title = ex.title ? `<h4>${escapeHTML(ex.title)}</h4>` : "";
146
+ const desc = ex.description ? `<p>${escapeHTML(ex.description)}</p>` : "";
147
+ return `${title}${desc}<pre><code class="language-${ex.language || "ts"}">${escapeHTML(ex.code)}</code></pre>`;
148
+ }).join("");
149
+ return `<h3>Examples</h3>${blocks}`;
150
+ }
151
+ function renderProperties(members) {
152
+ const props = getProperties(members);
153
+ if (!props.length)
154
+ return "";
155
+ const rows = props.map((p) => {
156
+ const type = escapeHTML(formatSchema(p.schema));
157
+ const desc = p.description ? escapeHTML(p.description) : "-";
158
+ return `
159
+ <tr>
160
+ <td><code>${escapeHTML(p.name || "")}</code></td>
161
+ <td><code>${type}</code></td>
162
+ <td>${desc}</td>
163
+ </tr>`;
164
+ }).join("");
165
+ return `
166
+ <h3>Properties</h3>
167
+ <table class="param-table">
168
+ <thead><tr><th>Name</th><th>Type</th><th>Description</th></tr></thead>
169
+ <tbody>${rows}</tbody>
170
+ </table>`;
171
+ }
172
+ function renderMethods(members) {
173
+ const methods = getMethods(members);
174
+ if (!methods.length)
175
+ return "";
176
+ const items = methods.map((m) => {
177
+ const sig = m.signatures?.[0];
178
+ const params = escapeHTML(formatParameters(sig));
179
+ const returnType = escapeHTML(formatReturnType(sig));
180
+ const desc = m.description ? `<p>${escapeHTML(m.description)}</p>` : "";
181
+ return `
182
+ <div class="method">
183
+ <h4><code>${escapeHTML(m.name || "")}${params}: ${returnType}</code></h4>
184
+ ${desc}
185
+ </div>`;
186
+ }).join("");
187
+ return `<h3>Methods</h3>${items}`;
188
+ }
189
+ function renderEnumMembers(members) {
190
+ if (!members?.length)
191
+ return "";
192
+ const items = members.map((m) => {
193
+ const desc = m.description ? ` - ${escapeHTML(m.description)}` : "";
194
+ return `<li><code>${escapeHTML(m.name || "")}</code>${desc}</li>`;
195
+ }).join("");
196
+ return `<h3>Members</h3><ul>${items}</ul>`;
197
+ }
198
+ function renderExport(exp) {
199
+ const deprecated = exp.deprecated ? " deprecated" : "";
200
+ const badge = exp.deprecated ? '<span class="badge">Deprecated</span>' : "";
201
+ const desc = exp.description ? `<p>${escapeHTML(exp.description)}</p>` : "";
202
+ const primarySig = exp.signatures?.[0];
203
+ let content = "";
204
+ switch (exp.kind) {
205
+ case "function":
206
+ content = renderParameters(primarySig) + renderReturns(primarySig);
207
+ break;
208
+ case "class":
209
+ case "interface":
210
+ content = renderProperties(exp.members) + renderMethods(exp.members);
211
+ break;
212
+ case "enum":
213
+ content = renderEnumMembers(exp.members);
214
+ break;
215
+ }
216
+ content += renderExamples(exp.examples);
217
+ return `
218
+ <article class="export-card${deprecated}" id="${exp.id}">
219
+ <h3>${escapeHTML(exp.name)}${badge}</h3>
220
+ ${renderSignature(exp)}
221
+ ${desc}
222
+ ${content}
223
+ </article>`;
224
+ }
225
+ function toHTML(spec, options = {}) {
226
+ const { includeStyles = true, fullDocument = true, customCSS = "" } = options;
227
+ if (options.export) {
228
+ const exp = spec.exports.find((e) => e.name === options.export || e.id === options.export);
229
+ if (!exp) {
230
+ throw new Error(`Export not found: ${options.export}`);
231
+ }
232
+ const content2 = renderExport(exp);
233
+ if (!fullDocument)
234
+ return content2;
235
+ const title2 = options.title || `${exp.name} | ${spec.meta.name}`;
236
+ const styles2 = includeStyles ? `<style>${defaultCSS}${customCSS}</style>` : "";
237
+ return `<!DOCTYPE html>
238
+ <html lang="en">
239
+ <head>
240
+ <meta charset="UTF-8">
241
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
242
+ <title>${escapeHTML(title2)}</title>
243
+ ${styles2}
244
+ ${options.headContent || ""}
245
+ </head>
246
+ <body>
247
+ <main>
248
+ <h1>${escapeHTML(exp.name)}</h1>
249
+ ${content2}
250
+ </main>
251
+ </body>
252
+ </html>`;
253
+ }
254
+ const title = options.title || `${spec.meta.name} API Reference`;
255
+ const description = spec.meta.description ? `<p>${escapeHTML(spec.meta.description)}</p>` : "";
256
+ const byKind = {};
257
+ for (const exp of spec.exports) {
258
+ if (!byKind[exp.kind])
259
+ byKind[exp.kind] = [];
260
+ byKind[exp.kind].push(exp);
261
+ }
262
+ const navItems = Object.entries(byKind).map(([kind, exports]) => {
263
+ const links = exports.map((e) => `<a href="#${e.id}">${escapeHTML(e.name)}</a>`).join("");
264
+ return `<li><strong>${kind}s:</strong> ${links}</li>`;
265
+ }).join("");
266
+ const nav = `<nav><ul>${navItems}</ul></nav>`;
267
+ const kindOrder = ["function", "class", "interface", "type", "enum", "variable"];
268
+ const sections = kindOrder.filter((kind) => byKind[kind]?.length).map((kind) => {
269
+ const exports = byKind[kind].map(renderExport).join("");
270
+ return `
271
+ <section class="kind-section">
272
+ <h2>${kind.charAt(0).toUpperCase() + kind.slice(1)}s</h2>
273
+ ${exports}
274
+ </section>`;
275
+ }).join("");
276
+ const content = `
277
+ <header>
278
+ <h1>${escapeHTML(spec.meta.name)} API Reference</h1>
279
+ ${description}
280
+ </header>
281
+ ${nav}
282
+ ${sections}`;
283
+ if (!fullDocument)
284
+ return content;
285
+ const styles = includeStyles ? `<style>${defaultCSS}${customCSS}</style>` : "";
286
+ return `<!DOCTYPE html>
287
+ <html lang="en">
288
+ <head>
289
+ <meta charset="UTF-8">
290
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
291
+ <title>${escapeHTML(title)}</title>
292
+ <meta name="description" content="${escapeHTML(spec.meta.description || "")}">
293
+ ${styles}
294
+ ${options.headContent || ""}
295
+ </head>
296
+ <body>
297
+ <main>
298
+ ${content}
299
+ </main>
300
+ </body>
301
+ </html>`;
302
+ }
303
+
304
+ // src/render/json.ts
305
+ function simplifySignature(sig) {
306
+ if (!sig)
307
+ return;
308
+ return {
309
+ parameters: (sig.parameters || []).map((p) => ({
310
+ name: p.name,
311
+ type: formatSchema(p.schema),
312
+ required: p.required !== false,
313
+ description: p.description,
314
+ default: p.default,
315
+ rest: p.rest
316
+ })),
317
+ returns: sig.returns ? {
318
+ type: formatSchema(sig.returns.schema),
319
+ description: sig.returns.description
320
+ } : undefined,
321
+ description: sig.description,
322
+ typeParameters: sig.typeParameters?.map((tp) => `${tp.name}${tp.constraint ? ` extends ${tp.constraint}` : ""}`)
323
+ };
324
+ }
325
+ function simplifyMember(member) {
326
+ const isMethod = !!member.signatures?.length;
327
+ return {
328
+ name: member.name || "",
329
+ kind: isMethod ? "method" : "property",
330
+ type: isMethod ? undefined : formatSchema(member.schema),
331
+ description: member.description,
332
+ visibility: member.visibility || "public",
333
+ signature: isMethod ? simplifySignature(member.signatures?.[0]) : undefined
334
+ };
335
+ }
336
+ function simplifyExample(example) {
337
+ if (typeof example === "string") {
338
+ return { code: example, language: "ts" };
339
+ }
340
+ return {
341
+ code: example.code,
342
+ title: example.title,
343
+ description: example.description,
344
+ language: example.language || "ts"
345
+ };
346
+ }
347
+ function simplifyExport(exp) {
348
+ const primarySig = exp.signatures?.[0];
349
+ const simplified = {
350
+ id: exp.id,
351
+ name: exp.name,
352
+ kind: exp.kind,
353
+ signature: buildSignatureString(exp),
354
+ description: exp.description,
355
+ deprecated: exp.deprecated === true,
356
+ tags: (exp.tags || []).map((t) => ({ name: t.name, text: t.text })),
357
+ extends: exp.extends,
358
+ implements: exp.implements,
359
+ sourceFile: exp.source?.file,
360
+ sourceLine: exp.source?.line
361
+ };
362
+ switch (exp.kind) {
363
+ case "function":
364
+ if (primarySig) {
365
+ const sigData = simplifySignature(primarySig);
366
+ simplified.parameters = sigData?.parameters;
367
+ simplified.returns = sigData?.returns;
368
+ }
369
+ break;
370
+ case "class":
371
+ case "interface":
372
+ if (exp.members?.length) {
373
+ simplified.members = exp.members.map(simplifyMember);
374
+ }
375
+ break;
376
+ case "enum":
377
+ if (exp.members?.length) {
378
+ simplified.members = exp.members.map((m) => ({
379
+ name: m.name || "",
380
+ kind: "property",
381
+ description: m.description,
382
+ visibility: "public"
383
+ }));
384
+ }
385
+ break;
386
+ }
387
+ if (exp.examples?.length) {
388
+ simplified.examples = exp.examples.map(simplifyExample);
389
+ }
390
+ return simplified;
391
+ }
392
+ function toJSON(spec, options = {}) {
393
+ if (options.export) {
394
+ const exp = spec.exports.find((e) => e.name === options.export || e.id === options.export);
395
+ if (!exp) {
396
+ throw new Error(`Export not found: ${options.export}`);
397
+ }
398
+ return simplifyExport(exp);
399
+ }
400
+ const exports = spec.exports.map(simplifyExport);
401
+ const byKind = {};
402
+ for (const exp of exports) {
403
+ if (!byKind[exp.kind])
404
+ byKind[exp.kind] = [];
405
+ byKind[exp.kind].push(exp);
406
+ }
407
+ return {
408
+ name: spec.meta.name,
409
+ version: spec.meta.version,
410
+ description: spec.meta.description,
411
+ exports,
412
+ byKind,
413
+ totalExports: exports.length
414
+ };
415
+ }
416
+ function toJSONString(spec, options = {}) {
417
+ const data = toJSON(spec, options);
418
+ return options.pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
419
+ }
420
+
421
+ // src/render/markdown.ts
422
+ var defaultSections = {
423
+ signature: true,
424
+ description: true,
425
+ parameters: true,
426
+ returns: true,
427
+ examples: true,
428
+ members: true,
429
+ properties: true,
430
+ methods: true
431
+ };
432
+ function generateFrontmatter(exp, custom) {
433
+ const slug = exp.name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
434
+ const meta = {
435
+ title: exp.name,
436
+ description: exp.description?.slice(0, 160) || `API reference for ${exp.name}`,
437
+ slug,
438
+ ...custom
439
+ };
440
+ const lines = ["---"];
441
+ for (const [key, value] of Object.entries(meta)) {
442
+ if (typeof value === "string") {
443
+ lines.push(`${key}: "${value.replace(/"/g, "\\\"")}"`);
444
+ } else if (typeof value === "boolean" || typeof value === "number") {
445
+ lines.push(`${key}: ${value}`);
446
+ } else if (Array.isArray(value)) {
447
+ lines.push(`${key}:`);
448
+ for (const item of value) {
449
+ lines.push(` - ${item}`);
450
+ }
451
+ }
452
+ }
453
+ lines.push("---", "");
454
+ return lines.join(`
455
+ `);
456
+ }
457
+ function heading(level, text, offset = 0) {
458
+ const actualLevel = Math.min(level + offset, 6);
459
+ return `${"#".repeat(actualLevel)} ${text}`;
460
+ }
461
+ function renderParameters2(sig, offset = 0) {
462
+ if (!sig?.parameters?.length)
463
+ return "";
464
+ const lines = [heading(2, "Parameters", offset), ""];
465
+ for (const param of sig.parameters) {
466
+ const type = formatSchema(param.schema);
467
+ const required = param.required !== false ? "" : "?";
468
+ const rest = param.rest ? "..." : "";
469
+ lines.push(`### \`${rest}${param.name}${required}\``);
470
+ lines.push("");
471
+ lines.push(`**Type:** \`${type}\``);
472
+ if (param.description) {
473
+ lines.push("");
474
+ lines.push(param.description);
475
+ }
476
+ if (param.default !== undefined) {
477
+ lines.push("");
478
+ lines.push(`**Default:** \`${JSON.stringify(param.default)}\``);
479
+ }
480
+ lines.push("");
481
+ }
482
+ return lines.join(`
483
+ `);
484
+ }
485
+ function renderReturns2(sig, offset = 0) {
486
+ if (!sig?.returns)
487
+ return "";
488
+ const lines = [heading(2, "Returns", offset), ""];
489
+ const type = formatSchema(sig.returns.schema);
490
+ lines.push(`**Type:** \`${type}\``);
491
+ if (sig.returns.description) {
492
+ lines.push("");
493
+ lines.push(sig.returns.description);
494
+ }
495
+ lines.push("");
496
+ return lines.join(`
497
+ `);
498
+ }
499
+ function renderExamples2(examples, offset = 0) {
500
+ if (!examples?.length)
501
+ return "";
502
+ const lines = [heading(2, "Examples", offset), ""];
503
+ for (const example of examples) {
504
+ if (typeof example === "string") {
505
+ lines.push("```ts");
506
+ lines.push(example);
507
+ lines.push("```");
508
+ } else {
509
+ if (example.title) {
510
+ lines.push(`### ${example.title}`);
511
+ lines.push("");
512
+ }
513
+ if (example.description) {
514
+ lines.push(example.description);
515
+ lines.push("");
516
+ }
517
+ lines.push(`\`\`\`${example.language || "ts"}`);
518
+ lines.push(example.code);
519
+ lines.push("```");
520
+ }
521
+ lines.push("");
522
+ }
523
+ return lines.join(`
524
+ `);
525
+ }
526
+ function renderProperties2(members, offset = 0) {
527
+ const props = getProperties(members);
528
+ if (!props.length)
529
+ return "";
530
+ const lines = [heading(2, "Properties", offset), ""];
531
+ for (const prop of props) {
532
+ const type = formatSchema(prop.schema);
533
+ lines.push(`### \`${prop.name}\``);
534
+ lines.push("");
535
+ lines.push(`**Type:** \`${type}\``);
536
+ if (prop.description) {
537
+ lines.push("");
538
+ lines.push(prop.description);
539
+ }
540
+ lines.push("");
541
+ }
542
+ return lines.join(`
543
+ `);
544
+ }
545
+ function renderMethods2(members, offset = 0) {
546
+ const methods = getMethods(members);
547
+ if (!methods.length)
548
+ return "";
549
+ const lines = [heading(2, "Methods", offset), ""];
550
+ for (const method of methods) {
551
+ const sig = method.signatures?.[0];
552
+ const params = formatParameters(sig);
553
+ const returnType = formatReturnType(sig);
554
+ lines.push(`### \`${method.name}${params}: ${returnType}\``);
555
+ lines.push("");
556
+ if (method.description) {
557
+ lines.push(method.description);
558
+ lines.push("");
559
+ }
560
+ if (sig?.parameters?.length) {
561
+ lines.push("**Parameters:**");
562
+ lines.push("");
563
+ for (const param of sig.parameters) {
564
+ const paramType = formatSchema(param.schema);
565
+ const desc = param.description ? ` - ${param.description}` : "";
566
+ lines.push(`- \`${param.name}\`: \`${paramType}\`${desc}`);
567
+ }
568
+ lines.push("");
569
+ }
570
+ if (sig?.returns) {
571
+ const retType = formatSchema(sig.returns.schema);
572
+ const desc = sig.returns.description ? ` - ${sig.returns.description}` : "";
573
+ lines.push(`**Returns:** \`${retType}\`${desc}`);
574
+ lines.push("");
575
+ }
576
+ }
577
+ return lines.join(`
578
+ `);
579
+ }
580
+ function renderEnumMembers2(members, offset = 0) {
581
+ if (!members?.length)
582
+ return "";
583
+ const lines = [heading(2, "Members", offset), ""];
584
+ for (const member of members) {
585
+ lines.push(`### \`${member.name}\``);
586
+ if (member.description) {
587
+ lines.push("");
588
+ lines.push(member.description);
589
+ }
590
+ lines.push("");
591
+ }
592
+ return lines.join(`
593
+ `);
594
+ }
595
+ function exportToMarkdown(exp, options = {}) {
596
+ const sections = { ...defaultSections, ...options.sections };
597
+ const offset = options.headingOffset ?? 0;
598
+ const parts = [];
599
+ if (options.frontmatter !== false) {
600
+ parts.push(generateFrontmatter(exp, options.customFrontmatter));
601
+ }
602
+ parts.push(heading(1, exp.name, offset));
603
+ parts.push("");
604
+ if (sections.signature) {
605
+ const sig = buildSignatureString(exp);
606
+ if (options.codeSignatures) {
607
+ parts.push("```ts");
608
+ parts.push(sig);
609
+ parts.push("```");
610
+ } else {
611
+ parts.push(`\`${sig}\``);
612
+ }
613
+ parts.push("");
614
+ }
615
+ if (sections.description && exp.description) {
616
+ parts.push(exp.description);
617
+ parts.push("");
618
+ }
619
+ if (exp.deprecated) {
620
+ parts.push("> **Deprecated**");
621
+ parts.push("");
622
+ }
623
+ const primarySig = exp.signatures?.[0];
624
+ switch (exp.kind) {
625
+ case "function":
626
+ if (sections.parameters)
627
+ parts.push(renderParameters2(primarySig, offset));
628
+ if (sections.returns)
629
+ parts.push(renderReturns2(primarySig, offset));
630
+ break;
631
+ case "class":
632
+ case "interface":
633
+ if (sections.properties)
634
+ parts.push(renderProperties2(exp.members, offset));
635
+ if (sections.methods)
636
+ parts.push(renderMethods2(exp.members, offset));
637
+ break;
638
+ case "enum":
639
+ if (sections.members)
640
+ parts.push(renderEnumMembers2(exp.members, offset));
641
+ break;
642
+ case "type":
643
+ case "variable":
644
+ break;
645
+ }
646
+ if (sections.examples) {
647
+ parts.push(renderExamples2(exp.examples, offset));
648
+ }
649
+ return `${parts.filter(Boolean).join(`
650
+ `).trim()}
651
+ `;
652
+ }
653
+ function toMarkdown(spec, options = {}) {
654
+ if (options.export) {
655
+ const exp = spec.exports.find((e) => e.name === options.export || e.id === options.export);
656
+ if (!exp) {
657
+ throw new Error(`Export not found: ${options.export}`);
658
+ }
659
+ return exportToMarkdown(exp, options);
660
+ }
661
+ const parts = [];
662
+ if (options.frontmatter !== false) {
663
+ const frontmatter = {
664
+ title: `${spec.meta.name} API Reference`,
665
+ description: spec.meta.description || `API documentation for ${spec.meta.name}`,
666
+ ...options.customFrontmatter || {}
667
+ };
668
+ const lines = ["---"];
669
+ for (const [key, value] of Object.entries(frontmatter)) {
670
+ if (typeof value === "string") {
671
+ lines.push(`${key}: "${value.replace(/"/g, "\\\"")}"`);
672
+ } else {
673
+ lines.push(`${key}: ${JSON.stringify(value)}`);
674
+ }
675
+ }
676
+ lines.push("---", "");
677
+ parts.push(lines.join(`
678
+ `));
679
+ }
680
+ parts.push(`# ${spec.meta.name} API Reference`);
681
+ parts.push("");
682
+ if (spec.meta.description) {
683
+ parts.push(spec.meta.description);
684
+ parts.push("");
685
+ }
686
+ const byKind = {};
687
+ for (const exp of spec.exports) {
688
+ if (!byKind[exp.kind])
689
+ byKind[exp.kind] = [];
690
+ byKind[exp.kind].push(exp);
691
+ }
692
+ const kindOrder = ["function", "class", "interface", "type", "enum", "variable"];
693
+ for (const kind of kindOrder) {
694
+ const exports = byKind[kind];
695
+ if (!exports?.length)
696
+ continue;
697
+ parts.push(`## ${kind.charAt(0).toUpperCase() + kind.slice(1)}s`);
698
+ parts.push("");
699
+ for (const exp of exports) {
700
+ const content = exportToMarkdown(exp, {
701
+ ...options,
702
+ frontmatter: false,
703
+ headingOffset: 1
704
+ });
705
+ parts.push(content);
706
+ }
707
+ }
708
+ return `${parts.join(`
709
+ `).trim()}
710
+ `;
711
+ }
712
+
713
+ // src/render/nav.ts
714
+ var defaultKindLabels = {
715
+ function: "Functions",
716
+ class: "Classes",
717
+ interface: "Interfaces",
718
+ type: "Types",
719
+ enum: "Enums",
720
+ variable: "Variables",
721
+ namespace: "Namespaces",
722
+ module: "Modules",
723
+ reference: "References",
724
+ external: "External"
725
+ };
726
+ var defaultSlugify = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
727
+ function getModuleName(exp) {
728
+ if (exp.source?.file) {
729
+ const parts = exp.source.file.split("/");
730
+ const lastPart = parts[parts.length - 1];
731
+ if (lastPart === "index.ts" || lastPart === "index.tsx") {
732
+ return parts[parts.length - 2] || "core";
733
+ }
734
+ return lastPart.replace(/\.[jt]sx?$/, "");
735
+ }
736
+ return "core";
737
+ }
738
+ function getPrimaryTag(exp) {
739
+ const categoryTag = exp.tags?.find((t) => t.name === "category" || t.name === "@category");
740
+ if (categoryTag)
741
+ return categoryTag.text;
742
+ const moduleTag = exp.tags?.find((t) => t.name === "module" || t.name === "@module");
743
+ if (moduleTag)
744
+ return moduleTag.text;
745
+ return "Other";
746
+ }
747
+ function groupExports(exports, groupBy) {
748
+ const groups = new Map;
749
+ for (const exp of exports) {
750
+ let key;
751
+ switch (groupBy) {
752
+ case "kind":
753
+ key = exp.kind;
754
+ break;
755
+ case "module":
756
+ key = getModuleName(exp);
757
+ break;
758
+ case "tag":
759
+ key = getPrimaryTag(exp);
760
+ break;
761
+ default:
762
+ key = "all";
763
+ }
764
+ const existing = groups.get(key) ?? [];
765
+ existing.push(exp);
766
+ groups.set(key, existing);
767
+ }
768
+ return groups;
769
+ }
770
+ function toGenericNav(spec, options) {
771
+ const {
772
+ groupBy = "kind",
773
+ basePath = "/api",
774
+ slugify = defaultSlugify,
775
+ kindLabels = {},
776
+ sortAlphabetically = true,
777
+ includeGroupIndex = false
778
+ } = options;
779
+ const labels = { ...defaultKindLabels, ...kindLabels };
780
+ const grouped = groupExports(spec.exports, groupBy);
781
+ const groups = [];
782
+ for (const [key, exports] of grouped) {
783
+ const sortedExports = sortAlphabetically ? sortByName(exports) : exports;
784
+ const items = sortedExports.map((exp) => ({
785
+ title: exp.name,
786
+ href: `${basePath}/${slugify(exp.name)}`
787
+ }));
788
+ const title = groupBy === "kind" ? labels[key] || key : key;
789
+ groups.push({
790
+ title,
791
+ items,
792
+ index: includeGroupIndex ? `${basePath}/${slugify(key)}` : undefined
793
+ });
794
+ }
795
+ if (groupBy === "kind") {
796
+ const kindOrder = [
797
+ "function",
798
+ "class",
799
+ "interface",
800
+ "type",
801
+ "enum",
802
+ "variable",
803
+ "namespace",
804
+ "module",
805
+ "reference",
806
+ "external"
807
+ ];
808
+ groups.sort((a, b) => {
809
+ const aIdx = kindOrder.indexOf(a.title.toLowerCase().replace(/s$/, ""));
810
+ const bIdx = kindOrder.indexOf(b.title.toLowerCase().replace(/s$/, ""));
811
+ return aIdx - bIdx;
812
+ });
813
+ }
814
+ const flatItems = groupBy === "none" ? groups.flatMap((g) => g.items) : groups.map((g) => ({
815
+ title: g.title,
816
+ items: g.items
817
+ }));
818
+ return {
819
+ title: `${spec.meta.name} API`,
820
+ groups,
821
+ items: flatItems
822
+ };
823
+ }
824
+ function toFumadocsMeta(spec, options) {
825
+ const generic = toGenericNav(spec, options);
826
+ const { slugify = defaultSlugify } = options;
827
+ const pages = generic.groups.map((group) => ({
828
+ title: group.title,
829
+ pages: group.items.map((item) => slugify(item.title)),
830
+ defaultOpen: true
831
+ }));
832
+ return {
833
+ root: true,
834
+ title: generic.title,
835
+ pages
836
+ };
837
+ }
838
+ function toDocusaurusSidebar(spec, options) {
839
+ const generic = toGenericNav(spec, options);
840
+ const { slugify = defaultSlugify, basePath = "api" } = options;
841
+ const sidebar = generic.groups.map((group) => ({
842
+ type: "category",
843
+ label: group.title,
844
+ items: group.items.map((item) => ({
845
+ type: "doc",
846
+ id: `${basePath}/${slugify(item.title)}`,
847
+ label: item.title
848
+ }))
849
+ }));
850
+ return sidebar;
851
+ }
852
+ function toNavigation(spec, options = {}) {
853
+ const format = options.format ?? "generic";
854
+ switch (format) {
855
+ case "fumadocs":
856
+ return toFumadocsMeta(spec, options);
857
+ case "docusaurus":
858
+ return toDocusaurusSidebar(spec, options);
859
+ default:
860
+ return toGenericNav(spec, options);
861
+ }
862
+ }
863
+ function toFumadocsMetaJSON(spec, options = {}) {
864
+ const meta = toFumadocsMeta(spec, { ...options, format: "fumadocs" });
865
+ return JSON.stringify(meta, null, 2);
866
+ }
867
+ function toDocusaurusSidebarJS(spec, options = {}) {
868
+ const sidebar = toDocusaurusSidebar(spec, { ...options, format: "docusaurus" });
869
+ return `module.exports = ${JSON.stringify(sidebar, null, 2)};`;
870
+ }
871
+
872
+ // src/core/search.ts
873
+ var defaultSlugify2 = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-");
874
+ function extractKeywords(exp, options = {}) {
875
+ const keywords = new Set;
876
+ keywords.add(exp.name);
877
+ keywords.add(exp.name.toLowerCase());
878
+ const camelParts = exp.name.split(/(?=[A-Z])/);
879
+ for (const part of camelParts) {
880
+ if (part.length > 2)
881
+ keywords.add(part.toLowerCase());
882
+ }
883
+ if (exp.tags) {
884
+ for (const tag of exp.tags) {
885
+ keywords.add(tag.name.replace("@", ""));
886
+ const tagWords = tag.text.split(/\s+/);
887
+ for (const word of tagWords) {
888
+ if (word.length > 2)
889
+ keywords.add(word.toLowerCase());
890
+ }
891
+ }
892
+ }
893
+ if (exp.description) {
894
+ const descWords = exp.description.toLowerCase().split(/\W+/).filter((w) => w.length > 2);
895
+ for (const word of descWords) {
896
+ keywords.add(word);
897
+ }
898
+ }
899
+ if (options.includeMembers && exp.members) {
900
+ for (const member of exp.members) {
901
+ if (member.name) {
902
+ keywords.add(member.name.toLowerCase());
903
+ }
904
+ }
905
+ }
906
+ if (options.includeParameters && exp.signatures) {
907
+ for (const sig of exp.signatures) {
908
+ for (const param of sig.parameters || []) {
909
+ keywords.add(param.name.toLowerCase());
910
+ }
911
+ }
912
+ }
913
+ return Array.from(keywords);
914
+ }
915
+ function buildContent(exp, options = {}) {
916
+ const parts = [];
917
+ parts.push(exp.name);
918
+ if (exp.description) {
919
+ parts.push(exp.description);
920
+ }
921
+ if (options.includeSignatures !== false) {
922
+ parts.push(buildSignatureString(exp));
923
+ }
924
+ if (exp.tags) {
925
+ parts.push(...exp.tags.map((t) => `${t.name} ${t.text}`));
926
+ }
927
+ if (options.includeMembers !== false && exp.members) {
928
+ const props = getProperties(exp.members);
929
+ const methods = getMethods(exp.members);
930
+ for (const prop of props) {
931
+ if (prop.name) {
932
+ parts.push(prop.name);
933
+ if (prop.description)
934
+ parts.push(prop.description);
935
+ }
936
+ }
937
+ for (const method of methods) {
938
+ if (method.name) {
939
+ parts.push(method.name);
940
+ if (method.description)
941
+ parts.push(method.description);
942
+ }
943
+ }
944
+ }
945
+ if (options.includeParameters !== false && exp.signatures) {
946
+ for (const sig of exp.signatures) {
947
+ for (const param of sig.parameters || []) {
948
+ parts.push(param.name);
949
+ if (param.description)
950
+ parts.push(param.description);
951
+ }
952
+ }
953
+ }
954
+ return parts.join(" ");
955
+ }
956
+ function createSearchRecord(exp, packageName, options = {}) {
957
+ const { baseUrl = "/api", slugify = defaultSlugify2 } = options;
958
+ return {
959
+ id: exp.id,
960
+ name: exp.name,
961
+ kind: exp.kind,
962
+ signature: buildSignatureString(exp),
963
+ description: exp.description,
964
+ content: buildContent(exp, options),
965
+ keywords: extractKeywords(exp, options),
966
+ url: `${baseUrl}/${slugify(exp.name)}`,
967
+ deprecated: exp.deprecated === true
968
+ };
969
+ }
970
+ function toSearchIndex(spec, options = {}) {
971
+ const records = spec.exports.map((exp) => createSearchRecord(exp, spec.meta.name, options));
972
+ return {
973
+ records,
974
+ version: spec.meta.version || "0.0.0",
975
+ generatedAt: new Date().toISOString(),
976
+ packageName: spec.meta.name
977
+ };
978
+ }
979
+ function toPagefindRecords(spec, options = {}) {
980
+ const { baseUrl = "/api", slugify = defaultSlugify2, weights = {} } = options;
981
+ const { name: nameWeight = 10, description: descWeight = 5, signature: sigWeight = 3 } = weights;
982
+ return spec.exports.map((exp) => {
983
+ const content = buildContent(exp, options);
984
+ const signature = buildSignatureString(exp);
985
+ const filters = {
986
+ kind: [exp.kind]
987
+ };
988
+ if (exp.deprecated) {
989
+ filters.deprecated = ["true"];
990
+ }
991
+ if (exp.tags?.length) {
992
+ filters.tags = exp.tags.map((t) => t.name.replace("@", ""));
993
+ }
994
+ return {
995
+ url: `${baseUrl}/${slugify(exp.name)}`,
996
+ content,
997
+ word_count: content.split(/\s+/).length,
998
+ filters,
999
+ meta: {
1000
+ title: exp.name,
1001
+ kind: exp.kind,
1002
+ description: exp.description?.slice(0, 160),
1003
+ signature
1004
+ },
1005
+ weighted_sections: [
1006
+ { weight: nameWeight, text: exp.name },
1007
+ ...exp.description ? [{ weight: descWeight, text: exp.description }] : [],
1008
+ { weight: sigWeight, text: signature }
1009
+ ]
1010
+ };
1011
+ });
1012
+ }
1013
+ function toAlgoliaRecords(spec, options = {}) {
1014
+ const { baseUrl = "/api", slugify = defaultSlugify2 } = options;
1015
+ return spec.exports.map((exp) => ({
1016
+ objectID: exp.id,
1017
+ name: exp.name,
1018
+ kind: exp.kind,
1019
+ description: exp.description,
1020
+ signature: buildSignatureString(exp),
1021
+ content: buildContent(exp, options),
1022
+ tags: (exp.tags || []).map((t) => t.name.replace("@", "")),
1023
+ deprecated: exp.deprecated === true,
1024
+ url: `${baseUrl}/${slugify(exp.name)}`,
1025
+ hierarchy: {
1026
+ lvl0: spec.meta.name,
1027
+ lvl1: `${exp.kind.charAt(0).toUpperCase() + exp.kind.slice(1)}s`,
1028
+ lvl2: exp.name
1029
+ }
1030
+ }));
1031
+ }
1032
+ function toSearchIndexJSON(spec, options = {}) {
1033
+ const index = toSearchIndex(spec, options);
1034
+ return options.pretty ? JSON.stringify(index, null, 2) : JSON.stringify(index);
1035
+ }
1036
+
1037
+ // src/core/loader.ts
1038
+ import * as fs from "node:fs";
1039
+ function loadSpec(spec) {
1040
+ return createDocsInstance(spec);
1041
+ }
1042
+ function createDocs(input) {
1043
+ const spec = typeof input === "string" ? JSON.parse(fs.readFileSync(input, "utf-8")) : input;
1044
+ return createDocsInstance(spec);
1045
+ }
1046
+ function createDocsInstance(spec) {
1047
+ const exportsById = new Map;
1048
+ const typesById = new Map;
1049
+ const exportsByTag = new Map;
1050
+ const exportsByModule = new Map;
1051
+ for (const exp of spec.exports) {
1052
+ exportsById.set(exp.id, exp);
1053
+ if (exp.tags) {
1054
+ for (const tag of exp.tags) {
1055
+ const tagKey = tag.name.startsWith("@") ? tag.name : `@${tag.name}`;
1056
+ const existing = exportsByTag.get(tagKey) ?? [];
1057
+ existing.push(exp);
1058
+ exportsByTag.set(tagKey, existing);
1059
+ }
1060
+ }
1061
+ const moduleName = extractModuleName(exp);
1062
+ if (moduleName) {
1063
+ const existing = exportsByModule.get(moduleName) ?? [];
1064
+ existing.push(exp);
1065
+ exportsByModule.set(moduleName, existing);
1066
+ }
1067
+ }
1068
+ if (spec.types) {
1069
+ for (const type of spec.types) {
1070
+ typesById.set(type.id, type);
1071
+ }
1072
+ }
1073
+ return {
1074
+ spec,
1075
+ getExport(id) {
1076
+ return exportsById.get(id);
1077
+ },
1078
+ getType(id) {
1079
+ return typesById.get(id);
1080
+ },
1081
+ getExportsByKind(kind) {
1082
+ return spec.exports.filter((exp) => exp.kind === kind);
1083
+ },
1084
+ getAllExports() {
1085
+ return spec.exports;
1086
+ },
1087
+ getAllTypes() {
1088
+ return spec.types ?? [];
1089
+ },
1090
+ getExportsByTag(tagName) {
1091
+ const normalizedTag = tagName.startsWith("@") ? tagName : `@${tagName}`;
1092
+ return exportsByTag.get(normalizedTag) ?? [];
1093
+ },
1094
+ search(query) {
1095
+ const lowerQuery = query.toLowerCase();
1096
+ return spec.exports.filter((exp) => {
1097
+ if (exp.name.toLowerCase().includes(lowerQuery))
1098
+ return true;
1099
+ if (exp.description?.toLowerCase().includes(lowerQuery))
1100
+ return true;
1101
+ if (exp.tags?.some((t) => t.text.toLowerCase().includes(lowerQuery)))
1102
+ return true;
1103
+ return false;
1104
+ });
1105
+ },
1106
+ getModule(moduleName) {
1107
+ return exportsByModule.get(moduleName) ?? [];
1108
+ },
1109
+ getDeprecated() {
1110
+ return spec.exports.filter((exp) => exp.deprecated === true);
1111
+ },
1112
+ groupByKind() {
1113
+ const groups = {};
1114
+ for (const exp of spec.exports) {
1115
+ if (!groups[exp.kind]) {
1116
+ groups[exp.kind] = [];
1117
+ }
1118
+ groups[exp.kind].push(exp);
1119
+ }
1120
+ return groups;
1121
+ },
1122
+ toMarkdown(options) {
1123
+ return toMarkdown(spec, options);
1124
+ },
1125
+ toHTML(options) {
1126
+ return toHTML(spec, options);
1127
+ },
1128
+ toJSON(options) {
1129
+ return toJSON(spec, options);
1130
+ },
1131
+ toNavigation(options) {
1132
+ return toNavigation(spec, options);
1133
+ },
1134
+ toSearchIndex(options) {
1135
+ return toSearchIndex(spec, options);
1136
+ },
1137
+ toPagefindRecords(options) {
1138
+ return toPagefindRecords(spec, options);
1139
+ },
1140
+ toAlgoliaRecords(options) {
1141
+ return toAlgoliaRecords(spec, options);
1142
+ }
1143
+ };
1144
+ }
1145
+ function extractModuleName(exp) {
1146
+ if (exp.source?.file) {
1147
+ const parts = exp.source.file.split("/");
1148
+ const lastPart = parts[parts.length - 1];
1149
+ if (lastPart === "index.ts" || lastPart === "index.tsx") {
1150
+ return parts[parts.length - 2] || "root";
1151
+ }
1152
+ return lastPart.replace(/\.[jt]sx?$/, "");
1153
+ }
1154
+ if (exp.kind === "namespace" || exp.kind === "module") {
1155
+ return exp.name;
1156
+ }
1157
+ return;
1158
+ }
1159
+
1160
+ export { toHTML, toJSON, toJSONString, exportToMarkdown, toMarkdown, toNavigation, toFumadocsMetaJSON, toDocusaurusSidebarJS, toSearchIndex, toPagefindRecords, toAlgoliaRecords, toSearchIndexJSON, loadSpec, createDocs };