rspress-plugin-api-extractor 0.13.3 → 0.15.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/BuildEnv.js +0 -1
- package/build-program.js +5 -5
- package/build-stages.js +98 -282
- package/config-helpers.js +1 -1
- package/emit/mdx.js +311 -0
- package/emit/meta.js +62 -0
- package/index.d.ts +6 -70
- package/layers/build-metrics.js +1 -2
- package/layers/config-resolution.js +5 -8
- package/layers/type-environment.js +5 -4
- package/llms-program.js +3 -3
- package/markdown/helpers.js +10 -177
- package/observability/sinks/console-sink.js +1 -3
- package/observability/sinks/metrics-sink.js +0 -2
- package/package.json +9 -7
- package/path-derivation.js +1 -29
- package/plugin.js +3 -10
- package/prettier-formatter.js +27 -59
- package/remark-with-api.js +3 -2
- package/schemas/config.js +3 -29
- package/schemas/observability.js +8 -23
- package/schemas/performance.js +1 -7
- package/services/TwoslashCacheService.js +18 -14
- package/services/TypeRegistryService.js +4 -4
- package/shiki-transformer.js +12 -51
- package/twoslash-transformer.js +16 -49
- package/api-extracted-package.js +0 -471
- package/code-post-processor.js +0 -38
- package/frontmatter.js +0 -176
- package/llms-processing.js +0 -270
- package/markdown/page-generators/class-page.js +0 -364
- package/markdown/page-generators/enum-page.js +0 -152
- package/markdown/page-generators/function-page.js +0 -128
- package/markdown/page-generators/index-pages.js +0 -25
- package/markdown/page-generators/interface-page.js +0 -311
- package/markdown/page-generators/namespace-page.js +0 -278
- package/markdown/page-generators/type-alias-page.js +0 -111
- package/markdown/page-generators/variable-page.js +0 -111
- package/markdown/prose-linker.js +0 -22
- package/tsconfig-parser.js +0 -115
- package/twoslash-cache.js +0 -174
- package/twoslash-patterns.js +0 -87
- package/type-reference-extractor.js +0 -199
- package/typescript-config.js +0 -170
package/llms-processing.js
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
//#region src/llms-processing.ts
|
|
2
|
-
/** Pre-compiled regex for parsing llms.txt link lines. */
|
|
3
|
-
const LLMS_TXT_LINE_RE = /^-\s+\[([^\]]+)\]\(([^)]+)\)(?::\s*(.+))?$/;
|
|
4
|
-
/**
|
|
5
|
-
* Parse a single line from llms.txt format.
|
|
6
|
-
*
|
|
7
|
-
* Recognizes the pattern: `- [title](url): description`
|
|
8
|
-
* The description portion (`: description`) is optional.
|
|
9
|
-
*
|
|
10
|
-
* @param line - A single line from an llms.txt file
|
|
11
|
-
* @returns Parsed entry or null for non-link lines (headers, empty lines, plain text)
|
|
12
|
-
*/
|
|
13
|
-
function parseLlmsTxtLine(line) {
|
|
14
|
-
const trimmed = line.trim();
|
|
15
|
-
if (trimmed === "") return null;
|
|
16
|
-
const match = LLMS_TXT_LINE_RE.exec(trimmed);
|
|
17
|
-
if (!match) return null;
|
|
18
|
-
const title = match[1];
|
|
19
|
-
const url = match[2];
|
|
20
|
-
const rawDescription = match[3];
|
|
21
|
-
return {
|
|
22
|
-
title,
|
|
23
|
-
url,
|
|
24
|
-
description: rawDescription ? rawDescription.trim() : void 0
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Filter API page entries from global llms.txt content.
|
|
29
|
-
*
|
|
30
|
-
* Removes lines whose parsed URL is in the `apiRoutes` set.
|
|
31
|
-
* Appends pointer lines for per-package llms files when `pointers` is non-empty.
|
|
32
|
-
*
|
|
33
|
-
* @param content - Full llms.txt content string
|
|
34
|
-
* @param apiRoutes - Set of API route paths to remove
|
|
35
|
-
* @param pointers - Per-package pointer entries to append
|
|
36
|
-
* @returns Filtered llms.txt content
|
|
37
|
-
*/
|
|
38
|
-
function filterLlmsTxt(content, apiRoutes, pointers) {
|
|
39
|
-
const lines = content.split("\n");
|
|
40
|
-
const filtered = [];
|
|
41
|
-
for (const line of lines) {
|
|
42
|
-
const entry = parseLlmsTxtLine(line);
|
|
43
|
-
if (entry && apiRoutes.has(entry.url)) continue;
|
|
44
|
-
filtered.push(line);
|
|
45
|
-
}
|
|
46
|
-
let result = filtered.join("\n");
|
|
47
|
-
if (pointers.length > 0) {
|
|
48
|
-
result += "\n\n";
|
|
49
|
-
for (const pointer of pointers) result += `- For ${pointer.name} API docs, see [${pointer.name} llms.txt](${pointer.llmsTxtUrl})\n`;
|
|
50
|
-
}
|
|
51
|
-
return result;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Generate a structured global llms.txt that groups pages by package scope.
|
|
55
|
-
*
|
|
56
|
-
* Output format:
|
|
57
|
-
* ```
|
|
58
|
-
* # {site title}
|
|
59
|
-
*
|
|
60
|
-
* ## Others
|
|
61
|
-
* - [Blog Post](/blog/post.md)
|
|
62
|
-
*
|
|
63
|
-
* ## Packages
|
|
64
|
-
*
|
|
65
|
-
* ### {name} {version}
|
|
66
|
-
* {description}
|
|
67
|
-
* - [Guide Page](/pkg/guides/guide.md)
|
|
68
|
-
* - [API Reference](/pkg/llms-api.txt)
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* @param content - Original RSPress-generated llms.txt content
|
|
72
|
-
* @param apiRoutes - Set of API route paths to exclude as individual entries
|
|
73
|
-
* @param packages - Package scope metadata
|
|
74
|
-
* @returns Restructured llms.txt content
|
|
75
|
-
*/
|
|
76
|
-
function generateStructuredLlmsTxt(content, apiRoutes, packages) {
|
|
77
|
-
const lines = content.split("\n");
|
|
78
|
-
let title = "";
|
|
79
|
-
for (const line of lines) if (line.startsWith("# ")) {
|
|
80
|
-
title = line;
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
const allEntries = [];
|
|
84
|
-
for (const line of lines) {
|
|
85
|
-
const entry = parseLlmsTxtLine(line);
|
|
86
|
-
if (entry && !apiRoutes.has(entry.url)) allEntries.push(entry);
|
|
87
|
-
}
|
|
88
|
-
const packageEntries = /* @__PURE__ */ new Map();
|
|
89
|
-
const others = [];
|
|
90
|
-
for (const entry of allEntries) {
|
|
91
|
-
let matched = false;
|
|
92
|
-
for (const pkg of packages) {
|
|
93
|
-
const base = pkg.packageRoute.endsWith("/") ? pkg.packageRoute : `${pkg.packageRoute}/`;
|
|
94
|
-
if (entry.url.startsWith(base) || entry.url === pkg.packageRoute) {
|
|
95
|
-
const existing = packageEntries.get(pkg.packageName) ?? [];
|
|
96
|
-
existing.push(entry);
|
|
97
|
-
packageEntries.set(pkg.packageName, existing);
|
|
98
|
-
matched = true;
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (!matched) others.push(entry);
|
|
103
|
-
}
|
|
104
|
-
const output = [];
|
|
105
|
-
if (title) {
|
|
106
|
-
output.push(title);
|
|
107
|
-
output.push("");
|
|
108
|
-
}
|
|
109
|
-
if (others.length > 0) {
|
|
110
|
-
output.push("## Others");
|
|
111
|
-
output.push("");
|
|
112
|
-
for (const entry of others) output.push(formatEntry(entry));
|
|
113
|
-
output.push("");
|
|
114
|
-
}
|
|
115
|
-
const packagesWithEntries = packages;
|
|
116
|
-
if (packagesWithEntries.length > 0) {
|
|
117
|
-
output.push("## Packages");
|
|
118
|
-
output.push("");
|
|
119
|
-
for (const pkg of packagesWithEntries) {
|
|
120
|
-
const versionSuffix = pkg.version ? ` ${pkg.version}` : "";
|
|
121
|
-
output.push(`### ${pkg.name}${versionSuffix}`);
|
|
122
|
-
output.push("");
|
|
123
|
-
if (pkg.description) {
|
|
124
|
-
output.push(pkg.description);
|
|
125
|
-
output.push("");
|
|
126
|
-
}
|
|
127
|
-
const entries = packageEntries.get(pkg.packageName) ?? [];
|
|
128
|
-
for (const entry of entries) output.push(formatEntry(entry));
|
|
129
|
-
output.push(`- [API Reference](${pkg.llmsApiTxtUrl})`);
|
|
130
|
-
output.push("");
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return output.join("\n");
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Parse llms-full.txt content into sections delimited by frontmatter blocks.
|
|
137
|
-
*
|
|
138
|
-
* Each section has the format:
|
|
139
|
-
* ```
|
|
140
|
-
* ---
|
|
141
|
-
* url: /path/to/page
|
|
142
|
-
* ---
|
|
143
|
-
*
|
|
144
|
-
* Content here...
|
|
145
|
-
* ```
|
|
146
|
-
*/
|
|
147
|
-
function parseSections(content) {
|
|
148
|
-
if (content.trim() === "") return [];
|
|
149
|
-
const sections = [];
|
|
150
|
-
const frontmatterPattern = /^---\nurl:\s*(.+)\n---$/gm;
|
|
151
|
-
let match = frontmatterPattern.exec(content);
|
|
152
|
-
const boundaries = [];
|
|
153
|
-
while (match !== null) {
|
|
154
|
-
boundaries.push({
|
|
155
|
-
url: match[1].trim(),
|
|
156
|
-
start: match.index,
|
|
157
|
-
fmEnd: match.index + match[0].length
|
|
158
|
-
});
|
|
159
|
-
match = frontmatterPattern.exec(content);
|
|
160
|
-
}
|
|
161
|
-
for (let i = 0; i < boundaries.length; i++) {
|
|
162
|
-
const boundary = boundaries[i];
|
|
163
|
-
const nextStart = i + 1 < boundaries.length ? boundaries[i + 1].start : content.length;
|
|
164
|
-
const sectionContent = content.slice(boundary.start, nextStart);
|
|
165
|
-
sections.push({
|
|
166
|
-
url: boundary.url,
|
|
167
|
-
raw: sectionContent.trimEnd()
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
return sections;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Filter API page content sections from global llms-full.txt.
|
|
174
|
-
*
|
|
175
|
-
* Sections are delimited by `---\nurl: {path}\n---` frontmatter blocks.
|
|
176
|
-
* Removes entire sections whose URL matches a known API route.
|
|
177
|
-
*
|
|
178
|
-
* @param content - Full llms-full.txt content string
|
|
179
|
-
* @param apiRoutes - Set of API route paths to remove
|
|
180
|
-
* @returns Filtered llms-full.txt content
|
|
181
|
-
*/
|
|
182
|
-
function filterLlmsFullTxt(content, apiRoutes) {
|
|
183
|
-
if (content.trim() === "") return "";
|
|
184
|
-
const kept = parseSections(content).filter((section) => !apiRoutes.has(section.url));
|
|
185
|
-
if (kept.length === 0) return "";
|
|
186
|
-
return kept.map((section) => section.raw).join("\n\n\n");
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Format a single llms.txt link entry.
|
|
190
|
-
*/
|
|
191
|
-
function formatEntry(entry) {
|
|
192
|
-
if (entry.description) return `- [${entry.title}](${entry.url}): ${entry.description}`;
|
|
193
|
-
return `- [${entry.title}](${entry.url})`;
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Generate a per-package llms.txt index.
|
|
197
|
-
*
|
|
198
|
-
* Output format:
|
|
199
|
-
* ```
|
|
200
|
-
* # {name}
|
|
201
|
-
*
|
|
202
|
-
* ## Guides
|
|
203
|
-
*
|
|
204
|
-
* - [Guide Title](/path): Description
|
|
205
|
-
*
|
|
206
|
-
* ## API Reference
|
|
207
|
-
*
|
|
208
|
-
* - [ApiItem](/path): Description
|
|
209
|
-
* ```
|
|
210
|
-
*
|
|
211
|
-
* Sections with no entries are omitted.
|
|
212
|
-
*
|
|
213
|
-
* @param input - Package name, guide pages, and API pages
|
|
214
|
-
* @returns Generated llms.txt content
|
|
215
|
-
*/
|
|
216
|
-
function generatePackageLlmsTxt(input) {
|
|
217
|
-
const parts = [
|
|
218
|
-
`# ${input.name}`,
|
|
219
|
-
"",
|
|
220
|
-
`> API documentation for the ${input.packageName} package`
|
|
221
|
-
];
|
|
222
|
-
if (input.guidePages.length > 0) {
|
|
223
|
-
parts.push("");
|
|
224
|
-
parts.push("## Guides");
|
|
225
|
-
parts.push("");
|
|
226
|
-
for (const page of input.guidePages) parts.push(formatEntry(page));
|
|
227
|
-
}
|
|
228
|
-
if (input.apiPages.length > 0) {
|
|
229
|
-
parts.push("");
|
|
230
|
-
parts.push("## API Reference");
|
|
231
|
-
parts.push("");
|
|
232
|
-
for (const page of input.apiPages) parts.push(formatEntry(page));
|
|
233
|
-
}
|
|
234
|
-
parts.push("");
|
|
235
|
-
return parts.join("\n");
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Concatenate page contents with frontmatter delimiters.
|
|
239
|
-
*
|
|
240
|
-
* Used for llms-full.txt, llms-docs.txt, and llms-api.txt generation
|
|
241
|
-
* (pass different page sets for each).
|
|
242
|
-
*
|
|
243
|
-
* Output format:
|
|
244
|
-
* ```
|
|
245
|
-
* ---
|
|
246
|
-
* url: /path/to/page
|
|
247
|
-
* ---
|
|
248
|
-
*
|
|
249
|
-
* Content here...
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* ---
|
|
253
|
-
* url: /path/to/next
|
|
254
|
-
* ---
|
|
255
|
-
*
|
|
256
|
-
* More content...
|
|
257
|
-
* ```
|
|
258
|
-
*
|
|
259
|
-
* @param pages - Array of page URLs and their markdown content
|
|
260
|
-
* @returns Concatenated content with frontmatter delimiters
|
|
261
|
-
*/
|
|
262
|
-
function generatePackageLlmsFullTxt(pages) {
|
|
263
|
-
if (pages.length === 0) return "";
|
|
264
|
-
const sections = [];
|
|
265
|
-
for (const page of pages) sections.push(`---\nurl: ${page.url}\n---\n\n${page.content}`);
|
|
266
|
-
return sections.join("\n\n\n");
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
//#endregion
|
|
270
|
-
export { filterLlmsFullTxt, filterLlmsTxt, generatePackageLlmsFullTxt, generatePackageLlmsTxt, generateStructuredLlmsTxt, parseLlmsTxtLine };
|
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
import { TypeReferenceExtractor } from "../../type-reference-extractor.js";
|
|
2
|
-
import { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives } from "../helpers.js";
|
|
3
|
-
import { linkProse } from "../prose-linker.js";
|
|
4
|
-
import { ApiItems, Routes, Signature, Tsdoc } from "@tsdoctor/model";
|
|
5
|
-
|
|
6
|
-
//#region src/markdown/page-generators/class-page.ts
|
|
7
|
-
/**
|
|
8
|
-
* Generates MDX documentation pages for TypeScript/JavaScript classes.
|
|
9
|
-
*
|
|
10
|
-
* This class transforms API Extractor class models into rich MDX documentation pages
|
|
11
|
-
* with syntax-highlighted signatures, cross-linked type references, and interactive
|
|
12
|
-
* features like copy-to-clipboard and line wrapping.
|
|
13
|
-
*
|
|
14
|
-
* **Page Structure:**
|
|
15
|
-
* 1. Frontmatter with title, description, and Open Graph metadata
|
|
16
|
-
* 2. Component imports (SourceCode, ParametersTable, ApiSignature, etc.)
|
|
17
|
-
* 3. Page title (H1) and summary
|
|
18
|
-
* 4. Optional deprecation warning and release tag badge
|
|
19
|
-
* 5. Source code link toolbar
|
|
20
|
-
* 6. Full class signature block showing all members
|
|
21
|
-
* 7. Member sections: Constructors, Static Properties, Static Methods, Properties, Getters/Setters, Methods
|
|
22
|
-
* 8. Examples section with Twoslash-enabled code blocks
|
|
23
|
-
* 9. See Also references
|
|
24
|
-
*
|
|
25
|
-
* **Member Rendering:**
|
|
26
|
-
* Each member is rendered with:
|
|
27
|
-
* - An `ApiMember` component showing the signature in class context
|
|
28
|
-
* - Optional `ParametersTable` for methods with parameters
|
|
29
|
-
* - Return type documentation
|
|
30
|
-
* - Cross-linked type references
|
|
31
|
-
*
|
|
32
|
-
* **Relationships:**
|
|
33
|
-
* - Created and invoked by {@link ApiExtractorPlugin} during page generation
|
|
34
|
-
* - Uses `Signature.format` from `@tsdoctor/model` for formatting type signatures
|
|
35
|
-
* - Uses the `Tsdoc` / `ApiItems` modules from `@tsdoctor/model` for extracting documentation
|
|
36
|
-
* - Uses the per-build prose linker (`linkProse`) for adding type reference links
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* const generator = new ClassPageGenerator();
|
|
41
|
-
* const { routePath, content } = await generator.generate(
|
|
42
|
-
* apiClass,
|
|
43
|
-
* "/api/my-package",
|
|
44
|
-
* "my-package",
|
|
45
|
-
* "Class",
|
|
46
|
-
* "My Package",
|
|
47
|
-
* sourceConfig,
|
|
48
|
-
* true, // suppressExampleErrors
|
|
49
|
-
* undefined, // llmsPlugin
|
|
50
|
-
* "claude-binary-plugin", // apiScope
|
|
51
|
-
* );
|
|
52
|
-
* ```
|
|
53
|
-
*
|
|
54
|
-
* @see {@link InterfacePageGenerator} for interface documentation
|
|
55
|
-
* @see {@link FunctionPageGenerator} for function documentation
|
|
56
|
-
*/
|
|
57
|
-
var ClassPageGenerator = class {
|
|
58
|
-
/**
|
|
59
|
-
* Generate a markdown page for a class
|
|
60
|
-
*
|
|
61
|
-
* @param apiScope - API scope identifier for VFS lookup
|
|
62
|
-
*/
|
|
63
|
-
async generate(apiClass, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom, syntheticBase, memberAnchors) {
|
|
64
|
-
const shouldSuppressErrors = suppressExampleErrors ?? true;
|
|
65
|
-
const name = apiClass.displayName;
|
|
66
|
-
const summary = Tsdoc.summary(apiClass) || "No description available.";
|
|
67
|
-
const releaseTag = Tsdoc.releaseTag(apiClass);
|
|
68
|
-
let content = generateFrontmatter(name, summary, singularName, apiName);
|
|
69
|
-
content += `import { SourceCode } from "@rspress/core/theme";\n`;
|
|
70
|
-
content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
|
|
71
|
-
content += `import { ApiSignature, ApiMember, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
|
|
72
|
-
content += `# ${name}\n\n`;
|
|
73
|
-
const deprecation = Tsdoc.deprecation(apiClass);
|
|
74
|
-
if (deprecation) {
|
|
75
|
-
const message = escapeMdxGenerics(linkProse(deprecation.message));
|
|
76
|
-
content += `> ⚠️ **Deprecated:** ${message}\n\n`;
|
|
77
|
-
}
|
|
78
|
-
if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
|
|
79
|
-
content += `${summary}\n\n`;
|
|
80
|
-
content += generateAvailableFrom(packageName, availableFrom);
|
|
81
|
-
const sourceLink = ApiItems.sourceLink(apiClass, sourceConfig);
|
|
82
|
-
if (sourceLink) {
|
|
83
|
-
content += `<div className="api-docs-toolbar">\n`;
|
|
84
|
-
content += ` <div className="api-docs-toolbar-left">\n`;
|
|
85
|
-
content += ` <SourceCode href="${sourceLink}" />\n`;
|
|
86
|
-
content += ` </div>\n`;
|
|
87
|
-
if (llmsPlugin?.enabled) {
|
|
88
|
-
content += ` <div className="api-docs-toolbar-right">\n`;
|
|
89
|
-
content += ` </div>\n`;
|
|
90
|
-
}
|
|
91
|
-
content += `</div>\n\n`;
|
|
92
|
-
}
|
|
93
|
-
const skeleton = this.generateClassSkeletonWithTwoslash(apiClass, packageName);
|
|
94
|
-
const displayCode = stripTwoslashDirectives(skeleton);
|
|
95
|
-
content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(skeleton)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
96
|
-
content += this.generateBaseClassSection(apiClass, syntheticBase, packageName, apiScope);
|
|
97
|
-
const constructors = apiClass.members.filter((m) => m.kind === "Constructor");
|
|
98
|
-
if (constructors.length > 0) {
|
|
99
|
-
content += `## Constructors\n\n`;
|
|
100
|
-
for (const ctor of constructors) {
|
|
101
|
-
const ctorSummary = Tsdoc.summary(ctor);
|
|
102
|
-
const ctorId = Routes.memberAnchor("constructor");
|
|
103
|
-
const ctorItem = ctor;
|
|
104
|
-
const params = Tsdoc.params(ctor);
|
|
105
|
-
const hasParameters = params.length > 0;
|
|
106
|
-
if (ctorItem.excerpt?.text) {
|
|
107
|
-
const memberSignature = Signature.format(ctorItem.excerpt).trim();
|
|
108
|
-
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, ctor, packageName);
|
|
109
|
-
const summaryMd = ctorSummary ? escapeMdxGenerics(linkProse(ctorSummary)) : void 0;
|
|
110
|
-
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`;
|
|
111
|
-
}
|
|
112
|
-
if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
|
|
113
|
-
name: p.name,
|
|
114
|
-
type: p.type,
|
|
115
|
-
description: linkProse(p.description)
|
|
116
|
-
})))}} />\n\n`;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const properties = apiClass.members.filter((m) => m.kind === "Property" || m.kind === "PropertySignature");
|
|
120
|
-
const methods = apiClass.members.filter((m) => m.kind === "Method" || m.kind === "MethodSignature");
|
|
121
|
-
const grouped = methods.length > 0 ? this.groupClassMembers(methods) : {
|
|
122
|
-
staticMethods: [],
|
|
123
|
-
instanceMethods: [],
|
|
124
|
-
getters: []
|
|
125
|
-
};
|
|
126
|
-
const staticProperties = properties.filter((m) => {
|
|
127
|
-
return m.isStatic === true;
|
|
128
|
-
});
|
|
129
|
-
const instanceProperties = properties.filter((m) => {
|
|
130
|
-
const isStatic = m.isStatic === true;
|
|
131
|
-
const isGetter = m.displayName.startsWith("get ") || m.displayName.startsWith("set ");
|
|
132
|
-
return !isStatic && !isGetter;
|
|
133
|
-
});
|
|
134
|
-
const anchors = memberAnchors ?? ApiItems.memberAnchors(apiClass);
|
|
135
|
-
const anchorFor = (member) => anchors.get(member.canonicalReference?.toString() ?? member.displayName) ?? Routes.memberAnchor(member.displayName);
|
|
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 = Tsdoc.summary(prop);
|
|
141
|
-
const propId = anchorFor(prop);
|
|
142
|
-
const propItem = prop;
|
|
143
|
-
if (propItem.excerpt?.text) {
|
|
144
|
-
const memberSignature = Signature.format(propItem.excerpt).trim();
|
|
145
|
-
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, prop, packageName);
|
|
146
|
-
const summaryMd = propSummary ? escapeMdxGenerics(linkProse(propSummary)) : void 0;
|
|
147
|
-
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`;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
const renderMethods = async (title, methodList) => {
|
|
152
|
-
if (methodList.length === 0) return;
|
|
153
|
-
content += `## ${title}\n\n`;
|
|
154
|
-
for (const method of methodList) {
|
|
155
|
-
const methodSummary = Tsdoc.summary(method);
|
|
156
|
-
const methodId = anchorFor(method);
|
|
157
|
-
const methodItem = method;
|
|
158
|
-
const params = Tsdoc.params(method);
|
|
159
|
-
const hasParameters = params.length > 0;
|
|
160
|
-
if (methodItem.excerpt?.text) {
|
|
161
|
-
const memberSignature = Signature.format(methodItem.excerpt).trim();
|
|
162
|
-
const skeletonWithContext = this.generateClassMemberWithContext(apiClass, method, packageName);
|
|
163
|
-
const summaryMd = methodSummary ? escapeMdxGenerics(linkProse(methodSummary)) : void 0;
|
|
164
|
-
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`;
|
|
165
|
-
}
|
|
166
|
-
if (hasParameters) content += `<ParametersTable parameters={${JSON.stringify(params.map((p) => ({
|
|
167
|
-
name: p.name,
|
|
168
|
-
type: p.type,
|
|
169
|
-
description: linkProse(p.description)
|
|
170
|
-
})))}} />\n\n`;
|
|
171
|
-
const returns = Tsdoc.returns(method);
|
|
172
|
-
if (returns) {
|
|
173
|
-
const description = escapeMdxGenerics(linkProse(returns.description));
|
|
174
|
-
content += `**Returns:** ${description}\n\n`;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
};
|
|
178
|
-
await renderProperties("Static Properties", staticProperties);
|
|
179
|
-
await renderMethods("Static Methods", grouped.staticMethods);
|
|
180
|
-
await renderProperties("Properties", instanceProperties);
|
|
181
|
-
await renderMethods("Getters & Setters", grouped.getters);
|
|
182
|
-
await renderMethods("Methods", grouped.instanceMethods);
|
|
183
|
-
const examples = Tsdoc.examples(apiClass);
|
|
184
|
-
if (examples.length > 0) {
|
|
185
|
-
content += `## Examples\n\n`;
|
|
186
|
-
for (const example of examples) {
|
|
187
|
-
const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
|
|
188
|
-
const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
|
|
189
|
-
api: packageName,
|
|
190
|
-
blockType: "example"
|
|
191
|
-
});
|
|
192
|
-
if (prepared.isTypeScript) {
|
|
193
|
-
const displayCode = stripTwoslashDirectives(formattedCode);
|
|
194
|
-
content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
195
|
-
} else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
const seeReferences = Tsdoc.seeReferences(apiClass);
|
|
199
|
-
if (seeReferences.length > 0) {
|
|
200
|
-
content += `## See Also\n\n`;
|
|
201
|
-
for (const reference of seeReferences) {
|
|
202
|
-
const refText = escapeMdxGenerics(linkProse(reference.text));
|
|
203
|
-
content += `- ${refText}\n`;
|
|
204
|
-
}
|
|
205
|
-
content += `\n`;
|
|
206
|
-
}
|
|
207
|
-
return {
|
|
208
|
-
routePath: `${baseRoute}/class/${name.toLowerCase()}`,
|
|
209
|
-
content
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Render the inline "Base Class" section for a synthetic base declaration
|
|
214
|
-
* (an unexported item referenced by the class's extends clause, e.g. the
|
|
215
|
-
* `Foo_base` variable TypeScript emits for `Schema.Class`-style patterns).
|
|
216
|
-
*
|
|
217
|
-
* The `## Base Class` heading slugs to `BASE_CLASS_ANCHOR` from
|
|
218
|
-
* `@tsdoctor/model`'s `SyntheticBases`, which is where the cross-link
|
|
219
|
-
* route for the base name points.
|
|
220
|
-
*/
|
|
221
|
-
generateBaseClassSection(apiClass, syntheticBase, packageName, apiScope) {
|
|
222
|
-
const baseDecl = syntheticBase;
|
|
223
|
-
if (!baseDecl?.excerpt?.text) return "";
|
|
224
|
-
let section = `## Base Class\n\n`;
|
|
225
|
-
section += `\`${apiClass.displayName}\` extends \`${baseDecl.displayName}\`, a compiler-generated declaration that is not exported from \`${packageName}\`.\n\n`;
|
|
226
|
-
const signature = Signature.format(baseDecl.excerpt).trim();
|
|
227
|
-
let source = signature;
|
|
228
|
-
const apiPackage = apiClass.getAssociatedPackage?.();
|
|
229
|
-
if (apiPackage) {
|
|
230
|
-
const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(baseDecl);
|
|
231
|
-
source = prependHiddenImports(signature, imports);
|
|
232
|
-
}
|
|
233
|
-
const displayCode = stripTwoslashDirectives(source);
|
|
234
|
-
section += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(source)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
|
|
235
|
-
return section;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Group class members by their type (static, instance, getters/setters)
|
|
239
|
-
*/
|
|
240
|
-
groupClassMembers(members) {
|
|
241
|
-
const staticMethods = [];
|
|
242
|
-
const instanceMethods = [];
|
|
243
|
-
const getters = [];
|
|
244
|
-
for (const member of members) {
|
|
245
|
-
const isGetter = member.kind === "Method" && (member.displayName.startsWith("get ") || member.displayName.startsWith("set "));
|
|
246
|
-
const isStatic = member.isStatic === true;
|
|
247
|
-
if (isGetter) getters.push(member);
|
|
248
|
-
else if (isStatic) staticMethods.push(member);
|
|
249
|
-
else instanceMethods.push(member);
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
staticMethods,
|
|
253
|
-
instanceMethods,
|
|
254
|
-
getters
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Generate a class member signature with full class context
|
|
259
|
-
* Includes hidden imports with cut directive for external type resolution
|
|
260
|
-
* Uses the simplified approach: 3 lines (class opening, member, closing)
|
|
261
|
-
*/
|
|
262
|
-
generateClassMemberWithContext(apiClass, targetMember, packageName) {
|
|
263
|
-
const className = apiClass.displayName;
|
|
264
|
-
const inheritance = ApiItems.inheritance(apiClass);
|
|
265
|
-
let declaration = `class ${className}`;
|
|
266
|
-
if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
|
|
267
|
-
if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
|
|
268
|
-
declaration += " {";
|
|
269
|
-
const memberItem = targetMember;
|
|
270
|
-
const memberSignature = memberItem.excerpt?.text ? Signature.format(memberItem.excerpt).trim() : "";
|
|
271
|
-
const skeleton = `${declaration}\n${memberSignature}\n}`;
|
|
272
|
-
const apiPackage = apiClass.getAssociatedPackage?.();
|
|
273
|
-
if (apiPackage) {
|
|
274
|
-
const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(targetMember);
|
|
275
|
-
return prependHiddenImports(skeleton, imports);
|
|
276
|
-
}
|
|
277
|
-
return skeleton;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Generate a class skeleton for signature blocks
|
|
281
|
-
* Includes hidden imports with cut directive for external type resolution
|
|
282
|
-
*/
|
|
283
|
-
generateClassSkeletonWithTwoslash(apiClass, packageName) {
|
|
284
|
-
const skeleton = this.generateClassSkeleton(apiClass);
|
|
285
|
-
const apiPackage = apiClass.getAssociatedPackage?.();
|
|
286
|
-
if (apiPackage) {
|
|
287
|
-
const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiClass);
|
|
288
|
-
return prependHiddenImports(skeleton, imports);
|
|
289
|
-
}
|
|
290
|
-
return skeleton;
|
|
291
|
-
}
|
|
292
|
-
/**
|
|
293
|
-
* Generate a complete class skeleton showing all members
|
|
294
|
-
*/
|
|
295
|
-
generateClassSkeleton(apiClass) {
|
|
296
|
-
const lines = [];
|
|
297
|
-
const className = apiClass.displayName;
|
|
298
|
-
const inheritance = ApiItems.inheritance(apiClass);
|
|
299
|
-
let declaration = `class ${className}`;
|
|
300
|
-
if (inheritance.extends && inheritance.extends.length > 0) declaration += ` extends ${inheritance.extends.join(", ")}`;
|
|
301
|
-
if (inheritance.implements && inheritance.implements.length > 0) declaration += ` implements ${inheritance.implements.join(", ")}`;
|
|
302
|
-
declaration += " {";
|
|
303
|
-
lines.push(declaration);
|
|
304
|
-
const constructors = apiClass.members.filter((m) => m.kind === "Constructor");
|
|
305
|
-
if (constructors.length > 0) for (const ctor of constructors) {
|
|
306
|
-
const ctorItem = ctor;
|
|
307
|
-
if (ctorItem.excerpt?.text) {
|
|
308
|
-
const signature = Signature.format(ctorItem.excerpt).trim();
|
|
309
|
-
lines.push(` ${signature}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
const methods = apiClass.members.filter((m) => m.kind === "Method" || m.kind === "MethodSignature");
|
|
313
|
-
const grouped = this.groupClassMembers(methods);
|
|
314
|
-
const properties = apiClass.members.filter((m) => m.kind === "Property" || m.kind === "PropertySignature");
|
|
315
|
-
const staticProperties = properties.filter((m) => {
|
|
316
|
-
return m.isStatic === true;
|
|
317
|
-
});
|
|
318
|
-
if (staticProperties.length > 0) for (const prop of staticProperties) {
|
|
319
|
-
const propItem = prop;
|
|
320
|
-
if (propItem.excerpt?.text) {
|
|
321
|
-
const signature = Signature.format(propItem.excerpt).trim();
|
|
322
|
-
lines.push(` ${signature}`);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (grouped.staticMethods.length > 0) for (const method of grouped.staticMethods) {
|
|
326
|
-
const methodItem = method;
|
|
327
|
-
if (methodItem.excerpt?.text) {
|
|
328
|
-
const signature = Signature.format(methodItem.excerpt).trim();
|
|
329
|
-
lines.push(` ${signature}`);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const instanceProperties = properties.filter((m) => {
|
|
333
|
-
const isStatic = m.isStatic === true;
|
|
334
|
-
const isGetter = m.displayName.startsWith("get ") || m.displayName.startsWith("set ");
|
|
335
|
-
return !isStatic && !isGetter;
|
|
336
|
-
});
|
|
337
|
-
if (instanceProperties.length > 0) for (const prop of instanceProperties) {
|
|
338
|
-
const propItem = prop;
|
|
339
|
-
if (propItem.excerpt?.text) {
|
|
340
|
-
const signature = Signature.format(propItem.excerpt).trim();
|
|
341
|
-
lines.push(` ${signature}`);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (grouped.getters.length > 0) for (const method of grouped.getters) {
|
|
345
|
-
const methodItem = method;
|
|
346
|
-
if (methodItem.excerpt?.text) {
|
|
347
|
-
const signature = Signature.format(methodItem.excerpt).trim();
|
|
348
|
-
lines.push(` ${signature}`);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
if (grouped.instanceMethods.length > 0) for (const method of grouped.instanceMethods) {
|
|
352
|
-
const methodItem = method;
|
|
353
|
-
if (methodItem.excerpt?.text) {
|
|
354
|
-
const signature = Signature.format(methodItem.excerpt).trim();
|
|
355
|
-
lines.push(` ${signature}`);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
lines.push("}");
|
|
359
|
-
return lines.join("\n");
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
//#endregion
|
|
364
|
-
export { ClassPageGenerator };
|