rspress-plugin-api-extractor 0.1.2 → 0.2.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.
Files changed (109) hide show
  1. package/README.md +2 -2
  2. package/api-extracted-package.js +426 -0
  3. package/build-program.js +120 -0
  4. package/build-stages.js +664 -0
  5. package/category-resolver.js +50 -0
  6. package/code-post-processor.js +38 -0
  7. package/config-helpers.js +106 -0
  8. package/config-utils.js +258 -0
  9. package/content-hash.js +79 -0
  10. package/errors.js +29 -0
  11. package/formatter.js +69 -0
  12. package/hide-cut-transformer.js +96 -0
  13. package/index.d.ts +901 -831
  14. package/index.js +4 -6128
  15. package/layers/ConfigServiceLive.js +377 -0
  16. package/layers/ObservabilityLive.js +136 -0
  17. package/layers/PathDerivationServiceLive.js +16 -0
  18. package/layers/SnapshotServiceLive.js +94 -0
  19. package/layers/TypeRegistryServiceLive.js +46 -0
  20. package/llms-processing.js +270 -0
  21. package/llms-program.js +262 -0
  22. package/loader.js +186 -0
  23. package/markdown/cross-linker.js +156 -0
  24. package/markdown/helpers.js +364 -0
  25. package/markdown/index.js +11 -0
  26. package/markdown/page-generators/class-page.js +357 -0
  27. package/markdown/page-generators/enum-page.js +152 -0
  28. package/markdown/page-generators/function-page.js +127 -0
  29. package/markdown/page-generators/index-pages.js +27 -0
  30. package/markdown/page-generators/interface-page.js +307 -0
  31. package/markdown/page-generators/namespace-page.js +280 -0
  32. package/markdown/page-generators/type-alias-page.js +110 -0
  33. package/markdown/page-generators/variable-page.js +110 -0
  34. package/markdown/shiki-utils.js +48 -0
  35. package/migrations/001_create_snapshots.js +25 -0
  36. package/model-loader.js +95 -0
  37. package/multi-entry-resolver.js +70 -0
  38. package/og-resolver.js +271 -0
  39. package/package.json +63 -73
  40. package/path-derivation.js +48 -0
  41. package/plugin.js +218 -0
  42. package/prettier-formatter.js +73 -0
  43. package/public/tsconfig/rspress.json +44 -0
  44. package/remark-api-codeblocks.js +130 -0
  45. package/remark-with-api.js +172 -0
  46. package/route-collisions.js +52 -0
  47. package/runtime/components/ApiExample/index.js +29 -25
  48. package/runtime/components/ApiLlmsPackageActions/index.js +252 -323
  49. package/runtime/components/ApiLlmsViewOptions/index.js +269 -340
  50. package/runtime/components/ApiMember/index.js +49 -47
  51. package/runtime/components/ApiSignature/index.js +32 -28
  52. package/runtime/components/EnumMembersTable/{index_module.css → index.css} +18 -19
  53. package/runtime/components/EnumMembersTable/index.js +36 -67
  54. package/runtime/components/EnumMembersTable/index.module.js +9 -6
  55. package/runtime/components/ExampleBlock/{index_module.css → index.css} +2 -3
  56. package/runtime/components/ExampleBlock/index.js +23 -28
  57. package/runtime/components/ExampleBlock/index.module.js +8 -5
  58. package/runtime/components/MarkdownContent/index.js +26 -18
  59. package/runtime/components/MarkdownText/index.js +28 -22
  60. package/runtime/components/MemberSignature/{index_module.css → index.css} +5 -6
  61. package/runtime/components/MemberSignature/index.js +46 -46
  62. package/runtime/components/MemberSignature/index.module.js +9 -6
  63. package/runtime/components/ParametersTable/{index_module.css → index.css} +19 -20
  64. package/runtime/components/ParametersTable/index.js +36 -67
  65. package/runtime/components/ParametersTable/index.module.js +9 -6
  66. package/runtime/components/SignatureBlock/{index_module.css → index.css} +5 -6
  67. package/runtime/components/SignatureBlock/index.js +30 -29
  68. package/runtime/components/SignatureBlock/index.module.js +9 -6
  69. package/runtime/components/SignatureCode/{index_module.css → index.css} +9 -10
  70. package/runtime/components/SignatureCode/index.js +40 -32
  71. package/runtime/components/SignatureCode/index.module.js +9 -6
  72. package/runtime/components/SignatureToolbar/{index_module.css → index.css} +18 -20
  73. package/runtime/components/SignatureToolbar/index.js +48 -51
  74. package/runtime/components/SignatureToolbar/index.module.js +13 -10
  75. package/runtime/components/buttons/ButtonGroup.js +13 -6
  76. package/runtime/components/buttons/CopyCodeButton.js +40 -38
  77. package/runtime/components/buttons/WrapSignatureButton.js +20 -16
  78. package/runtime/components/buttons/{index_module.css → index.css} +5 -6
  79. package/runtime/components/buttons/index.module.js +8 -5
  80. package/runtime/components/icons/CheckIcon/index.js +20 -17
  81. package/runtime/components/icons/CopyIcon/index.js +20 -17
  82. package/runtime/components/icons/UnwrapIcon/index.js +22 -18
  83. package/runtime/components/icons/WrapIcon/index.js +20 -17
  84. package/runtime/components/shared/_twoslash.css +3 -10
  85. package/runtime/components/shared/variables.css +0 -3
  86. package/runtime/hooks/useWrapToggle.js +32 -9
  87. package/runtime/index.d.ts +513 -173
  88. package/runtime/index.js +11 -9
  89. package/runtime/utils/decode-hast.js +31 -16
  90. package/runtime/utils/hast-renderer.js +21 -7
  91. package/schemas/config.js +199 -0
  92. package/schemas/index.js +5 -0
  93. package/schemas/opengraph.js +26 -0
  94. package/schemas/performance.js +19 -0
  95. package/serve.js +133 -0
  96. package/services/ConfigService.js +7 -0
  97. package/services/PathDerivationService.js +7 -0
  98. package/services/SnapshotService.js +7 -0
  99. package/services/TypeRegistryService.js +7 -0
  100. package/shiki-transformer.js +758 -0
  101. package/tsconfig-parser.js +127 -0
  102. package/tsdoc-metadata.json +11 -11
  103. package/twoslash-patterns.js +87 -0
  104. package/twoslash-transformer.js +316 -0
  105. package/type-reference-extractor.js +201 -0
  106. package/typescript-config.js +168 -0
  107. package/vfs-registry.js +121 -0
  108. package/0~llms-program.js +0 -344
  109. package/runtime/components/ApiLlmsPackageActions/index.module.js +0 -2
