rspress-plugin-api-extractor 0.1.2 → 0.2.1

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 +64 -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,11 @@
1
+ import { MarkdownCrossLinker, markdownCrossLinker } from "./cross-linker.js";
2
+ import { ClassPageGenerator } from "./page-generators/class-page.js";
3
+ import { EnumPageGenerator } from "./page-generators/enum-page.js";
4
+ import { FunctionPageGenerator } from "./page-generators/function-page.js";
5
+ import { MainIndexPageGenerator } from "./page-generators/index-pages.js";
6
+ import { InterfacePageGenerator } from "./page-generators/interface-page.js";
7
+ import { NamespacePageGenerator } from "./page-generators/namespace-page.js";
8
+ import { TypeAliasPageGenerator } from "./page-generators/type-alias-page.js";
9
+ import { VariablePageGenerator } from "./page-generators/variable-page.js";
10
+
11
+ export { };
@@ -0,0 +1,357 @@
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/class-page.ts
8
+ /**
9
+ * Generates MDX documentation pages for TypeScript/JavaScript classes.
10
+ *
11
+ * This class transforms API Extractor class models into rich MDX documentation pages
12
+ * with syntax-highlighted signatures, cross-linked type references, and interactive
13
+ * features like copy-to-clipboard and line wrapping.
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 class signature block showing all members
22
+ * 7. Member sections: Constructors, Static Properties, Static Methods, Properties, Getters/Setters, Methods
23
+ * 8. Examples section with Twoslash-enabled code blocks
24
+ * 9. See Also references
25
+ *
26
+ * **Member Rendering:**
27
+ * Each member is rendered with:
28
+ * - An `ApiMember` component showing the signature in class context
29
+ * - Optional `ParametersTable` for methods with parameters
30
+ * - Return type documentation
31
+ * - Cross-linked type references
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 ClassPageGenerator();
42
+ * const { routePath, content } = await generator.generate(
43
+ * apiClass,
44
+ * "/api/my-package",
45
+ * "my-package",
46
+ * "Class",
47
+ * "My Package",
48
+ * sourceConfig,
49
+ * true, // suppressExampleErrors
50
+ * undefined, // llmsPlugin
51
+ * "claude-binary-plugin", // apiScope
52
+ * );
53
+ * ```
54
+ *
55
+ * @see {@link InterfacePageGenerator} for interface documentation
56
+ * @see {@link FunctionPageGenerator} for function documentation
57
+ */
58
+ var ClassPageGenerator = class {
59
+ typeFormatter = new TypeSignatureFormatter();
60
+ /**
61
+ * Generate a markdown page for a class
62
+ *
63
+ * @param apiScope - API scope identifier for VFS lookup
64
+ */
65
+ async generate(apiClass, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
66
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
67
+ const name = apiClass.displayName;
68
+ const summary = ApiParser.getSummary(apiClass) || "No description available.";
69
+ const releaseTag = ApiParser.getReleaseTag(apiClass);
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(apiClass);
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(apiClass, 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.generateClassSkeletonWithTwoslash(apiClass, 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 constructors = apiClass.members.filter((m) => m.kind === "Constructor");
99
+ if (constructors.length > 0) {
100
+ content += `## Constructors\n\n`;
101
+ for (const ctor of constructors) {
102
+ const ctorSummary = ApiParser.getSummary(ctor);
103
+ const ctorId = sanitizeId("constructor");
104
+ const ctorItem = ctor;
105
+ const params = ApiParser.getParams(ctor);
106
+ const hasParameters = params.length > 0;
107
+ if (ctorItem.excerpt?.text) {
108
+ const memberSignature = this.typeFormatter.format(ctorItem.excerpt).trim();
109
+ const skeletonWithContext = this.generateClassMemberWithContext(apiClass, ctor, packageName);
110
+ const summaryMd = ctorSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(ctorSummary)) : void 0;
111
+ content += `<ApiMember code={${JSON.stringify(memberSignature)}} source={${JSON.stringify(skeletonWithContext)}} apiScope={${JSON.stringify(apiScope)}} memberName="constructor"${summaryMd ? ` summary={${JSON.stringify(summaryMd)}}` : ""} id={${JSON.stringify(ctorId)}} hasParameters={${hasParameters}} />\n\n`;
112
+ }
113
+ if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
114
+ name: p.name,
115
+ type: p.type,
116
+ description: markdownCrossLinker.addCrossLinks(p.description)
117
+ })))}} />\n\n`;
118
+ }
119
+ }
120
+ const properties = apiClass.members.filter((m) => m.kind === "Property" || m.kind === "PropertySignature");
121
+ const methods = apiClass.members.filter((m) => m.kind === "Method" || m.kind === "MethodSignature");
122
+ const grouped = methods.length > 0 ? this.groupClassMembers(methods) : {
123
+ staticMethods: [],
124
+ instanceMethods: [],
125
+ getters: []
126
+ };
127
+ const staticProperties = properties.filter((m) => {
128
+ return m.isStatic === true;
129
+ });
130
+ const instanceProperties = properties.filter((m) => {
131
+ const isStatic = m.isStatic === true;
132
+ const isGetter = m.displayName.startsWith("get ") || m.displayName.startsWith("set ");
133
+ return !isStatic && !isGetter;
134
+ });
135
+ const prefixMap = this.detectMemberConflicts(staticProperties, grouped.staticMethods, instanceProperties, grouped.instanceMethods, grouped.getters);
136
+ const renderProperties = async (title, propList) => {
137
+ if (propList.length === 0) return;
138
+ content += `## ${title}\n\n`;
139
+ for (const prop of propList) {
140
+ const propSummary = ApiParser.getSummary(prop);
141
+ const baseName = sanitizeId(prop.displayName);
142
+ const prefix = prefixMap.get(baseName) || "";
143
+ const propId = sanitizeId(prop.displayName, prefix);
144
+ const propItem = prop;
145
+ if (propItem.excerpt?.text) {
146
+ const memberSignature = this.typeFormatter.format(propItem.excerpt).trim();
147
+ const skeletonWithContext = this.generateClassMemberWithContext(apiClass, prop, packageName);
148
+ const summaryMd = propSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(propSummary)) : void 0;
149
+ 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`;
150
+ }
151
+ }
152
+ };
153
+ const renderMethods = async (title, methodList) => {
154
+ if (methodList.length === 0) return;
155
+ content += `## ${title}\n\n`;
156
+ for (const method of methodList) {
157
+ const methodSummary = ApiParser.getSummary(method);
158
+ const baseName = sanitizeId(method.displayName);
159
+ const prefix = prefixMap.get(baseName) || "";
160
+ const methodId = sanitizeId(method.displayName, prefix);
161
+ const methodItem = method;
162
+ const params = ApiParser.getParams(method);
163
+ const hasParameters = params.length > 0;
164
+ if (methodItem.excerpt?.text) {
165
+ const memberSignature = this.typeFormatter.format(methodItem.excerpt).trim();
166
+ const skeletonWithContext = this.generateClassMemberWithContext(apiClass, method, packageName);
167
+ const summaryMd = methodSummary ? escapeMdxGenerics(markdownCrossLinker.addCrossLinks(methodSummary)) : void 0;
168
+ 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`;
169
+ }
170
+ if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
171
+ name: p.name,
172
+ type: p.type,
173
+ description: markdownCrossLinker.addCrossLinks(p.description)
174
+ })))}} />\n\n`;
175
+ const returns = ApiParser.getReturns(method);
176
+ if (returns) {
177
+ const description = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(returns.description));
178
+ content += `**Returns:** ${description}\n\n`;
179
+ }
180
+ }
181
+ };
182
+ await renderProperties("Static Properties", staticProperties);
183
+ await renderMethods("Static Methods", grouped.staticMethods);
184
+ await renderProperties("Properties", instanceProperties);
185
+ await renderMethods("Getters & Setters", grouped.getters);
186
+ await renderMethods("Methods", grouped.instanceMethods);
187
+ const examples = ApiParser.getExamples(apiClass);
188
+ if (examples.length > 0) {
189
+ content += `## Examples\n\n`;
190
+ for (const example of examples) {
191
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
192
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
193
+ api: packageName,
194
+ blockType: "example"
195
+ });
196
+ if (prepared.isTypeScript) {
197
+ const displayCode = stripTwoslashDirectives(formattedCode);
198
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
199
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
200
+ }
201
+ }
202
+ const seeReferences = ApiParser.getSeeReferences(apiClass);
203
+ if (seeReferences.length > 0) {
204
+ content += `## See Also\n\n`;
205
+ for (const reference of seeReferences) {
206
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
207
+ content += `- ${refText}\n`;
208
+ }
209
+ content += `\n`;
210
+ }
211
+ return {
212
+ routePath: `${baseRoute}/class/${name.toLowerCase()}`,
213
+ content
214
+ };
215
+ }
216
+ /**
217
+ * Group class members by their type (static, instance, getters/setters)
218
+ */
219
+ groupClassMembers(members) {
220
+ const staticMethods = [];
221
+ const instanceMethods = [];
222
+ const getters = [];
223
+ for (const member of members) {
224
+ const isGetter = member.kind === "Method" && (member.displayName.startsWith("get ") || member.displayName.startsWith("set "));
225
+ const isStatic = member.isStatic === true;
226
+ if (isGetter) getters.push(member);
227
+ else if (isStatic) staticMethods.push(member);
228
+ else instanceMethods.push(member);
229
+ }
230
+ return {
231
+ staticMethods,
232
+ instanceMethods,
233
+ getters
234
+ };
235
+ }
236
+ /**
237
+ * Detect naming conflicts between class members and return prefixes to apply.
238
+ * Returns a Map of sanitized name to prefix (empty string if no conflict).
239
+ */
240
+ detectMemberConflicts(staticProps, staticMethods, instanceProps, instanceMethods, getters) {
241
+ const prefixMap = /* @__PURE__ */ new Map();
242
+ const staticPropNames = new Set(staticProps.map((p) => sanitizeId(p.displayName)));
243
+ const staticMethodNames = new Set(staticMethods.map((m) => sanitizeId(m.displayName)));
244
+ const instancePropNames = new Set(instanceProps.map((p) => sanitizeId(p.displayName)));
245
+ const instanceMethodNames = new Set(instanceMethods.map((m) => sanitizeId(m.displayName)));
246
+ const getterNames = new Set(getters.map((g) => sanitizeId(g.displayName)));
247
+ for (const name of staticPropNames) if (staticMethodNames.has(name) || instancePropNames.has(name) || instanceMethodNames.has(name) || getterNames.has(name)) prefixMap.set(name, "static-property");
248
+ for (const name of staticMethodNames) if (instanceMethodNames.has(name) || instancePropNames.has(name) || getterNames.has(name)) prefixMap.set(name, "static");
249
+ else if (staticPropNames.has(name)) prefixMap.set(name, "");
250
+ for (const name of instancePropNames) if (instanceMethodNames.has(name) || getterNames.has(name)) prefixMap.set(name, "property");
251
+ else if (staticMethodNames.has(name)) prefixMap.set(name, "");
252
+ for (const name of instanceMethodNames) if (!prefixMap.has(name)) prefixMap.set(name, "");
253
+ for (const name of getterNames) if (!prefixMap.has(name)) prefixMap.set(name, "");
254
+ return prefixMap;
255
+ }
256
+ /**
257
+ * Generate a class member signature with full class context
258
+ * Includes hidden imports with cut directive for external type resolution
259
+ * Uses the simplified approach: 3 lines (class opening, member, closing)
260
+ */
261
+ generateClassMemberWithContext(apiClass, targetMember, packageName) {
262
+ const className = apiClass.displayName;
263
+ const inheritance = ApiParser.getInheritance(apiClass);
264
+ let declaration = `class ${className}`;
265
+ if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
266
+ if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
267
+ declaration += " {";
268
+ const memberItem = targetMember;
269
+ const memberSignature = memberItem.excerpt?.text ? this.typeFormatter.format(memberItem.excerpt).trim() : "";
270
+ const skeleton = `${declaration}\n${memberSignature}\n}`;
271
+ const apiPackage = apiClass.getAssociatedPackage?.();
272
+ if (apiPackage) return prependHiddenImports(skeleton, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(targetMember));
273
+ return skeleton;
274
+ }
275
+ /**
276
+ * Generate a class skeleton for signature blocks
277
+ * Includes hidden imports with cut directive for external type resolution
278
+ */
279
+ generateClassSkeletonWithTwoslash(apiClass, packageName) {
280
+ const skeleton = this.generateClassSkeleton(apiClass);
281
+ const apiPackage = apiClass.getAssociatedPackage?.();
282
+ if (apiPackage) return prependHiddenImports(skeleton, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiClass));
283
+ return skeleton;
284
+ }
285
+ /**
286
+ * Generate a complete class skeleton showing all members
287
+ */
288
+ generateClassSkeleton(apiClass) {
289
+ const lines = [];
290
+ const className = apiClass.displayName;
291
+ const inheritance = ApiParser.getInheritance(apiClass);
292
+ let declaration = `class ${className}`;
293
+ if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
294
+ if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
295
+ declaration += " {";
296
+ lines.push(declaration);
297
+ const constructors = apiClass.members.filter((m) => m.kind === "Constructor");
298
+ if (constructors.length > 0) for (const ctor of constructors) {
299
+ const ctorItem = ctor;
300
+ if (ctorItem.excerpt?.text) {
301
+ const signature = this.typeFormatter.format(ctorItem.excerpt).trim();
302
+ lines.push(` ${signature}`);
303
+ }
304
+ }
305
+ const methods = apiClass.members.filter((m) => m.kind === "Method" || m.kind === "MethodSignature");
306
+ const grouped = this.groupClassMembers(methods);
307
+ const properties = apiClass.members.filter((m) => m.kind === "Property" || m.kind === "PropertySignature");
308
+ const staticProperties = properties.filter((m) => {
309
+ return m.isStatic === true;
310
+ });
311
+ if (staticProperties.length > 0) for (const prop of staticProperties) {
312
+ const propItem = prop;
313
+ if (propItem.excerpt?.text) {
314
+ const signature = this.typeFormatter.format(propItem.excerpt).trim();
315
+ lines.push(` ${signature}`);
316
+ }
317
+ }
318
+ if (grouped.staticMethods.length > 0) for (const method of grouped.staticMethods) {
319
+ const methodItem = method;
320
+ if (methodItem.excerpt?.text) {
321
+ const signature = this.typeFormatter.format(methodItem.excerpt).trim();
322
+ lines.push(` ${signature}`);
323
+ }
324
+ }
325
+ const instanceProperties = properties.filter((m) => {
326
+ const isStatic = m.isStatic === true;
327
+ const isGetter = m.displayName.startsWith("get ") || m.displayName.startsWith("set ");
328
+ return !isStatic && !isGetter;
329
+ });
330
+ if (instanceProperties.length > 0) for (const prop of instanceProperties) {
331
+ const propItem = prop;
332
+ if (propItem.excerpt?.text) {
333
+ const signature = this.typeFormatter.format(propItem.excerpt).trim();
334
+ lines.push(` ${signature}`);
335
+ }
336
+ }
337
+ if (grouped.getters.length > 0) for (const method of grouped.getters) {
338
+ const methodItem = method;
339
+ if (methodItem.excerpt?.text) {
340
+ const signature = this.typeFormatter.format(methodItem.excerpt).trim();
341
+ lines.push(` ${signature}`);
342
+ }
343
+ }
344
+ if (grouped.instanceMethods.length > 0) for (const method of grouped.instanceMethods) {
345
+ const methodItem = method;
346
+ if (methodItem.excerpt?.text) {
347
+ const signature = this.typeFormatter.format(methodItem.excerpt).trim();
348
+ lines.push(` ${signature}`);
349
+ }
350
+ }
351
+ lines.push("}");
352
+ return lines.join("\n");
353
+ }
354
+ };
355
+
356
+ //#endregion
357
+ export { ClassPageGenerator };
@@ -0,0 +1,152 @@
1
+ import { ApiParser } from "../../loader.js";
2
+ import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, stripTwoslashDirectives } from "../helpers.js";
3
+ import { markdownCrossLinker } from "../cross-linker.js";
4
+
5
+ //#region src/markdown/page-generators/enum-page.ts
6
+ /**
7
+ * Generates MDX documentation pages for TypeScript enums.
8
+ *
9
+ * This class transforms API Extractor enum models into rich MDX documentation
10
+ * pages with syntax-highlighted signatures, member tables, and cross-linked
11
+ * type references.
12
+ *
13
+ * **Page Structure:**
14
+ * 1. Frontmatter with title, description, and Open Graph metadata
15
+ * 2. Component imports
16
+ * 3. Page title (H1) and summary
17
+ * 4. Optional deprecation warning and release tag badge
18
+ * 5. Source code link toolbar
19
+ * 6. Enum signature block
20
+ * 7. Members table (Name, Value, Description)
21
+ * 8. Examples section with Twoslash-enabled code blocks
22
+ * 9. See Also references
23
+ *
24
+ * **Relationships:**
25
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
26
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
27
+ * - Uses {@link ApiParser} for extracting documentation from API models
28
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
29
+ *
30
+ * @see {@link TypeAliasPageGenerator} for type alias documentation
31
+ * @see {@link VariablePageGenerator} for variable/constant documentation
32
+ */
33
+ var EnumPageGenerator = class {
34
+ /**
35
+ * Generate a markdown page for an enum
36
+ *
37
+ * @param apiScope - API scope identifier for VFS lookup
38
+ */
39
+ async generate(apiEnum, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
40
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
41
+ const name = apiEnum.displayName;
42
+ const summary = ApiParser.getSummary(apiEnum) || "No description available.";
43
+ const releaseTag = ApiParser.getReleaseTag(apiEnum);
44
+ let content = generateFrontmatter(name, summary, singularName, apiName);
45
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
46
+ content += `import { EnumMembersTable } from "rspress-plugin-api-extractor/runtime";\n`;
47
+ content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
48
+ content += `# ${name}\n\n`;
49
+ const deprecation = ApiParser.getDeprecation(apiEnum);
50
+ if (deprecation) {
51
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
52
+ content += `> ⚠️ **Deprecated:** ${message}\n\n`;
53
+ }
54
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
55
+ content += `${summary}\n\n`;
56
+ content += generateAvailableFrom(packageName, availableFrom);
57
+ const sourceLink = ApiParser.getSourceLink(apiEnum, sourceConfig);
58
+ if (sourceLink) {
59
+ content += `<div className="api-docs-toolbar">\n`;
60
+ content += ` <div className="api-docs-toolbar-left">\n`;
61
+ content += ` <SourceCode href="${sourceLink}" />\n`;
62
+ content += ` </div>\n`;
63
+ if (llmsPlugin?.enabled) {
64
+ content += ` <div className="api-docs-toolbar-right">\n`;
65
+ content += ` </div>\n`;
66
+ }
67
+ content += `</div>\n\n`;
68
+ }
69
+ const skeleton = this.generateEnumSkeleton(apiEnum);
70
+ const hasMembers = apiEnum.members.length > 0;
71
+ if (skeleton) {
72
+ const displayCode = stripTwoslashDirectives(skeleton);
73
+ const signatureNewlines = hasMembers ? "\n" : "\n\n";
74
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(skeleton)}} apiScope={${JSON.stringify(apiScope)}} hasMembers={${hasMembers}} />${signatureNewlines}`;
75
+ }
76
+ if (hasMembers) {
77
+ const membersData = apiEnum.members.map((member) => {
78
+ const memberItem = member;
79
+ const memberSummary = ApiParser.getSummary(member) || "";
80
+ let value;
81
+ if (memberItem.excerpt?.text) {
82
+ const excerptText = memberItem.excerpt.text.trim();
83
+ const equalsIndex = excerptText.indexOf("=");
84
+ if (equalsIndex !== -1) value = excerptText.substring(equalsIndex + 1).trim().replace(/,\s*$/, "");
85
+ }
86
+ return {
87
+ name: member.displayName,
88
+ value,
89
+ description: markdownCrossLinker.addCrossLinks(memberSummary)
90
+ };
91
+ });
92
+ content += `<EnumMembersTable members={${JSON.stringify(membersData)}} />\n\n`;
93
+ }
94
+ const examples = ApiParser.getExamples(apiEnum);
95
+ if (examples.length > 0) {
96
+ content += `## Examples\n\n`;
97
+ for (const example of examples) {
98
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
99
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
100
+ api: packageName,
101
+ blockType: "example"
102
+ });
103
+ if (prepared.isTypeScript) {
104
+ const displayCode = stripTwoslashDirectives(formattedCode);
105
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
106
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
107
+ }
108
+ }
109
+ const seeReferences = ApiParser.getSeeReferences(apiEnum);
110
+ if (seeReferences.length > 0) {
111
+ content += `## See Also\n\n`;
112
+ for (const reference of seeReferences) {
113
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
114
+ content += `- ${refText}\n`;
115
+ }
116
+ content += `\n`;
117
+ }
118
+ return {
119
+ routePath: `${baseRoute}/enum/${name.toLowerCase()}`,
120
+ content
121
+ };
122
+ }
123
+ /**
124
+ * Generate a complete enum skeleton showing all members
125
+ */
126
+ generateEnumSkeleton(apiEnum) {
127
+ const lines = [];
128
+ const enumName = apiEnum.displayName;
129
+ lines.push(`enum ${enumName} {`);
130
+ const members = apiEnum.members;
131
+ for (let i = 0; i < members.length; i++) {
132
+ const member = members[i];
133
+ const memberItem = member;
134
+ let memberLine = ` ${member.displayName}`;
135
+ if (memberItem.excerpt?.text) {
136
+ const excerptText = memberItem.excerpt.text.trim();
137
+ const equalsIndex = excerptText.indexOf("=");
138
+ if (equalsIndex !== -1) {
139
+ const value = excerptText.substring(equalsIndex + 1).trim().replace(/,\s*$/, "");
140
+ memberLine += ` = ${value}`;
141
+ }
142
+ }
143
+ if (i < members.length - 1) memberLine += ",";
144
+ lines.push(memberLine);
145
+ }
146
+ lines.push("}");
147
+ return lines.join("\n");
148
+ }
149
+ };
150
+
151
+ //#endregion
152
+ export { EnumPageGenerator };
@@ -0,0 +1,127 @@
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
+
7
+ //#region src/markdown/page-generators/function-page.ts
8
+ /**
9
+ * Generates MDX documentation pages for TypeScript/JavaScript functions.
10
+ *
11
+ * This class transforms API Extractor function models into rich MDX documentation
12
+ * pages with syntax-highlighted signatures and cross-linked type references.
13
+ *
14
+ * **Page Structure:**
15
+ * 1. Frontmatter with title, description, and Open Graph metadata
16
+ * 2. Component imports
17
+ * 3. Page title (H1) and summary
18
+ * 4. Optional deprecation warning and release tag badge
19
+ * 5. Source code link toolbar
20
+ * 6. Function signature block
21
+ * 7. Parameters documentation
22
+ * 8. Returns documentation
23
+ * 9. Examples section with Twoslash-enabled code blocks
24
+ * 10. See Also references
25
+ *
26
+ * **Relationships:**
27
+ * - Created and invoked by {@link ApiExtractorPlugin} during page generation
28
+ * - Uses {@link TypeSignatureFormatter} for formatting type signatures
29
+ * - Uses {@link ApiParser} for extracting documentation from API models
30
+ * - Uses {@link MarkdownCrossLinker} for adding type reference links
31
+ *
32
+ * @see {@link ClassPageGenerator} for class documentation
33
+ * @see {@link TypeAliasPageGenerator} for type alias documentation
34
+ */
35
+ var FunctionPageGenerator = class {
36
+ typeFormatter = new TypeSignatureFormatter();
37
+ /**
38
+ * Generate a markdown page for a function
39
+ *
40
+ * @param apiScope - API scope identifier for VFS lookup
41
+ */
42
+ async generate(apiFunction, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
43
+ const shouldSuppressErrors = suppressExampleErrors ?? true;
44
+ const name = apiFunction.displayName;
45
+ const summary = ApiParser.getSummary(apiFunction) || "No description available.";
46
+ const releaseTag = ApiParser.getReleaseTag(apiFunction);
47
+ let content = generateFrontmatter(name, summary, singularName, apiName);
48
+ content += `import { SourceCode } from "@rspress/core/theme";\n`;
49
+ content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
50
+ content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
51
+ content += `# ${name}\n\n`;
52
+ const deprecation = ApiParser.getDeprecation(apiFunction);
53
+ if (deprecation) {
54
+ const message = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(deprecation.message));
55
+ content += `> ⚠️ **Deprecated:** ${message}\n\n`;
56
+ }
57
+ if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
58
+ content += `${summary}\n\n`;
59
+ content += generateAvailableFrom(packageName, availableFrom);
60
+ const sourceLink = ApiParser.getSourceLink(apiFunction, sourceConfig);
61
+ if (sourceLink) {
62
+ content += `<div className="api-docs-toolbar">\n`;
63
+ content += ` <div className="api-docs-toolbar-left">\n`;
64
+ content += ` <SourceCode href="${sourceLink}" />\n`;
65
+ content += ` </div>\n`;
66
+ if (llmsPlugin?.enabled) {
67
+ content += ` <div className="api-docs-toolbar-right">\n`;
68
+ content += ` </div>\n`;
69
+ }
70
+ content += `</div>\n\n`;
71
+ }
72
+ const params = ApiParser.getParams(apiFunction);
73
+ const hasParameters = params.length > 0;
74
+ if (apiFunction.excerpt.text) {
75
+ const signature = this.typeFormatter.format(apiFunction.excerpt).trim();
76
+ let signatureWithImports = signature;
77
+ const apiPackage = apiFunction.getAssociatedPackage?.();
78
+ if (apiPackage) signatureWithImports = prependHiddenImports(signature, new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiFunction));
79
+ const displayCode = stripTwoslashDirectives(signatureWithImports);
80
+ content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} hasParameters={${hasParameters}} />\n\n`;
81
+ }
82
+ if (hasParameters) {
83
+ const parametersData = params.map((param) => ({
84
+ name: param.name,
85
+ type: param.type,
86
+ description: escapeMdxGenerics(markdownCrossLinker.addCrossLinks(param.description))
87
+ }));
88
+ content += `<ParametersTable parameters={${JSON.stringify(parametersData)}} />\n\n`;
89
+ }
90
+ const returns = ApiParser.getReturns(apiFunction);
91
+ if (returns) {
92
+ const description = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(returns.description));
93
+ content += `## Returns\n\n${description}\n\n`;
94
+ }
95
+ const examples = ApiParser.getExamples(apiFunction);
96
+ if (examples.length > 0) {
97
+ content += `## Examples\n\n`;
98
+ for (const example of examples) {
99
+ const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
100
+ const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
101
+ api: packageName,
102
+ blockType: "example"
103
+ });
104
+ if (prepared.isTypeScript) {
105
+ const displayCode = stripTwoslashDirectives(formattedCode);
106
+ content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
107
+ } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
108
+ }
109
+ }
110
+ const seeReferences = ApiParser.getSeeReferences(apiFunction);
111
+ if (seeReferences.length > 0) {
112
+ content += `## See Also\n\n`;
113
+ for (const reference of seeReferences) {
114
+ const refText = escapeMdxGenerics(markdownCrossLinker.addCrossLinks(reference.text));
115
+ content += `- ${refText}\n`;
116
+ }
117
+ content += `\n`;
118
+ }
119
+ return {
120
+ routePath: `${baseRoute}/function/${name.toLowerCase()}`,
121
+ content
122
+ };
123
+ }
124
+ };
125
+
126
+ //#endregion
127
+ export { FunctionPageGenerator };
@@ -0,0 +1,27 @@
1
+ import { escapeYamlString } from "../helpers.js";
2
+
3
+ //#region src/markdown/page-generators/index-pages.ts
4
+ /**
5
+ * Generator for main API index page with only frontmatter (no content)
6
+ */
7
+ var MainIndexPageGenerator = class {
8
+ /**
9
+ * Generate the main API index page
10
+ */
11
+ generate(packageName, baseRoute, _categoryCounts) {
12
+ const content = `---
13
+ title: API Reference
14
+ description: Auto-generated API documentation for ${escapeYamlString(packageName)}
15
+ overview: true
16
+ ---
17
+
18
+ `;
19
+ return {
20
+ routePath: `${baseRoute}/index`,
21
+ content
22
+ };
23
+ }
24
+ };
25
+
26
+ //#endregion
27
+ export { MainIndexPageGenerator };