@fgv/typedoc-compact-theme 1.0.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.
package/lib/index.js ADDED
@@ -0,0 +1,1083 @@
1
+ /**
2
+ * TypeDoc Compact Theme
3
+ *
4
+ * Renders API documentation in a compact HTML table format similar to api-documenter.
5
+ * Uses TypeDoc's project model directly, bypassing the theme partial system for
6
+ * complete control over output format.
7
+ *
8
+ * Properties table: Property | Modifiers | Type | Description (includes accessors)
9
+ * Methods table: Method | Modifiers | Description
10
+ */
11
+ import * as fs from 'fs';
12
+ import * as path from 'path';
13
+ import { Application, Converter, DeclarationReflection, ParameterType, ProjectReflection, ReflectionKind } from 'typedoc';
14
+ /**
15
+ * Renders a class or interface to markdown.
16
+ */
17
+ class CompactMarkdownRenderer {
18
+ _outputDir;
19
+ _options;
20
+ _urlMap = new Map();
21
+ _currentFilePath = '';
22
+ constructor(outputDir, options) {
23
+ this._outputDir = outputDir;
24
+ this._options = options;
25
+ }
26
+ /**
27
+ * Render the entire project.
28
+ */
29
+ renderProject(project) {
30
+ // Clear and recreate output directory to remove stale files
31
+ if (fs.existsSync(this._outputDir)) {
32
+ fs.rmSync(this._outputDir, { recursive: true });
33
+ }
34
+ fs.mkdirSync(this._outputDir, { recursive: true });
35
+ // First pass: build URL map for all types
36
+ this._buildUrlMap(project, '');
37
+ // Render index
38
+ this._renderIndex(project);
39
+ // Recursively render all declarations
40
+ this._renderChildren(project, '');
41
+ }
42
+ /**
43
+ * Build URL map for all documented types.
44
+ */
45
+ _buildUrlMap(parent, basePath) {
46
+ if (!(parent instanceof DeclarationReflection) && !(parent instanceof ProjectReflection)) {
47
+ return;
48
+ }
49
+ const children = parent.children ?? [];
50
+ for (const child of children) {
51
+ const name = child.name;
52
+ let subdir = '';
53
+ let shouldIndex = true;
54
+ if (child.kind === ReflectionKind.Class) {
55
+ subdir = 'classes';
56
+ }
57
+ else if (child.kind === ReflectionKind.Interface) {
58
+ subdir = 'interfaces';
59
+ }
60
+ else if (child.kind === ReflectionKind.Enum) {
61
+ subdir = 'enums';
62
+ }
63
+ else if (child.kind === ReflectionKind.TypeAlias) {
64
+ subdir = 'type-aliases';
65
+ }
66
+ else if (child.kind === ReflectionKind.Function) {
67
+ subdir = 'functions';
68
+ }
69
+ else if (child.kind === ReflectionKind.Variable) {
70
+ subdir = 'variables';
71
+ }
72
+ else if (child.kind === ReflectionKind.Namespace || child.kind === ReflectionKind.Module) {
73
+ // Recurse into namespaces
74
+ const nsPath = path.join(basePath, this._sanitizeName(child.name));
75
+ this._buildUrlMap(child, nsPath);
76
+ shouldIndex = false;
77
+ }
78
+ else {
79
+ shouldIndex = false;
80
+ }
81
+ if (shouldIndex && subdir) {
82
+ const url = path.join(basePath, subdir, `${this._sanitizeName(name)}.md`);
83
+ this._urlMap.set(name, { url, kind: child.kind });
84
+ // Also index with full path for namespaced types
85
+ if (basePath) {
86
+ const fullName = basePath.replace(/\//g, '.') + '.' + name;
87
+ this._urlMap.set(fullName, { url, kind: child.kind });
88
+ }
89
+ }
90
+ // Recurse into children for nested types
91
+ this._buildUrlMap(child, basePath);
92
+ }
93
+ }
94
+ _renderChildren(parent, basePath) {
95
+ if (!(parent instanceof DeclarationReflection) && !(parent instanceof ProjectReflection)) {
96
+ return;
97
+ }
98
+ const children = parent.children ?? [];
99
+ for (const child of children) {
100
+ if (child.kind === ReflectionKind.Class || child.kind === ReflectionKind.Interface) {
101
+ this._renderClassOrInterface(child, basePath);
102
+ }
103
+ else if (child.kind === ReflectionKind.Namespace || child.kind === ReflectionKind.Module) {
104
+ // Create subdirectory for namespace and render its index
105
+ const nsPath = path.join(basePath, this._sanitizeName(child.name));
106
+ this._renderNamespaceIndex(child, nsPath);
107
+ this._renderChildren(child, nsPath);
108
+ }
109
+ else if (child.kind === ReflectionKind.Enum) {
110
+ this._renderEnum(child, basePath);
111
+ }
112
+ else if (child.kind === ReflectionKind.TypeAlias) {
113
+ this._renderTypeAlias(child, basePath);
114
+ }
115
+ else if (child.kind === ReflectionKind.Function) {
116
+ this._renderFunction(child, basePath);
117
+ }
118
+ else if (child.kind === ReflectionKind.Variable) {
119
+ this._renderVariable(child, basePath);
120
+ }
121
+ // Recurse into children
122
+ this._renderChildren(child, basePath);
123
+ }
124
+ }
125
+ _renderIndex(project) {
126
+ this._setCurrentFile('README.md');
127
+ const lines = [];
128
+ lines.push(`# ${project.name}`);
129
+ lines.push('');
130
+ if (project.comment?.summary) {
131
+ lines.push(this._getCommentText(project.comment.summary));
132
+ lines.push('');
133
+ }
134
+ // Find namespaces/modules first
135
+ const namespaces = this._findByKind(project, ReflectionKind.Namespace);
136
+ const modules = this._findByKind(project, ReflectionKind.Module);
137
+ const allNamespaces = [...namespaces, ...modules];
138
+ if (allNamespaces.length > 0) {
139
+ lines.push('## Namespaces');
140
+ lines.push('');
141
+ lines.push(this._renderIndexTable(allNamespaces, (n) => `./${this._sanitizeName(n.name)}/README.md`));
142
+ lines.push('');
143
+ }
144
+ // List all top-level exports
145
+ const classes = this._findByKind(project, ReflectionKind.Class);
146
+ const interfaces = this._findByKind(project, ReflectionKind.Interface);
147
+ const enums = this._findByKind(project, ReflectionKind.Enum);
148
+ const typeAliases = this._findByKind(project, ReflectionKind.TypeAlias);
149
+ const functions = this._findByKind(project, ReflectionKind.Function);
150
+ const variables = this._findByKind(project, ReflectionKind.Variable);
151
+ if (classes.length > 0) {
152
+ lines.push('## Classes');
153
+ lines.push('');
154
+ lines.push(this._renderIndexTable(classes, (c) => `./classes/${this._sanitizeName(c.name)}.md`));
155
+ lines.push('');
156
+ }
157
+ if (interfaces.length > 0) {
158
+ lines.push('## Interfaces');
159
+ lines.push('');
160
+ lines.push(this._renderIndexTable(interfaces, (i) => `./interfaces/${this._sanitizeName(i.name)}.md`));
161
+ lines.push('');
162
+ }
163
+ if (enums.length > 0) {
164
+ lines.push('## Enums');
165
+ lines.push('');
166
+ lines.push(this._renderIndexTable(enums, (e) => `./enums/${this._sanitizeName(e.name)}.md`));
167
+ lines.push('');
168
+ }
169
+ if (typeAliases.length > 0) {
170
+ lines.push('## Type Aliases');
171
+ lines.push('');
172
+ lines.push(this._renderIndexTable(typeAliases, (t) => `./type-aliases/${this._sanitizeName(t.name)}.md`));
173
+ lines.push('');
174
+ }
175
+ if (functions.length > 0) {
176
+ lines.push('## Functions');
177
+ lines.push('');
178
+ lines.push(this._renderIndexTable(functions, (f) => `./functions/${this._sanitizeName(f.name)}.md`));
179
+ lines.push('');
180
+ }
181
+ if (variables.length > 0) {
182
+ lines.push('## Variables');
183
+ lines.push('');
184
+ lines.push(this._renderIndexTable(variables, (v) => `./variables/${this._sanitizeName(v.name)}.md`));
185
+ lines.push('');
186
+ }
187
+ const indexPath = path.join(this._outputDir, 'README.md');
188
+ fs.writeFileSync(indexPath, lines.join('\n'));
189
+ }
190
+ /**
191
+ * Render an index table for a list of declarations.
192
+ */
193
+ _renderIndexTable(items, urlFn) {
194
+ const lines = [];
195
+ lines.push('<table><thead><tr><th>');
196
+ lines.push('');
197
+ lines.push('Name');
198
+ lines.push('');
199
+ lines.push('</th><th>');
200
+ lines.push('');
201
+ lines.push('Description');
202
+ lines.push('');
203
+ lines.push('</th></tr></thead>');
204
+ lines.push('<tbody>');
205
+ for (const item of items) {
206
+ const url = urlFn(item);
207
+ const desc = this._getItemDescription(item);
208
+ lines.push('<tr><td>');
209
+ lines.push('');
210
+ lines.push(`[${item.name}](${url})`);
211
+ lines.push('');
212
+ lines.push('</td><td>');
213
+ lines.push('');
214
+ lines.push(desc);
215
+ lines.push('');
216
+ lines.push('</td></tr>');
217
+ }
218
+ lines.push('</tbody></table>');
219
+ return lines.join('\n');
220
+ }
221
+ /**
222
+ * Get description for an item (first line of comment or signature comment).
223
+ */
224
+ _getItemDescription(item) {
225
+ // Try item's own comment
226
+ if (item.comment?.summary) {
227
+ return this._getFirstLine(this._getCommentText(item.comment.summary));
228
+ }
229
+ // For functions, try signature comment
230
+ const sig = item.signatures?.[0];
231
+ if (sig?.comment?.summary) {
232
+ return this._getFirstLine(this._getCommentText(sig.comment.summary));
233
+ }
234
+ // For accessors, try getter/setter comment
235
+ if (item.getSignature?.comment?.summary) {
236
+ return this._getFirstLine(this._getCommentText(item.getSignature.comment.summary));
237
+ }
238
+ return '';
239
+ }
240
+ /**
241
+ * Get the first line/sentence of a description.
242
+ */
243
+ _getFirstLine(text) {
244
+ // Split on period followed by space or newline
245
+ const firstSentence = text.split(/\.\s|\.\n/)[0];
246
+ if (firstSentence && firstSentence !== text) {
247
+ return firstSentence + '.';
248
+ }
249
+ // If no period, take first line
250
+ const firstLine = text.split('\n')[0];
251
+ return firstLine.trim();
252
+ }
253
+ /**
254
+ * Render an index page for a namespace/module.
255
+ */
256
+ _renderNamespaceIndex(ns, nsPath) {
257
+ const filePath = path.join(nsPath, 'README.md');
258
+ this._setCurrentFile(filePath);
259
+ const lines = [];
260
+ const kindName = ns.kind === ReflectionKind.Namespace ? 'Namespace' : 'Module';
261
+ // Breadcrumb - nsPath includes this namespace, file is at nsPath/README.md
262
+ // We need to go up one level more than pathParts length to reach root
263
+ const pathParts = nsPath.split('/').filter((p) => p);
264
+ const depth = pathParts.length;
265
+ const homePrefix = '../'.repeat(depth);
266
+ const breadcrumbParts = [`[Home](${homePrefix}README.md)`];
267
+ // Add parent namespaces (all but the last one)
268
+ for (let i = 0; i < pathParts.length - 1; i++) {
269
+ const levelsUp = depth - (i + 1);
270
+ const nsPrefix = '../'.repeat(levelsUp);
271
+ breadcrumbParts.push(`[${pathParts[i]}](${nsPrefix}README.md)`);
272
+ }
273
+ // Current namespace (not linked)
274
+ breadcrumbParts.push(ns.name);
275
+ lines.push(breadcrumbParts.join(' > '));
276
+ lines.push('');
277
+ lines.push(`# ${kindName}: ${ns.name}`);
278
+ lines.push('');
279
+ if (ns.comment?.summary) {
280
+ lines.push(this._getCommentText(ns.comment.summary));
281
+ lines.push('');
282
+ }
283
+ // Collect children by kind
284
+ const children = ns.children ?? [];
285
+ const childNamespaces = children.filter((c) => c.kind === ReflectionKind.Namespace || c.kind === ReflectionKind.Module);
286
+ const classes = children.filter((c) => c.kind === ReflectionKind.Class);
287
+ const interfaces = children.filter((c) => c.kind === ReflectionKind.Interface);
288
+ const enums = children.filter((c) => c.kind === ReflectionKind.Enum);
289
+ const typeAliases = children.filter((c) => c.kind === ReflectionKind.TypeAlias);
290
+ const functions = children.filter((c) => c.kind === ReflectionKind.Function);
291
+ const variables = children.filter((c) => c.kind === ReflectionKind.Variable);
292
+ if (childNamespaces.length > 0) {
293
+ lines.push('## Namespaces');
294
+ lines.push('');
295
+ lines.push(this._renderIndexTable(childNamespaces, (n) => `./${this._sanitizeName(n.name)}/README.md`));
296
+ lines.push('');
297
+ }
298
+ if (classes.length > 0) {
299
+ lines.push('## Classes');
300
+ lines.push('');
301
+ lines.push(this._renderIndexTable(classes, (c) => `./classes/${this._sanitizeName(c.name)}.md`));
302
+ lines.push('');
303
+ }
304
+ if (interfaces.length > 0) {
305
+ lines.push('## Interfaces');
306
+ lines.push('');
307
+ lines.push(this._renderIndexTable(interfaces, (i) => `./interfaces/${this._sanitizeName(i.name)}.md`));
308
+ lines.push('');
309
+ }
310
+ if (enums.length > 0) {
311
+ lines.push('## Enums');
312
+ lines.push('');
313
+ lines.push(this._renderIndexTable(enums, (e) => `./enums/${this._sanitizeName(e.name)}.md`));
314
+ lines.push('');
315
+ }
316
+ if (typeAliases.length > 0) {
317
+ lines.push('## Type Aliases');
318
+ lines.push('');
319
+ lines.push(this._renderIndexTable(typeAliases, (t) => `./type-aliases/${this._sanitizeName(t.name)}.md`));
320
+ lines.push('');
321
+ }
322
+ if (functions.length > 0) {
323
+ lines.push('## Functions');
324
+ lines.push('');
325
+ lines.push(this._renderIndexTable(functions, (f) => `./functions/${this._sanitizeName(f.name)}.md`));
326
+ lines.push('');
327
+ }
328
+ if (variables.length > 0) {
329
+ lines.push('## Variables');
330
+ lines.push('');
331
+ lines.push(this._renderIndexTable(variables, (v) => `./variables/${this._sanitizeName(v.name)}.md`));
332
+ lines.push('');
333
+ }
334
+ // Ensure directory exists
335
+ const outDir = path.join(this._outputDir, nsPath);
336
+ if (!fs.existsSync(outDir)) {
337
+ fs.mkdirSync(outDir, { recursive: true });
338
+ }
339
+ const outPath = path.join(this._outputDir, filePath);
340
+ fs.writeFileSync(outPath, lines.join('\n'));
341
+ }
342
+ _renderClassOrInterface(decl, basePath) {
343
+ const lines = [];
344
+ const kindName = decl.kind === ReflectionKind.Class ? 'Class' : 'Interface';
345
+ // Set current file for relative URL computation
346
+ const subdir = decl.kind === ReflectionKind.Class ? 'classes' : 'interfaces';
347
+ const filePath = path.join(basePath, subdir, `${this._sanitizeName(decl.name)}.md`);
348
+ this._setCurrentFile(filePath);
349
+ // Breadcrumb
350
+ lines.push(this._generateBreadcrumb(basePath, decl.name, subdir));
351
+ lines.push('');
352
+ // Header
353
+ lines.push(`# ${kindName}: ${decl.name}`);
354
+ lines.push('');
355
+ // Description
356
+ if (decl.comment?.summary) {
357
+ lines.push(this._getCommentText(decl.comment.summary));
358
+ lines.push('');
359
+ }
360
+ // Extends and Implements on single lines with links
361
+ if (decl.extendedTypes && decl.extendedTypes.length > 0) {
362
+ const extList = decl.extendedTypes.map((t) => this._linkTypeInline(t.toString())).join(', ');
363
+ lines.push(`**Extends:** ${extList}`);
364
+ lines.push('');
365
+ }
366
+ if (decl.implementedTypes && decl.implementedTypes.length > 0) {
367
+ const implList = decl.implementedTypes.map((t) => this._linkTypeInline(t.toString())).join(', ');
368
+ lines.push(`**Implements:** ${implList}`);
369
+ lines.push('');
370
+ }
371
+ // Collect members by category
372
+ const children = decl.children ?? [];
373
+ const constructors = children.filter((c) => c.kind === ReflectionKind.Constructor);
374
+ const properties = children.filter((c) => c.kind === ReflectionKind.Property);
375
+ const accessors = children.filter((c) => c.kind === ReflectionKind.Accessor);
376
+ const methods = children.filter((c) => c.kind === ReflectionKind.Method);
377
+ // Member detail pages base path
378
+ const memberBasePath = path.join(basePath, subdir);
379
+ // Render constructors
380
+ if (constructors.length > 0) {
381
+ lines.push('## Constructors');
382
+ lines.push('');
383
+ lines.push(this._renderConstructorsTable(constructors, decl.name, memberBasePath));
384
+ lines.push('');
385
+ }
386
+ // Render properties + accessors combined
387
+ const allProperties = [...properties, ...accessors];
388
+ if (allProperties.length > 0) {
389
+ lines.push('## Properties');
390
+ lines.push('');
391
+ lines.push(this._renderPropertiesTable(allProperties, decl.name, memberBasePath));
392
+ lines.push('');
393
+ // Render property detail pages (optionally skip inherited members)
394
+ for (const prop of allProperties) {
395
+ if (this._options.includeInheritedMemberPages || !prop.inheritedFrom) {
396
+ this._renderPropertyDetailPage(prop, decl, memberBasePath);
397
+ }
398
+ }
399
+ }
400
+ // Render methods
401
+ if (methods.length > 0) {
402
+ lines.push('## Methods');
403
+ lines.push('');
404
+ lines.push(this._renderMethodsTable(methods, decl.name, memberBasePath));
405
+ lines.push('');
406
+ // Render method detail pages (optionally skip inherited members)
407
+ for (const method of methods) {
408
+ if (this._options.includeInheritedMemberPages || !method.inheritedFrom) {
409
+ this._renderMethodDetailPage(method, decl, memberBasePath);
410
+ }
411
+ }
412
+ }
413
+ // Write file
414
+ const outDir = path.join(this._outputDir, basePath, subdir);
415
+ if (!fs.existsSync(outDir)) {
416
+ fs.mkdirSync(outDir, { recursive: true });
417
+ }
418
+ const outPath = path.join(outDir, `${this._sanitizeName(decl.name)}.md`);
419
+ fs.writeFileSync(outPath, lines.join('\n'));
420
+ }
421
+ /**
422
+ * Render a detail page for a property or accessor.
423
+ */
424
+ _renderPropertyDetailPage(prop, parent, basePath) {
425
+ const fileName = `${this._sanitizeName(parent.name)}.${this._sanitizeName(prop.name)}.md`;
426
+ const filePath = path.join(basePath, fileName);
427
+ this._setCurrentFile(filePath);
428
+ const lines = [];
429
+ const kindLabel = prop.kind === ReflectionKind.Accessor ? 'property' : 'property';
430
+ // Breadcrumb: extract namespace path from basePath (which is like "Namespace/classes")
431
+ const pathParts = basePath.split('/').filter((p) => p);
432
+ const subdir = pathParts.pop(); // Remove "classes" or "interfaces"
433
+ const namespacePath = pathParts.join('/');
434
+ // Generate breadcrumb with linked parent class
435
+ const breadcrumbParts = [];
436
+ const depth = pathParts.length + 1; // +1 for subdir
437
+ breadcrumbParts.push(`[Home](${'../'.repeat(depth)}README.md)`);
438
+ // Add namespace parts
439
+ for (let i = 0; i < pathParts.length; i++) {
440
+ const levelsUp = depth - (i + 1);
441
+ breadcrumbParts.push(`[${pathParts[i]}](${'../'.repeat(levelsUp)}README.md)`);
442
+ }
443
+ // Add linked parent class
444
+ breadcrumbParts.push(`[${parent.name}](./${this._sanitizeName(parent.name)}.md)`);
445
+ // Add current property (unlinked)
446
+ breadcrumbParts.push(prop.name);
447
+ lines.push(breadcrumbParts.join(' > '));
448
+ lines.push('');
449
+ // Header
450
+ lines.push(`## ${parent.name}.${prop.name} ${kindLabel}`);
451
+ lines.push('');
452
+ // Description
453
+ let description = '';
454
+ if (prop.kind === ReflectionKind.Accessor) {
455
+ const getter = prop.getSignature;
456
+ const setter = prop.setSignature;
457
+ if (getter?.comment?.summary) {
458
+ description = this._getCommentText(getter.comment.summary);
459
+ }
460
+ else if (setter?.comment?.summary) {
461
+ description = this._getCommentText(setter.comment.summary);
462
+ }
463
+ }
464
+ else if (prop.comment?.summary) {
465
+ description = this._getCommentText(prop.comment.summary);
466
+ }
467
+ if (description) {
468
+ lines.push(description);
469
+ lines.push('');
470
+ }
471
+ // Signature
472
+ lines.push('**Signature:**');
473
+ lines.push('');
474
+ lines.push('```typescript');
475
+ lines.push(this._getPropertySignature(prop));
476
+ lines.push('```');
477
+ lines.push('');
478
+ // Write file
479
+ const outDir = path.join(this._outputDir, basePath);
480
+ if (!fs.existsSync(outDir)) {
481
+ fs.mkdirSync(outDir, { recursive: true });
482
+ }
483
+ const outPath = path.join(this._outputDir, filePath);
484
+ fs.writeFileSync(outPath, lines.join('\n'));
485
+ }
486
+ /**
487
+ * Render a detail page for a method.
488
+ */
489
+ _renderMethodDetailPage(method, parent, basePath) {
490
+ const fileName = `${this._sanitizeName(parent.name)}.${this._sanitizeName(method.name)}.md`;
491
+ const filePath = path.join(basePath, fileName);
492
+ this._setCurrentFile(filePath);
493
+ const lines = [];
494
+ // Breadcrumb: extract namespace path from basePath (which is like "Namespace/classes")
495
+ const pathParts = basePath.split('/').filter((p) => p);
496
+ pathParts.pop(); // Remove "classes" or "interfaces"
497
+ // Generate breadcrumb with linked parent class
498
+ const breadcrumbParts = [];
499
+ const depth = pathParts.length + 1; // +1 for subdir
500
+ breadcrumbParts.push(`[Home](${'../'.repeat(depth)}README.md)`);
501
+ // Add namespace parts
502
+ for (let i = 0; i < pathParts.length; i++) {
503
+ const levelsUp = depth - (i + 1);
504
+ breadcrumbParts.push(`[${pathParts[i]}](${'../'.repeat(levelsUp)}README.md)`);
505
+ }
506
+ // Add linked parent class
507
+ breadcrumbParts.push(`[${parent.name}](./${this._sanitizeName(parent.name)}.md)`);
508
+ // Add current method (unlinked)
509
+ breadcrumbParts.push(method.name);
510
+ lines.push(breadcrumbParts.join(' > '));
511
+ lines.push('');
512
+ // Header
513
+ lines.push(`## ${parent.name}.${method.name}() method`);
514
+ lines.push('');
515
+ // Description from signature
516
+ const sig = method.signatures?.[0];
517
+ if (sig?.comment?.summary) {
518
+ lines.push(this._getCommentText(sig.comment.summary));
519
+ lines.push('');
520
+ }
521
+ // Signature
522
+ lines.push('**Signature:**');
523
+ lines.push('');
524
+ lines.push('```typescript');
525
+ lines.push(this._getMethodSignature(method));
526
+ lines.push('```');
527
+ lines.push('');
528
+ // Parameters
529
+ if (sig?.parameters && sig.parameters.length > 0) {
530
+ lines.push('**Parameters:**');
531
+ lines.push('');
532
+ lines.push('<table><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead>');
533
+ lines.push('<tbody>');
534
+ for (const param of sig.parameters) {
535
+ const paramType = param.type?.toString() ?? 'unknown';
536
+ const paramDesc = param.comment?.summary ? this._getCommentText(param.comment.summary) : '';
537
+ lines.push(`<tr><td>${param.name}</td><td>${this._escapeHtml(paramType)}</td><td>${paramDesc}</td></tr>`);
538
+ }
539
+ lines.push('</tbody></table>');
540
+ lines.push('');
541
+ }
542
+ // Returns
543
+ if (sig?.type) {
544
+ lines.push('**Returns:**');
545
+ lines.push('');
546
+ lines.push(`${this._linkType(sig.type.toString())}`);
547
+ if (sig.comment?.blockTags) {
548
+ const returnsTag = sig.comment.blockTags.find((t) => t.tag === '@returns');
549
+ if (returnsTag?.content) {
550
+ lines.push('');
551
+ lines.push(this._getCommentText(returnsTag.content));
552
+ }
553
+ }
554
+ lines.push('');
555
+ }
556
+ // Write file
557
+ const outDir = path.join(this._outputDir, basePath);
558
+ if (!fs.existsSync(outDir)) {
559
+ fs.mkdirSync(outDir, { recursive: true });
560
+ }
561
+ const outPath = path.join(this._outputDir, filePath);
562
+ fs.writeFileSync(outPath, lines.join('\n'));
563
+ }
564
+ /**
565
+ * Get signature string for a property.
566
+ */
567
+ _getPropertySignature(prop) {
568
+ const modifiers = [];
569
+ if (prop.flags?.isReadonly)
570
+ modifiers.push('readonly');
571
+ if (prop.flags?.isStatic)
572
+ modifiers.push('static');
573
+ let typeStr = '';
574
+ if (prop.kind === ReflectionKind.Accessor) {
575
+ const getter = prop.getSignature;
576
+ const setter = prop.setSignature;
577
+ if (getter?.type) {
578
+ typeStr = getter.type.toString();
579
+ }
580
+ else if (setter?.parameters?.[0]?.type) {
581
+ typeStr = setter.parameters[0].type.toString();
582
+ }
583
+ if (getter && !setter)
584
+ modifiers.push('readonly');
585
+ }
586
+ else {
587
+ typeStr = prop.type?.toString() ?? 'unknown';
588
+ }
589
+ const modStr = modifiers.length > 0 ? modifiers.join(' ') + ' ' : '';
590
+ return `${modStr}${prop.name}: ${typeStr};`;
591
+ }
592
+ /**
593
+ * Get signature string for a method.
594
+ */
595
+ _getMethodSignature(method) {
596
+ const sig = method.signatures?.[0];
597
+ if (!sig)
598
+ return `${method.name}(): unknown;`;
599
+ const modifiers = [];
600
+ if (method.flags?.isStatic)
601
+ modifiers.push('static');
602
+ const params = sig.parameters
603
+ ?.map((p) => {
604
+ const optional = p.flags?.isOptional ? '?' : '';
605
+ return `${p.name}${optional}: ${p.type?.toString() ?? 'unknown'}`;
606
+ })
607
+ .join(', ') ?? '';
608
+ const returnType = sig.type?.toString() ?? 'void';
609
+ const modStr = modifiers.length > 0 ? modifiers.join(' ') + ' ' : '';
610
+ return `${modStr}${method.name}(${params}): ${returnType};`;
611
+ }
612
+ _renderEnum(decl, basePath) {
613
+ const lines = [];
614
+ // Breadcrumb
615
+ lines.push(this._generateBreadcrumb(basePath, decl.name, 'enums'));
616
+ lines.push('');
617
+ lines.push(`# Enum: ${decl.name}`);
618
+ lines.push('');
619
+ if (decl.comment?.summary) {
620
+ lines.push(this._getCommentText(decl.comment.summary));
621
+ lines.push('');
622
+ }
623
+ const members = decl.children ?? [];
624
+ if (members.length > 0) {
625
+ lines.push('## Members');
626
+ lines.push('');
627
+ lines.push('<table><thead><tr><th>');
628
+ lines.push('');
629
+ lines.push('Member');
630
+ lines.push('');
631
+ lines.push('</th><th>');
632
+ lines.push('');
633
+ lines.push('Value');
634
+ lines.push('');
635
+ lines.push('</th><th>');
636
+ lines.push('');
637
+ lines.push('Description');
638
+ lines.push('');
639
+ lines.push('</th></tr></thead>');
640
+ lines.push('<tbody>');
641
+ for (const member of members) {
642
+ const value = member.type?.toString() ?? '';
643
+ const desc = member.comment?.summary ? this._getCommentText(member.comment.summary) : '';
644
+ lines.push('<tr><td>');
645
+ lines.push('');
646
+ lines.push(`\`${member.name}\``);
647
+ lines.push('');
648
+ lines.push('</td><td>');
649
+ lines.push('');
650
+ lines.push(value);
651
+ lines.push('');
652
+ lines.push('</td><td>');
653
+ lines.push('');
654
+ lines.push(desc);
655
+ lines.push('');
656
+ lines.push('</td></tr>');
657
+ }
658
+ lines.push('</tbody></table>');
659
+ lines.push('');
660
+ }
661
+ const outDir = path.join(this._outputDir, basePath, 'enums');
662
+ if (!fs.existsSync(outDir)) {
663
+ fs.mkdirSync(outDir, { recursive: true });
664
+ }
665
+ const outPath = path.join(outDir, `${this._sanitizeName(decl.name)}.md`);
666
+ fs.writeFileSync(outPath, lines.join('\n'));
667
+ }
668
+ _renderTypeAlias(decl, basePath) {
669
+ const lines = [];
670
+ // Breadcrumb
671
+ lines.push(this._generateBreadcrumb(basePath, decl.name, 'type-aliases'));
672
+ lines.push('');
673
+ lines.push(`# Type Alias: ${decl.name}`);
674
+ lines.push('');
675
+ if (decl.comment?.summary) {
676
+ lines.push(this._getCommentText(decl.comment.summary));
677
+ lines.push('');
678
+ }
679
+ if (decl.type) {
680
+ lines.push('## Type');
681
+ lines.push('');
682
+ lines.push('```typescript');
683
+ lines.push(`type ${decl.name} = ${decl.type.toString()}`);
684
+ lines.push('```');
685
+ lines.push('');
686
+ }
687
+ const outDir = path.join(this._outputDir, basePath, 'type-aliases');
688
+ if (!fs.existsSync(outDir)) {
689
+ fs.mkdirSync(outDir, { recursive: true });
690
+ }
691
+ const outPath = path.join(outDir, `${this._sanitizeName(decl.name)}.md`);
692
+ fs.writeFileSync(outPath, lines.join('\n'));
693
+ }
694
+ _renderFunction(decl, basePath) {
695
+ const lines = [];
696
+ // Breadcrumb
697
+ lines.push(this._generateBreadcrumb(basePath, decl.name, 'functions'));
698
+ lines.push('');
699
+ lines.push(`# Function: ${decl.name}`);
700
+ lines.push('');
701
+ const sig = decl.signatures?.[0];
702
+ if (sig?.comment?.summary) {
703
+ lines.push(this._getCommentText(sig.comment.summary));
704
+ lines.push('');
705
+ }
706
+ if (sig) {
707
+ lines.push('## Signature');
708
+ lines.push('');
709
+ lines.push('```typescript');
710
+ lines.push(this._renderSignature(decl.name, sig));
711
+ lines.push('```');
712
+ lines.push('');
713
+ }
714
+ const outDir = path.join(this._outputDir, basePath, 'functions');
715
+ if (!fs.existsSync(outDir)) {
716
+ fs.mkdirSync(outDir, { recursive: true });
717
+ }
718
+ const outPath = path.join(outDir, `${this._sanitizeName(decl.name)}.md`);
719
+ fs.writeFileSync(outPath, lines.join('\n'));
720
+ }
721
+ _renderVariable(decl, basePath) {
722
+ const lines = [];
723
+ // Breadcrumb
724
+ lines.push(this._generateBreadcrumb(basePath, decl.name, 'variables'));
725
+ lines.push('');
726
+ lines.push(`# Variable: ${decl.name}`);
727
+ lines.push('');
728
+ if (decl.comment?.summary) {
729
+ lines.push(this._getCommentText(decl.comment.summary));
730
+ lines.push('');
731
+ }
732
+ if (decl.type) {
733
+ lines.push('## Type');
734
+ lines.push('');
735
+ lines.push(`\`${decl.type.toString()}\``);
736
+ lines.push('');
737
+ }
738
+ const outDir = path.join(this._outputDir, basePath, 'variables');
739
+ if (!fs.existsSync(outDir)) {
740
+ fs.mkdirSync(outDir, { recursive: true });
741
+ }
742
+ const outPath = path.join(outDir, `${this._sanitizeName(decl.name)}.md`);
743
+ fs.writeFileSync(outPath, lines.join('\n'));
744
+ }
745
+ _renderConstructorsTable(constructors, _parentName, _basePath) {
746
+ const lines = [];
747
+ lines.push('<table><thead><tr><th>');
748
+ lines.push('');
749
+ lines.push('Constructor');
750
+ lines.push('');
751
+ lines.push('</th><th>');
752
+ lines.push('');
753
+ lines.push('Modifiers');
754
+ lines.push('');
755
+ lines.push('</th><th>');
756
+ lines.push('');
757
+ lines.push('Description');
758
+ lines.push('');
759
+ lines.push('</th></tr></thead>');
760
+ lines.push('<tbody>');
761
+ for (const ctor of constructors) {
762
+ const sig = ctor.signatures?.[0];
763
+ const params = sig?.parameters?.map((p) => p.name).join(', ') ?? '';
764
+ const ctorSig = `constructor(${params})`;
765
+ const modifiers = [];
766
+ if (ctor.flags?.isProtected)
767
+ modifiers.push('protected');
768
+ if (ctor.flags?.isPrivate)
769
+ modifiers.push('private');
770
+ const desc = sig?.comment?.summary ? this._getCommentText(sig.comment.summary) : '';
771
+ lines.push('<tr><td>');
772
+ lines.push('');
773
+ lines.push(`\`${ctorSig}\``);
774
+ lines.push('');
775
+ lines.push('</td><td>');
776
+ lines.push('');
777
+ lines.push(modifiers.map((m) => `\`${m}\``).join(' '));
778
+ lines.push('');
779
+ lines.push('</td><td>');
780
+ lines.push('');
781
+ lines.push(desc);
782
+ lines.push('');
783
+ lines.push('</td></tr>');
784
+ }
785
+ lines.push('</tbody></table>');
786
+ return lines.join('\n');
787
+ }
788
+ _renderPropertiesTable(members, parentName, _basePath) {
789
+ const lines = [];
790
+ lines.push('<table><thead><tr><th>');
791
+ lines.push('');
792
+ lines.push('Property');
793
+ lines.push('');
794
+ lines.push('</th><th>');
795
+ lines.push('');
796
+ lines.push('Modifiers');
797
+ lines.push('');
798
+ lines.push('</th><th>');
799
+ lines.push('');
800
+ lines.push('Type');
801
+ lines.push('');
802
+ lines.push('</th><th>');
803
+ lines.push('');
804
+ lines.push('Description');
805
+ lines.push('');
806
+ lines.push('</th></tr></thead>');
807
+ lines.push('<tbody>');
808
+ for (const member of members) {
809
+ if (member.kind === ReflectionKind.Accessor) {
810
+ lines.push(this._renderAccessorRow(member, parentName));
811
+ }
812
+ else {
813
+ lines.push(this._renderPropertyRow(member, parentName));
814
+ }
815
+ }
816
+ lines.push('</tbody></table>');
817
+ return lines.join('\n');
818
+ }
819
+ _renderPropertyRow(prop, parentName) {
820
+ const typeStr = prop.type?.toString() ?? '';
821
+ const modifiers = [];
822
+ if (prop.flags?.isReadonly)
823
+ modifiers.push('readonly');
824
+ if (prop.flags?.isProtected)
825
+ modifiers.push('protected');
826
+ if (prop.flags?.isPrivate)
827
+ modifiers.push('private');
828
+ if (prop.flags?.isStatic)
829
+ modifiers.push('static');
830
+ const desc = prop.comment?.summary ? this._getFirstLine(this._getCommentText(prop.comment.summary)) : '';
831
+ // For inherited members, link to the defining class's detail page (unless includeInheritedMemberPages is true)
832
+ const definingClass = !this._options.includeInheritedMemberPages && prop.inheritedFrom
833
+ ? this._sanitizeName(prop.inheritedFrom.reflection?.parent?.name ?? parentName)
834
+ : parentName;
835
+ const detailUrl = `./${this._sanitizeName(definingClass)}.${this._sanitizeName(prop.name)}.md`;
836
+ return this._formatTableRow(prop.name, modifiers, typeStr, desc, detailUrl);
837
+ }
838
+ _renderAccessorRow(accessor, parentName) {
839
+ const getter = accessor.getSignature;
840
+ const setter = accessor.setSignature;
841
+ let typeStr = '';
842
+ if (getter?.type) {
843
+ typeStr = getter.type.toString();
844
+ }
845
+ else if (setter?.parameters?.[0]?.type) {
846
+ typeStr = setter.parameters[0].type.toString();
847
+ }
848
+ const modifiers = [];
849
+ if (getter && !setter)
850
+ modifiers.push('readonly');
851
+ if (accessor.flags?.isProtected)
852
+ modifiers.push('protected');
853
+ if (accessor.flags?.isPrivate)
854
+ modifiers.push('private');
855
+ if (accessor.flags?.isStatic)
856
+ modifiers.push('static');
857
+ let desc = '';
858
+ if (getter?.comment?.summary) {
859
+ desc = this._getFirstLine(this._getCommentText(getter.comment.summary));
860
+ }
861
+ else if (setter?.comment?.summary) {
862
+ desc = this._getFirstLine(this._getCommentText(setter.comment.summary));
863
+ }
864
+ // For inherited members, link to the defining class's detail page (unless includeInheritedMemberPages is true)
865
+ const definingClass = !this._options.includeInheritedMemberPages && accessor.inheritedFrom
866
+ ? this._sanitizeName(accessor.inheritedFrom.reflection?.parent?.name ?? parentName)
867
+ : parentName;
868
+ const detailUrl = `./${this._sanitizeName(definingClass)}.${this._sanitizeName(accessor.name)}.md`;
869
+ return this._formatTableRow(accessor.name, modifiers, typeStr, desc, detailUrl);
870
+ }
871
+ _formatTableRow(name, modifiers, type, description, detailUrl) {
872
+ const linkedType = this._linkType(type);
873
+ const modifierStr = modifiers.map((m) => `\`${m}\``).join(' ');
874
+ // Create linked name if detail URL provided
875
+ const nameDisplay = detailUrl ? `[${name}](${detailUrl})` : `\`${name}\``;
876
+ const lines = [];
877
+ lines.push('<tr><td>');
878
+ lines.push('');
879
+ lines.push(nameDisplay);
880
+ lines.push('');
881
+ lines.push('</td><td>');
882
+ lines.push('');
883
+ lines.push(modifierStr);
884
+ lines.push('');
885
+ lines.push('</td><td>');
886
+ lines.push('');
887
+ lines.push(linkedType);
888
+ lines.push('');
889
+ lines.push('</td><td>');
890
+ lines.push('');
891
+ lines.push(description);
892
+ lines.push('');
893
+ lines.push('</td></tr>');
894
+ return lines.join('\n');
895
+ }
896
+ _renderMethodsTable(methods, parentName, _basePath) {
897
+ const lines = [];
898
+ lines.push('<table><thead><tr><th>');
899
+ lines.push('');
900
+ lines.push('Method');
901
+ lines.push('');
902
+ lines.push('</th><th>');
903
+ lines.push('');
904
+ lines.push('Modifiers');
905
+ lines.push('');
906
+ lines.push('</th><th>');
907
+ lines.push('');
908
+ lines.push('Description');
909
+ lines.push('');
910
+ lines.push('</th></tr></thead>');
911
+ lines.push('<tbody>');
912
+ for (const method of methods) {
913
+ const sig = method.signatures?.[0];
914
+ const params = sig?.parameters?.map((p) => p.name).join(', ') ?? '';
915
+ const methodSig = `${method.name}(${params})`;
916
+ const modifiers = [];
917
+ if (method.flags?.isProtected)
918
+ modifiers.push('protected');
919
+ if (method.flags?.isPrivate)
920
+ modifiers.push('private');
921
+ if (method.flags?.isStatic)
922
+ modifiers.push('static');
923
+ const desc = sig?.comment?.summary ? this._getFirstLine(this._getCommentText(sig.comment.summary)) : '';
924
+ // For inherited members, link to the defining class's detail page (unless includeInheritedMemberPages is true)
925
+ const definingClass = !this._options.includeInheritedMemberPages && method.inheritedFrom
926
+ ? this._sanitizeName(method.inheritedFrom.reflection?.parent?.name ?? parentName)
927
+ : parentName;
928
+ const detailUrl = `./${this._sanitizeName(definingClass)}.${this._sanitizeName(method.name)}.md`;
929
+ lines.push('<tr><td>');
930
+ lines.push('');
931
+ lines.push(`[${methodSig}](${detailUrl})`);
932
+ lines.push('');
933
+ lines.push('</td><td>');
934
+ lines.push('');
935
+ lines.push(modifiers.map((m) => `\`${m}\``).join(' '));
936
+ lines.push('');
937
+ lines.push('</td><td>');
938
+ lines.push('');
939
+ lines.push(desc);
940
+ lines.push('');
941
+ lines.push('</td></tr>');
942
+ }
943
+ lines.push('</tbody></table>');
944
+ return lines.join('\n');
945
+ }
946
+ _renderSignature(name, sig) {
947
+ const params = sig.parameters?.map((p) => `${p.name}: ${p.type?.toString() ?? 'unknown'}`).join(', ') ?? '';
948
+ const returnType = sig.type?.toString() ?? 'void';
949
+ return `function ${name}(${params}): ${returnType}`;
950
+ }
951
+ /**
952
+ * Find direct children of a specific kind (no recursion into namespaces).
953
+ */
954
+ _findByKind(parent, kind) {
955
+ if (!(parent instanceof DeclarationReflection) && !(parent instanceof ProjectReflection)) {
956
+ return [];
957
+ }
958
+ return (parent.children ?? []).filter((child) => child.kind === kind);
959
+ }
960
+ _sanitizeName(name) {
961
+ return name.replace(/[^a-zA-Z0-9_-]/g, '_');
962
+ }
963
+ /**
964
+ * Generate breadcrumb navigation for a page.
965
+ * @param basePath - The namespace path (e.g., "LibraryRuntime/Indexers")
966
+ * @param currentName - The name of the current item
967
+ * @param subdir - The subdirectory within the namespace (e.g., "classes", "interfaces")
968
+ */
969
+ _generateBreadcrumb(basePath, currentName, subdir) {
970
+ const parts = [];
971
+ // Calculate how many levels deep we are
972
+ const pathParts = basePath ? basePath.split('/').filter((p) => p) : [];
973
+ const subdirDepth = subdir ? 1 : 0;
974
+ const totalDepth = pathParts.length + subdirDepth;
975
+ // Home link - go up the appropriate number of directories
976
+ const homePrefix = totalDepth > 0 ? '../'.repeat(totalDepth) : './';
977
+ parts.push(`[Home](${homePrefix}README.md)`);
978
+ // Namespace path components
979
+ let accumulatedPath = '';
980
+ for (let i = 0; i < pathParts.length; i++) {
981
+ const nsName = pathParts[i];
982
+ accumulatedPath += (accumulatedPath ? '/' : '') + nsName;
983
+ // Calculate relative path from current location to this namespace
984
+ const levelsUp = totalDepth - (i + 1);
985
+ const nsPrefix = levelsUp > 0 ? '../'.repeat(levelsUp) : './';
986
+ parts.push(`[${nsName}](${nsPrefix}README.md)`);
987
+ }
988
+ // Current item (not linked)
989
+ parts.push(currentName);
990
+ return parts.join(' > ');
991
+ }
992
+ _escapeHtml(str) {
993
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
994
+ }
995
+ _getCommentText(summary) {
996
+ return summary.map((part) => part.text).join('');
997
+ }
998
+ /**
999
+ * Compute relative path from current file to target file.
1000
+ */
1001
+ _relativePath(from, to) {
1002
+ const fromDir = path.dirname(from);
1003
+ return path.relative(fromDir, to);
1004
+ }
1005
+ /**
1006
+ * Convert a type string to a linked version where possible (for HTML table cells).
1007
+ * Handles generic types like `Map<K, V>` by linking individual type names.
1008
+ */
1009
+ _linkType(typeStr) {
1010
+ if (!typeStr)
1011
+ return '';
1012
+ // Parse type string and link known types
1013
+ // This regex matches type names (identifiers) that might be linkable
1014
+ const result = typeStr.replace(/\b([A-Z][a-zA-Z0-9_]*)\b/g, (match, typeName) => {
1015
+ const urlInfo = this._urlMap.get(typeName);
1016
+ if (urlInfo) {
1017
+ const relativePath = this._relativePath(this._currentFilePath, urlInfo.url);
1018
+ return `[${typeName}](${relativePath})`;
1019
+ }
1020
+ return match;
1021
+ });
1022
+ return this._escapeHtml(result);
1023
+ }
1024
+ /**
1025
+ * Convert a type string to a linked version for inline markdown (not in HTML tables).
1026
+ * Returns the type wrapped in backticks, with links if the type is documented.
1027
+ */
1028
+ _linkTypeInline(typeStr) {
1029
+ if (!typeStr)
1030
+ return '';
1031
+ // Check if the whole type name (without generics) is linkable
1032
+ const baseType = typeStr.split('<')[0].trim();
1033
+ const urlInfo = this._urlMap.get(baseType);
1034
+ if (urlInfo) {
1035
+ const relativePath = this._relativePath(this._currentFilePath, urlInfo.url);
1036
+ return `[\`${typeStr}\`](${relativePath})`;
1037
+ }
1038
+ return `\`${typeStr}\``;
1039
+ }
1040
+ /**
1041
+ * Set the current file being rendered (for computing relative URLs).
1042
+ */
1043
+ _setCurrentFile(filePath) {
1044
+ this._currentFilePath = filePath;
1045
+ }
1046
+ }
1047
+ /**
1048
+ * Plugin load function - entry point for TypeDoc.
1049
+ */
1050
+ export function load(app) {
1051
+ // Declare custom option for inherited member pages
1052
+ app.options.addDeclaration({
1053
+ name: 'includeInheritedMemberPages',
1054
+ help: '[Compact Theme] Generate separate detail pages for inherited members instead of linking to parent class',
1055
+ type: ParameterType.Boolean,
1056
+ defaultValue: false
1057
+ });
1058
+ // Read options after bootstrap
1059
+ let outputDir;
1060
+ let includeInheritedMemberPages = false;
1061
+ app.on(Application.EVENT_BOOTSTRAP_END, () => {
1062
+ outputDir = app.options.getValue('out');
1063
+ includeInheritedMemberPages = app.options.getValue('includeInheritedMemberPages');
1064
+ });
1065
+ // Render our markdown after conversion completes, before the renderer runs
1066
+ app.converter.on(Converter.EVENT_RESOLVE_END, (context) => {
1067
+ if (!outputDir) {
1068
+ console.error('[compact-markdown] Output directory not set');
1069
+ return;
1070
+ }
1071
+ // Render using our custom format
1072
+ const options = {
1073
+ includeInheritedMemberPages
1074
+ };
1075
+ const renderer = new CompactMarkdownRenderer(outputDir, options);
1076
+ renderer.renderProject(context.project);
1077
+ console.log(`[compact-markdown] Generated markdown documentation at ${outputDir}`);
1078
+ // Exit before TypeDoc's HTML renderer runs
1079
+ // This is the simplest way to prevent default output without intermediate files
1080
+ process.exit(0);
1081
+ });
1082
+ }
1083
+ //# sourceMappingURL=index.js.map