@@ -0,0 +1,307 @@
1
+ import { ApiParser } from "../../loader.js";
2
+ import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
3
+ import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, sanitizeId, stripTwoslashDirectives } from "../helpers.js";
4
+ import { markdownCrossLinker } from "../cross-linker.js";
5
+ import { TypeSignatureFormatter } from "../../formatter.js";
6
+
7
+ //#region src/markdown/page-generators/interface-page.ts
8
+ /**
9
+ * Generates MDX documentation pages for TypeScript interfaces.
10
+ *
11
+ * This class transforms API Extractor interface models into rich MDX documentation
12
+ * pages with syntax-highlighted signatures, cross-linked type references, and
13
+ * interactive features.
14
+ *
15
+ * **Page Structure:**
16
+ * 1. Frontmatter with title, description, and Open Graph metadata
17
+ * 2. Component imports (SourceCode, ParametersTable, ApiSignature, etc.)
18
+ * 3. Page title (H1) and summary
19
+ * 4. Optional deprecation warning and release tag badge
20
+ * 5. Source code link toolbar
21
+ * 6. Full interface signature block showing all members
22
+ * 7. Member sections: Call Signatures, Construct Signatures, Index Signatures, Properties, Methods
23
+ * 8. Examples section with Twoslash-enabled code blocks
24
+ * 9. See Also references
25
+ *
26
+ * **Interface-Specific Features:**
27
+ * - Handles type parameters (generics) in interface declarations
28
+ * - Supports call signatures for callable interfaces
29
+ * - Supports construct signatures for constructable interfaces
30
+ * - Supports index signatures for dictionary-like interfaces
31
+ * - Handles extends clauses for interface inheritance
32
+ *
33
+ * **Relationships:**
34
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
35
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
36
+ * - Uses {@link ApiParser} for extracting documentation from API models
37
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const generator = new InterfacePageGenerator();
42
+ * const { routePath, content } = await generator.generate(
43
+ * apiInterface,
44
+ * "/api/my-package",
45
+ * "my-package",
46
+ * "Interface",
47
+ * "My Package",
48
+ * sourceConfig,
49
+ * true, // suppressExampleErrors
50
+ * undefined, // llmsPlugin
51
+ * "claude-binary-plugin", // apiScope
52
+ * );
53
+ * ```
54
+ *
55
+ * @see {@link ClassPageGenerator} for class documentation
56
+ * @see {@link TypeAliasPageGenerator} for type alias documentation
57
+ */
58
+ var InterfacePageGenerator = class {
59
+ typeFormatter = new TypeSignatureFormatter();
60
+ /**
61
+ * Generate a markdown page for an interface
62
+ *
63
+ * @param apiScope - API scope identifier for VFS lookup
64
+ */
65
+ async generate(apiInterface, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
66
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
67
+ const name = apiInterface.displayName;
68
+ const summary = ApiParser.getSummary(apiInterface) || "No description available.";
69
+ const releaseTag = ApiParser.getReleaseTag(apiInterface);
70
+ let content = generateFrontmatter(name, summary, singularName, apiName);
71
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
72
+ content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
73
+ content += `import { ApiSignature, ApiMember, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
74
+ content += `# ${name}\n\n`;
75
+ const deprecation = ApiParser.getDeprecation(apiInterface);
76
+ if (deprecation) {
77
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
78
+ content += `> ⚠️ **Deprecated:** ${message}\n\n`;
79
+ }
80
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
81
+ content += `${summary}\n\n`;
82
+ content += generateAvailableFrom(packageName, availableFrom);
83
+ const sourceLink = ApiParser.getSourceLink(apiInterface, sourceConfig);
84
+ if (sourceLink) {
85
+ content += `<div className="api-docs-toolbar">\n`;
86
+ content += ` <div className="api-docs-toolbar-left">\n`;
87
+ content += ` <SourceCode href="${sourceLink}" />\n`;
88
+ content += ` </div>\n`;
89
+ if (llmsPlugin?.enabled) {
90
+ content += ` <div className="api-docs-toolbar-right">\n`;
91
+ content += ` </div>\n`;
92
+ }
93
+ content += `</div>\n\n`;
94
+ }
95
+ const skeleton = this.generateInterfaceSkeletonWithTwoslash(apiInterface, packageName);
96
+ const displayCode = stripTwoslashDirectives(skeleton);
97
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(skeleton)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
98
+ const callSignatures = apiInterface.members.filter((m) => m.kind === "CallSignature");
99
+ if (callSignatures.length > 0) {
100
+ content += `## Call Signatures\n\n`;
101
+ for (const callSig of callSignatures) {
102
+ const callSigSummary = ApiParser.getSummary(callSig);
103
+ const callSigId = sanitizeId("call-signature");
104
+ const callSigItem = callSig;
105
+ if (callSigItem.excerpt?.text) {
106
+ const memberSignature = this.typeFormatter.format(callSigItem.excerpt).trim();
107
+ const skeletonWithContext = this.generateInterfaceMemberWithContext(apiInterface, callSig, packageName);
108
+ const summaryMd = callSigSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(callSigSummary)) : void 0;
109
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName="Call Signature"${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(callSigId)}} />\n\n`;
110
+ }
111
+ }
112
+ }
113
+ const constructSignatures = apiInterface.members.filter((m) => m.kind === "ConstructSignature");
114
+ if (constructSignatures.length > 0) {
115
+ content += `## Construct Signatures\n\n`;
116
+ for (const constructSig of constructSignatures) {
117
+ const constructSigSummary = ApiParser.getSummary(constructSig);
118
+ const constructSigId = sanitizeId("construct-signature");
119
+ const constructSigItem = constructSig;
120
+ if (constructSigItem.excerpt?.text) {
121
+ const memberSignature = this.typeFormatter.format(constructSigItem.excerpt).trim();
122
+ const skeletonWithContext = this.generateInterfaceMemberWithContext(apiInterface, constructSig, packageName);
123
+ const summaryMd = constructSigSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(constructSigSummary)) : void 0;
124
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName="Construct Signature"${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(constructSigId)}} />\n\n`;
125
+ }
126
+ }
127
+ }
128
+ const indexSignatures = apiInterface.members.filter((m) => m.kind === "IndexSignature");
129
+ if (indexSignatures.length > 0) {
130
+ content += `## Index Signature\n\n`;
131
+ for (const indexSig of indexSignatures) {
132
+ const indexSigSummary = ApiParser.getSummary(indexSig);
133
+ const indexSigId = sanitizeId("index-signature");
134
+ const indexSigItem = indexSig;
135
+ if (indexSigItem.excerpt?.text) {
136
+ const memberSignature = this.typeFormatter.format(indexSigItem.excerpt).trim();
137
+ const skeletonWithContext = this.generateInterfaceMemberWithContext(apiInterface, indexSig, packageName);
138
+ const summaryMd = indexSigSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(indexSigSummary)) : void 0;
139
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName="Index Signature"${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(indexSigId)}} />\n\n`;
140
+ }
141
+ }
142
+ }
143
+ const properties = apiInterface.members.filter((m) => m.kind === "PropertySignature");
144
+ if (properties.length > 0) {
145
+ content += `## Properties\n\n`;
146
+ for (const prop of properties) {
147
+ const propSummary = ApiParser.getSummary(prop);
148
+ const propId = sanitizeId(prop.displayName);
149
+ const propItem = prop;
150
+ if (propItem.excerpt?.text) {
151
+ const memberSignature = this.typeFormatter.format(propItem.excerpt).trim();
152
+ const skeletonWithContext = this.generateInterfaceMemberWithContext(apiInterface, prop, packageName);
153
+ const summaryMd = propSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(propSummary)) : void 0;
154
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName={${JSON.stringify(prop.displayName)}}${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(propId)}} />\n\n`;
155
+ }
156
+ }
157
+ }
158
+ const methods = apiInterface.members.filter((m) => m.kind === "MethodSignature");
159
+ if (methods.length > 0) {
160
+ content += `## Methods\n\n`;
161
+ for (const method of methods) {
162
+ const methodSummary = ApiParser.getSummary(method);
163
+ const methodId = sanitizeId(method.displayName);
164
+ const methodItem = method;
165
+ if (methodItem.excerpt?.text) {
166
+ const memberSignature = this.typeFormatter.format(methodItem.excerpt).trim();
167
+ const skeletonWithContext = this.generateInterfaceMemberWithContext(apiInterface, method, packageName);
168
+ const hasParameters = ApiParser.getParams(method).length > 0;
169
+ const summaryMd = methodSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(methodSummary)) : void 0;
170
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName={${JSON.stringify(method.displayName)}}${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(methodId)}} hasParameters={${hasParameters}} />\n\n`;
171
+ }
172
+ const params = ApiParser.getParams(method);
173
+ if (params.length > 0) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
174
+ name: p.name,
175
+ type: p.type,
176
+ description: markdownCrossLinker.addCrossLinks(p.description)
177
+ })))}} />\n\n`;
178
+ const returns = ApiParser.getReturns(method);
179
+ if (returns) {
180
+ const description = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(returns.description));
181
+ content += `**Returns:** ${description}\n\n`;
182
+ }
183
+ }
184
+ }
185
+ const examples = ApiParser.getExamples(apiInterface);
186
+ if (examples.length > 0) {
187
+ content += `## Examples\n\n`;
188
+ for (const example of examples) {
189
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
190
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
191
+ api: packageName,
192
+ blockType: "example"
193
+ });
194
+ if (prepared.isTypeScript) {
195
+ const displayCode = stripTwoslashDirectives(formattedCode);
196
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
197
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
198
+ }
199
+ }
200
+ const seeReferences = ApiParser.getSeeReferences(apiInterface);
201
+ if (seeReferences.length > 0) {
202
+ content += `## See Also\n\n`;
203
+ for (const reference of seeReferences) {
204
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
205
+ content += `- ${refText}\n`;
206
+ }
207
+ content += `\n`;
208
+ }
209
+ return {
210
+ routePath: `${baseRoute}/interface/${name.toLowerCase()}`,
211
+ content
212
+ };
213
+ }
214
+ /**
215
+ * Generate an interface member signature with full interface context
216
+ * Includes hidden imports with cut directive for external type resolution
217
+ * Uses the simplified approach: 3 lines (interface opening, member, closing)
218
+ */
219
+ generateInterfaceMemberWithContext(apiInterface, targetMember, packageName) {
220
+ const interfaceName = apiInterface.displayName;
221
+ const inheritance = ApiParser.getInheritance(apiInterface);
222
+ let declaration = `interface ${interfaceName}`;
223
+ if (apiInterface.typeParameters && apiInterface.typeParameters.length > 0) {
224
+ const typeParams = apiInterface.typeParameters.map((tp) => tp.name).join(", ");
225
+ declaration += `<${typeParams}>`;
226
+ }
227
+ if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
228
+ declaration += " {";
229
+ const memberItem = targetMember;
230
+ const memberSignature = memberItem.excerpt?.text ? this.typeFormatter.format(memberItem.excerpt).trim() : "";
231
+ const skeleton = `${declaration}\n${memberSignature}\n}`;
232
+ const apiPackage = apiInterface.getAssociatedPackage?.();
233
+ if (apiPackage) return prependHiddenImports(skeleton, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(targetMember));
234
+ return skeleton;
235
+ }
236
+ /**
237
+ * Generate an interface skeleton for signature blocks
238
+ * Includes hidden imports with cut directive for external type resolution
239
+ */
240
+ generateInterfaceSkeletonWithTwoslash(apiInterface, packageName) {
241
+ const skeleton = this.generateInterfaceSkeleton(apiInterface);
242
+ const apiPackage = apiInterface.getAssociatedPackage?.();
243
+ if (apiPackage) return prependHiddenImports(skeleton, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiInterface));
244
+ return skeleton;
245
+ }
246
+ /**
247
+ * Generate a complete interface skeleton showing all members
248
+ */
249
+ generateInterfaceSkeleton(apiInterface) {
250
+ const lines = [];
251
+ const interfaceName = apiInterface.displayName;
252
+ const inheritance = ApiParser.getInheritance(apiInterface);
253
+ let declaration = `interface ${interfaceName}`;
254
+ if (apiInterface.typeParameters && apiInterface.typeParameters.length > 0) {
255
+ const typeParams = apiInterface.typeParameters.map((tp) => tp.name).join(", ");
256
+ declaration += `<${typeParams}>`;
257
+ }
258
+ if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
259
+ declaration += " {";
260
+ lines.push(declaration);
261
+ const callSignatures = apiInterface.members.filter((m) => m.kind === "CallSignature");
262
+ if (callSignatures.length > 0) for (const callSig of callSignatures) {
263
+ const callSigItem = callSig;
264
+ if (callSigItem.excerpt?.text) {
265
+ const signature = this.typeFormatter.format(callSigItem.excerpt).trim();
266
+ lines.push(` ${signature}`);
267
+ }
268
+ }
269
+ const constructSignatures = apiInterface.members.filter((m) => m.kind === "ConstructSignature");
270
+ if (constructSignatures.length > 0) for (const constructSig of constructSignatures) {
271
+ const constructSigItem = constructSig;
272
+ if (constructSigItem.excerpt?.text) {
273
+ const signature = this.typeFormatter.format(constructSigItem.excerpt).trim();
274
+ lines.push(` ${signature}`);
275
+ }
276
+ }
277
+ const indexSignatures = apiInterface.members.filter((m) => m.kind === "IndexSignature");
278
+ if (indexSignatures.length > 0) for (const indexSig of indexSignatures) {
279
+ const indexSigItem = indexSig;
280
+ if (indexSigItem.excerpt?.text) {
281
+ const signature = this.typeFormatter.format(indexSigItem.excerpt).trim();
282
+ lines.push(` ${signature}`);
283
+ }
284
+ }
285
+ const properties = apiInterface.members.filter((m) => m.kind === "PropertySignature");
286
+ if (properties.length > 0) for (const prop of properties) {
287
+ const propItem = prop;
288
+ if (propItem.excerpt?.text) {
289
+ const signature = this.typeFormatter.format(propItem.excerpt).trim();
290
+ lines.push(` ${signature}`);
291
+ }
292
+ }
293
+ const methods = apiInterface.members.filter((m) => m.kind === "MethodSignature");
294
+ if (methods.length > 0) for (const method of methods) {
295
+ const methodItem = method;
296
+ if (methodItem.excerpt?.text) {
297
+ const signature = this.typeFormatter.format(methodItem.excerpt).trim();
298
+ lines.push(` ${signature}`);
299
+ }
300
+ }
301
+ lines.push("}");
302
+ return lines.join("\n");
303
+ }
304
+ };
305
+
306
+ //#endregion
307
+ export { InterfacePageGenerator };
@@ -0,0 +1,280 @@
1
+ import { ApiParser } from "../../loader.js";
2
+ import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
3
+ import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
4
+ import { markdownCrossLinker } from "../cross-linker.js";
5
+ import { TypeSignatureFormatter } from "../../formatter.js";
6
+ import { ApiItemKind } from "@microsoft/api-extractor-model";
7
+
8
+ //#region src/markdown/page-generators/namespace-page.ts
9
+ /**
10
+ * Generates MDX documentation pages for TypeScript namespaces.
11
+ *
12
+ * This class transforms API Extractor namespace models into rich MDX documentation pages
13
+ * with syntax-highlighted signatures, cross-linked type references, and member listings.
14
+ *
15
+ * **Page Structure:**
16
+ * 1. Frontmatter with title, description, and Open Graph metadata
17
+ * 2. Component imports (SourceCode, ApiSignature, etc.)
18
+ * 3. Page title (H1) and summary
19
+ * 4. Optional deprecation warning and release tag badge
20
+ * 5. Source code link toolbar
21
+ * 6. Full namespace signature block showing all members
22
+ * 7. Member sections: Classes, Interfaces, Functions, Variables, Types, Enums, Namespaces
23
+ * 8. Examples section with Twoslash-enabled code blocks
24
+ * 9. See Also references
25
+ *
26
+ * **Member Rendering:**
27
+ * Each member section lists members with links to their individual documentation pages.
28
+ *
29
+ * **Relationships:**
30
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
31
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
32
+ * - Uses {@link ApiParser} for extracting documentation from API models
33
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const generator = new NamespacePageGenerator();
38
+ * const { routePath, content } = await generator.generate(
39
+ * apiNamespace,
40
+ * "/api/my-package",
41
+ * "my-package",
42
+ * "Namespace",
43
+ * "My Package",
44
+ * sourceConfig,
45
+ * true, // suppressExampleErrors
46
+ * undefined, // llmsPlugin
47
+ * "my-scope"
48
+ * );
49
+ * ```
50
+ *
51
+ * @see {@link ClassPageGenerator} for class documentation
52
+ * @see {@link InterfacePageGenerator} for interface documentation
53
+ */
54
+ var NamespacePageGenerator = class {
55
+ typeFormatter = new TypeSignatureFormatter();
56
+ /**
57
+ * Generate a markdown page for a namespace
58
+ *
59
+ * @param apiScope - API scope identifier for VFS lookup
60
+ */
61
+ async generate(apiNamespace, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
62
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
63
+ const name = apiNamespace.displayName;
64
+ const summary = ApiParser.getSummary(apiNamespace) || "No description available.";
65
+ const releaseTag = ApiParser.getReleaseTag(apiNamespace);
66
+ let content = generateFrontmatter(name, summary, singularName, apiName);
67
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
68
+ content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
69
+ content += `# ${name}\n\n`;
70
+ const deprecation = ApiParser.getDeprecation(apiNamespace);
71
+ if (deprecation) {
72
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
73
+ content += `> **Deprecated:** ${message}\n\n`;
74
+ }
75
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
76
+ content += `${summary}\n\n`;
77
+ content += generateAvailableFrom(packageName, availableFrom);
78
+ const sourceLink = ApiParser.getSourceLink(apiNamespace, sourceConfig);
79
+ if (sourceLink) {
80
+ content += `<div className="api-docs-toolbar">\n`;
81
+ content += ` <div className="api-docs-toolbar-left">\n`;
82
+ content += ` <SourceCode href="${sourceLink}" />\n`;
83
+ content += ` </div>\n`;
84
+ if (llmsPlugin?.enabled) {
85
+ content += ` <div className="api-docs-toolbar-right">\n`;
86
+ content += ` </div>\n`;
87
+ }
88
+ content += `</div>\n\n`;
89
+ }
90
+ const skeleton = this.generateNamespaceSkeletonWithTwoslash(apiNamespace, packageName);
91
+ const displayCode = stripTwoslashDirectives(skeleton);
92
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(skeleton)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
93
+ const grouped = this.groupNamespaceMembers(apiNamespace.members);
94
+ content += this.renderMemberSection("Classes", grouped.classes, baseRoute, "class", name);
95
+ content += this.renderMemberSection("Interfaces", grouped.interfaces, baseRoute, "interface", name);
96
+ content += this.renderMemberSection("Functions", grouped.functions, baseRoute, "function", name);
97
+ content += this.renderMemberSection("Variables", grouped.variables, baseRoute, "variable", name);
98
+ content += this.renderMemberSection("Types", grouped.typeAliases, baseRoute, "type", name);
99
+ content += this.renderMemberSection("Enums", grouped.enums, baseRoute, "enum", name);
100
+ content += this.renderMemberSection("Namespaces", grouped.namespaces, baseRoute, "namespace", name);
101
+ const examples = ApiParser.getExamples(apiNamespace);
102
+ if (examples.length > 0) {
103
+ content += `## Examples\n\n`;
104
+ for (const example of examples) {
105
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
106
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
107
+ api: packageName,
108
+ blockType: "example"
109
+ });
110
+ if (prepared.isTypeScript) {
111
+ const displayCode = stripTwoslashDirectives(formattedCode);
112
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
113
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
114
+ }
115
+ }
116
+ const seeReferences = ApiParser.getSeeReferences(apiNamespace);
117
+ if (seeReferences.length > 0) {
118
+ content += `## See Also\n\n`;
119
+ for (const reference of seeReferences) {
120
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
121
+ content += `- ${refText}\n`;
122
+ }
123
+ content += `\n`;
124
+ }
125
+ return {
126
+ routePath: `${baseRoute}/namespace/${name.toLowerCase()}`,
127
+ content
128
+ };
129
+ }
130
+ /**
131
+ * Group namespace members by their type
132
+ */
133
+ groupNamespaceMembers(members) {
134
+ const classes = [];
135
+ const interfaces = [];
136
+ const functions = [];
137
+ const variables = [];
138
+ const typeAliases = [];
139
+ const enums = [];
140
+ const namespaces = [];
141
+ for (const member of members) switch (member.kind) {
142
+ case ApiItemKind.Class:
143
+ classes.push(member);
144
+ break;
145
+ case ApiItemKind.Interface:
146
+ interfaces.push(member);
147
+ break;
148
+ case ApiItemKind.Function:
149
+ functions.push(member);
150
+ break;
151
+ case ApiItemKind.Variable:
152
+ variables.push(member);
153
+ break;
154
+ case ApiItemKind.TypeAlias:
155
+ typeAliases.push(member);
156
+ break;
157
+ case ApiItemKind.Enum:
158
+ enums.push(member);
159
+ break;
160
+ case ApiItemKind.Namespace:
161
+ namespaces.push(member);
162
+ break;
163
+ default: break;
164
+ }
165
+ return {
166
+ classes,
167
+ interfaces,
168
+ functions,
169
+ variables,
170
+ typeAliases,
171
+ enums,
172
+ namespaces
173
+ };
174
+ }
175
+ /**
176
+ * Render a section of members with links to their pages
177
+ * @param title - Section heading
178
+ * @param members - Array of API items to list
179
+ * @param baseRoute - Base API route (e.g., /api/package)
180
+ * @param categoryFolder - Category folder name (e.g., "class", "function")
181
+ * @param namespaceName - Parent namespace name for qualified routes
182
+ */
183
+ renderMemberSection(title, members, baseRoute, categoryFolder, namespaceName) {
184
+ if (members.length === 0) return "";
185
+ let section = `## ${title}\n\n`;
186
+ for (const member of members) {
187
+ const memberName = member.displayName;
188
+ const memberSummary = ApiParser.getSummary(member);
189
+ const memberRoute = `${baseRoute}/${categoryFolder}/${`${namespaceName}.${memberName}`.toLowerCase()}`;
190
+ if (memberSummary) {
191
+ const escapedSummary = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(memberSummary));
192
+ section += `- [${memberName}](${memberRoute}) - ${escapedSummary}\n`;
193
+ } else section += `- [${memberName}](${memberRoute})\n`;
194
+ }
195
+ section += `\n`;
196
+ return section;
197
+ }
198
+ /**
199
+ * Generate a namespace skeleton for signature blocks
200
+ * Includes hidden imports with cut directive for external type resolution
201
+ */
202
+ generateNamespaceSkeletonWithTwoslash(apiNamespace, packageName) {
203
+ const skeleton = this.generateNamespaceSkeleton(apiNamespace);
204
+ const apiPackage = apiNamespace.getAssociatedPackage?.();
205
+ if (apiPackage) return prependHiddenImports(skeleton, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiNamespace));
206
+ return skeleton;
207
+ }
208
+ /**
209
+ * Generate a complete namespace skeleton showing all members
210
+ */
211
+ generateNamespaceSkeleton(apiNamespace) {
212
+ const lines = [];
213
+ const namespaceName = apiNamespace.displayName;
214
+ lines.push(`namespace ${namespaceName} {`);
215
+ const grouped = this.groupNamespaceMembers(apiNamespace.members);
216
+ for (const cls of grouped.classes) {
217
+ const clsItem = cls;
218
+ if (clsItem.excerpt?.text) {
219
+ const signature = this.typeFormatter.format(clsItem.excerpt).trim();
220
+ lines.push(` ${this.abbreviateDeclaration(signature, "class")} { }`);
221
+ }
222
+ }
223
+ for (const iface of grouped.interfaces) {
224
+ const ifaceItem = iface;
225
+ if (ifaceItem.excerpt?.text) {
226
+ const signature = this.typeFormatter.format(ifaceItem.excerpt).trim();
227
+ lines.push(` ${this.abbreviateDeclaration(signature, "interface")} { }`);
228
+ }
229
+ }
230
+ for (const func of grouped.functions) {
231
+ const funcItem = func;
232
+ if (funcItem.excerpt?.text) {
233
+ const signature = this.typeFormatter.format(funcItem.excerpt).trim();
234
+ lines.push(` ${signature}`);
235
+ }
236
+ }
237
+ for (const variable of grouped.variables) {
238
+ const varItem = variable;
239
+ if (varItem.excerpt?.text) {
240
+ const signature = this.typeFormatter.format(varItem.excerpt).trim();
241
+ lines.push(` ${signature}`);
242
+ }
243
+ }
244
+ for (const typeAlias of grouped.typeAliases) {
245
+ const typeItem = typeAlias;
246
+ if (typeItem.excerpt?.text) {
247
+ const signature = this.typeFormatter.format(typeItem.excerpt).trim();
248
+ lines.push(` ${signature}`);
249
+ }
250
+ }
251
+ for (const enumItem of grouped.enums) {
252
+ const enumDeclItem = enumItem;
253
+ if (enumDeclItem.excerpt?.text) {
254
+ const signature = this.typeFormatter.format(enumDeclItem.excerpt).trim();
255
+ lines.push(` ${this.abbreviateDeclaration(signature, "enum")} { }`);
256
+ }
257
+ }
258
+ for (const ns of grouped.namespaces) {
259
+ const nsItem = ns;
260
+ if (nsItem.excerpt?.text) {
261
+ const signature = this.typeFormatter.format(nsItem.excerpt).trim();
262
+ lines.push(` ${this.abbreviateDeclaration(signature, "namespace")} { }`);
263
+ }
264
+ }
265
+ lines.push("}");
266
+ return lines.join("\n");
267
+ }
268
+ /**
269
+ * Abbreviate a full declaration to just its header
270
+ * For example, `class Foo extends Bar implements Baz \{ ... \}` becomes `class Foo extends Bar implements Baz`
271
+ */
272
+ abbreviateDeclaration(signature, _keyword) {
273
+ const braceIndex = signature.indexOf("{");
274
+ if (braceIndex === -1) return signature;
275
+ return signature.substring(0, braceIndex).trim();
276
+ }
277
+ };
278
+
279
+ //#endregion
280
+ export { NamespacePageGenerator };