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.
Files changed (44) hide show
  1. package/BuildEnv.js +0 -1
  2. package/build-program.js +5 -5
  3. package/build-stages.js +98 -282
  4. package/config-helpers.js +1 -1
  5. package/emit/mdx.js +311 -0
  6. package/emit/meta.js +62 -0
  7. package/index.d.ts +6 -70
  8. package/layers/build-metrics.js +1 -2
  9. package/layers/config-resolution.js +5 -8
  10. package/layers/type-environment.js +5 -4
  11. package/llms-program.js +3 -3
  12. package/markdown/helpers.js +10 -177
  13. package/observability/sinks/console-sink.js +1 -3
  14. package/observability/sinks/metrics-sink.js +0 -2
  15. package/package.json +9 -7
  16. package/path-derivation.js +1 -29
  17. package/plugin.js +3 -10
  18. package/prettier-formatter.js +27 -59
  19. package/remark-with-api.js +3 -2
  20. package/schemas/config.js +3 -29
  21. package/schemas/observability.js +8 -23
  22. package/schemas/performance.js +1 -7
  23. package/services/TwoslashCacheService.js +18 -14
  24. package/services/TypeRegistryService.js +4 -4
  25. package/shiki-transformer.js +12 -51
  26. package/twoslash-transformer.js +16 -49
  27. package/api-extracted-package.js +0 -471
  28. package/code-post-processor.js +0 -38
  29. package/frontmatter.js +0 -176
  30. package/llms-processing.js +0 -270
  31. package/markdown/page-generators/class-page.js +0 -364
  32. package/markdown/page-generators/enum-page.js +0 -152
  33. package/markdown/page-generators/function-page.js +0 -128
  34. package/markdown/page-generators/index-pages.js +0 -25
  35. package/markdown/page-generators/interface-page.js +0 -311
  36. package/markdown/page-generators/namespace-page.js +0 -278
  37. package/markdown/page-generators/type-alias-page.js +0 -111
  38. package/markdown/page-generators/variable-page.js +0 -111
  39. package/markdown/prose-linker.js +0 -22
  40. package/tsconfig-parser.js +0 -115
  41. package/twoslash-cache.js +0 -174
  42. package/twoslash-patterns.js +0 -87
  43. package/type-reference-extractor.js +0 -199
  44. package/typescript-config.js +0 -170
@@ -1,278 +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 { ApiItemKind } from "@microsoft/api-extractor-model";
5
- import { ApiItems, Signature, Tsdoc } from "@tsdoctor/model";
6
-
7
- //#region src/markdown/page-generators/namespace-page.ts
8
- /**
9
- * Generates MDX documentation pages for TypeScript namespaces.
10
- *
11
- * This class transforms API Extractor namespace models into rich MDX documentation pages
12
- * with syntax-highlighted signatures, cross-linked type references, and member listings.
13
- *
14
- * **Page Structure:**
15
- * 1. Frontmatter with title, description, and Open Graph metadata
16
- * 2. Component imports (SourceCode, 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 namespace signature block showing all members
21
- * 7. Member sections: Classes, Interfaces, Functions, Variables, Types, Enums, Namespaces
22
- * 8. Examples section with Twoslash-enabled code blocks
23
- * 9. See Also references
24
- *
25
- * **Member Rendering:**
26
- * Each member section lists members with links to their individual documentation pages.
27
- *
28
- * **Relationships:**
29
- * - Created and invoked by {@link ApiExtractorPlugin} during page generation
30
- * - Uses `Signature.format` from `@tsdoctor/model` for formatting type signatures
31
- * - Uses the `Tsdoc` / `ApiItems` modules from `@tsdoctor/model` for extracting documentation
32
- * - Uses the per-build prose linker (`linkProse`) for adding type reference links
33
- *
34
- * @example
35
- * ```ts
36
- * const generator = new NamespacePageGenerator();
37
- * const { routePath, content } = await generator.generate(
38
- * apiNamespace,
39
- * "/api/my-package",
40
- * "my-package",
41
- * "Namespace",
42
- * "My Package",
43
- * sourceConfig,
44
- * true, // suppressExampleErrors
45
- * undefined, // llmsPlugin
46
- * "my-scope"
47
- * );
48
- * ```
49
- *
50
- * @see {@link ClassPageGenerator} for class documentation
51
- * @see {@link InterfacePageGenerator} for interface documentation
52
- */
53
- var NamespacePageGenerator = class {
54
- /**
55
- * Generate a markdown page for a namespace
56
- *
57
- * @param apiScope - API scope identifier for VFS lookup
58
- */
59
- async generate(apiNamespace, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
60
- const shouldSuppressErrors = suppressExampleErrors ?? true;
61
- const name = apiNamespace.displayName;
62
- const summary = Tsdoc.summary(apiNamespace) || "No description available.";
63
- const releaseTag = Tsdoc.releaseTag(apiNamespace);
64
- let content = generateFrontmatter(name, summary, singularName, apiName);
65
- content += `import { SourceCode } from "@rspress/core/theme";\n`;
66
- content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
67
- content += `# ${name}\n\n`;
68
- const deprecation = Tsdoc.deprecation(apiNamespace);
69
- if (deprecation) {
70
- const message = escapeMdxGenerics(linkProse(deprecation.message));
71
- content += `> **Deprecated:** ${message}\n\n`;
72
- }
73
- if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
74
- content += `${summary}\n\n`;
75
- content += generateAvailableFrom(packageName, availableFrom);
76
- const sourceLink = ApiItems.sourceLink(apiNamespace, sourceConfig);
77
- if (sourceLink) {
78
- content += `<div className="api-docs-toolbar">\n`;
79
- content += ` <div className="api-docs-toolbar-left">\n`;
80
- content += ` <SourceCode href="${sourceLink}" />\n`;
81
- content += ` </div>\n`;
82
- if (llmsPlugin?.enabled) {
83
- content += ` <div className="api-docs-toolbar-right">\n`;
84
- content += ` </div>\n`;
85
- }
86
- content += `</div>\n\n`;
87
- }
88
- const skeleton = this.generateNamespaceSkeletonWithTwoslash(apiNamespace, packageName);
89
- const displayCode = stripTwoslashDirectives(skeleton);
90
- content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(skeleton)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
91
- const grouped = this.groupNamespaceMembers(apiNamespace.members);
92
- content += this.renderMemberSection("Classes", grouped.classes, baseRoute, "class", name);
93
- content += this.renderMemberSection("Interfaces", grouped.interfaces, baseRoute, "interface", name);
94
- content += this.renderMemberSection("Functions", grouped.functions, baseRoute, "function", name);
95
- content += this.renderMemberSection("Variables", grouped.variables, baseRoute, "variable", name);
96
- content += this.renderMemberSection("Types", grouped.typeAliases, baseRoute, "type", name);
97
- content += this.renderMemberSection("Enums", grouped.enums, baseRoute, "enum", name);
98
- content += this.renderMemberSection("Namespaces", grouped.namespaces, baseRoute, "namespace", name);
99
- const examples = Tsdoc.examples(apiNamespace);
100
- if (examples.length > 0) {
101
- content += `## Examples\n\n`;
102
- for (const example of examples) {
103
- const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
104
- const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
105
- api: packageName,
106
- blockType: "example"
107
- });
108
- if (prepared.isTypeScript) {
109
- const displayCode = stripTwoslashDirectives(formattedCode);
110
- content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
111
- } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
112
- }
113
- }
114
- const seeReferences = Tsdoc.seeReferences(apiNamespace);
115
- if (seeReferences.length > 0) {
116
- content += `## See Also\n\n`;
117
- for (const reference of seeReferences) {
118
- const refText = escapeMdxGenerics(linkProse(reference.text));
119
- content += `- ${refText}\n`;
120
- }
121
- content += `\n`;
122
- }
123
- return {
124
- routePath: `${baseRoute}/namespace/${name.toLowerCase()}`,
125
- content
126
- };
127
- }
128
- /**
129
- * Group namespace members by their type
130
- */
131
- groupNamespaceMembers(members) {
132
- const classes = [];
133
- const interfaces = [];
134
- const functions = [];
135
- const variables = [];
136
- const typeAliases = [];
137
- const enums = [];
138
- const namespaces = [];
139
- for (const member of members) switch (member.kind) {
140
- case ApiItemKind.Class:
141
- classes.push(member);
142
- break;
143
- case ApiItemKind.Interface:
144
- interfaces.push(member);
145
- break;
146
- case ApiItemKind.Function:
147
- functions.push(member);
148
- break;
149
- case ApiItemKind.Variable:
150
- variables.push(member);
151
- break;
152
- case ApiItemKind.TypeAlias:
153
- typeAliases.push(member);
154
- break;
155
- case ApiItemKind.Enum:
156
- enums.push(member);
157
- break;
158
- case ApiItemKind.Namespace: namespaces.push(member);
159
- }
160
- return {
161
- classes,
162
- interfaces,
163
- functions,
164
- variables,
165
- typeAliases,
166
- enums,
167
- namespaces
168
- };
169
- }
170
- /**
171
- * Render a section of members with links to their pages
172
- * @param title - Section heading
173
- * @param members - Array of API items to list
174
- * @param baseRoute - Base API route (e.g., /api/package)
175
- * @param categoryFolder - Category folder name (e.g., "class", "function")
176
- * @param namespaceName - Parent namespace name for qualified routes
177
- */
178
- renderMemberSection(title, members, baseRoute, categoryFolder, namespaceName) {
179
- if (members.length === 0) return "";
180
- let section = `## ${title}\n\n`;
181
- for (const member of members) {
182
- const memberName = member.displayName;
183
- const memberSummary = Tsdoc.summary(member);
184
- const memberRoute = `${baseRoute}/${categoryFolder}/${`${namespaceName}.${memberName}`.toLowerCase()}`;
185
- if (memberSummary) {
186
- const escapedSummary = escapeMdxGenerics(linkProse(memberSummary));
187
- section += `- [${memberName}](${memberRoute}) - ${escapedSummary}\n`;
188
- } else section += `- [${memberName}](${memberRoute})\n`;
189
- }
190
- section += `\n`;
191
- return section;
192
- }
193
- /**
194
- * Generate a namespace skeleton for signature blocks
195
- * Includes hidden imports with cut directive for external type resolution
196
- */
197
- generateNamespaceSkeletonWithTwoslash(apiNamespace, packageName) {
198
- const skeleton = this.generateNamespaceSkeleton(apiNamespace);
199
- const apiPackage = apiNamespace.getAssociatedPackage?.();
200
- if (apiPackage) {
201
- const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiNamespace);
202
- return prependHiddenImports(skeleton, imports);
203
- }
204
- return skeleton;
205
- }
206
- /**
207
- * Generate a complete namespace skeleton showing all members
208
- */
209
- generateNamespaceSkeleton(apiNamespace) {
210
- const lines = [];
211
- const namespaceName = apiNamespace.displayName;
212
- lines.push(`namespace ${namespaceName} {`);
213
- const grouped = this.groupNamespaceMembers(apiNamespace.members);
214
- for (const cls of grouped.classes) {
215
- const clsItem = cls;
216
- if (clsItem.excerpt?.text) {
217
- const signature = Signature.format(clsItem.excerpt).trim();
218
- lines.push(` ${this.abbreviateDeclaration(signature, "class")} { }`);
219
- }
220
- }
221
- for (const iface of grouped.interfaces) {
222
- const ifaceItem = iface;
223
- if (ifaceItem.excerpt?.text) {
224
- const signature = Signature.format(ifaceItem.excerpt).trim();
225
- lines.push(` ${this.abbreviateDeclaration(signature, "interface")} { }`);
226
- }
227
- }
228
- for (const func of grouped.functions) {
229
- const funcItem = func;
230
- if (funcItem.excerpt?.text) {
231
- const signature = Signature.format(funcItem.excerpt).trim();
232
- lines.push(` ${signature}`);
233
- }
234
- }
235
- for (const variable of grouped.variables) {
236
- const varItem = variable;
237
- if (varItem.excerpt?.text) {
238
- const signature = Signature.format(varItem.excerpt).trim();
239
- lines.push(` ${signature}`);
240
- }
241
- }
242
- for (const typeAlias of grouped.typeAliases) {
243
- const typeItem = typeAlias;
244
- if (typeItem.excerpt?.text) {
245
- const signature = Signature.format(typeItem.excerpt).trim();
246
- lines.push(` ${signature}`);
247
- }
248
- }
249
- for (const enumItem of grouped.enums) {
250
- const enumDeclItem = enumItem;
251
- if (enumDeclItem.excerpt?.text) {
252
- const signature = Signature.format(enumDeclItem.excerpt).trim();
253
- lines.push(` ${this.abbreviateDeclaration(signature, "enum")} { }`);
254
- }
255
- }
256
- for (const ns of grouped.namespaces) {
257
- const nsItem = ns;
258
- if (nsItem.excerpt?.text) {
259
- const signature = Signature.format(nsItem.excerpt).trim();
260
- lines.push(` ${this.abbreviateDeclaration(signature, "namespace")} { }`);
261
- }
262
- }
263
- lines.push("}");
264
- return lines.join("\n");
265
- }
266
- /**
267
- * Abbreviate a full declaration to just its header
268
- * For example, `class Foo extends Bar implements Baz \{ ... \}` becomes `class Foo extends Bar implements Baz`
269
- */
270
- abbreviateDeclaration(signature, _keyword) {
271
- const braceIndex = signature.indexOf("{");
272
- if (braceIndex === -1) return signature;
273
- return signature.substring(0, braceIndex).trim();
274
- }
275
- };
276
-
277
- //#endregion
278
- export { NamespacePageGenerator };
@@ -1,111 +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, Signature, Tsdoc } from "@tsdoctor/model";
5
-
6
- //#region src/markdown/page-generators/type-alias-page.ts
7
- /**
8
- * Generates MDX documentation pages for TypeScript type aliases.
9
- *
10
- * This class transforms API Extractor type alias models into rich MDX documentation
11
- * pages with syntax-highlighted signatures and cross-linked 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. Type alias signature block (full type definition)
20
- * 7. Examples section with Twoslash-enabled code blocks
21
- * 8. See Also references
22
- *
23
- * **Relationships:**
24
- * - Created and invoked by {@link ApiExtractorPlugin} during page generation
25
- * - Uses `Signature.format` from `@tsdoctor/model` for formatting type signatures
26
- * - Uses the `Tsdoc` / `ApiItems` modules from `@tsdoctor/model` for extracting documentation
27
- * - Uses the per-build prose linker (`linkProse`) for adding type reference links
28
- *
29
- * @see {@link InterfacePageGenerator} for interface documentation
30
- * @see {@link EnumPageGenerator} for enum documentation
31
- */
32
- var TypeAliasPageGenerator = class {
33
- /**
34
- * Generate a markdown page for a type alias
35
- *
36
- * @param apiScope - API scope identifier for VFS lookup
37
- */
38
- async generate(apiTypeAlias, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
39
- const shouldSuppressErrors = suppressExampleErrors ?? true;
40
- const name = apiTypeAlias.displayName;
41
- const summary = Tsdoc.summary(apiTypeAlias) || "No description available.";
42
- const releaseTag = Tsdoc.releaseTag(apiTypeAlias);
43
- let content = generateFrontmatter(name, summary, singularName, apiName);
44
- content += `import { SourceCode } from "@rspress/core/theme";\n`;
45
- content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
46
- content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
47
- content += `# ${name}\n\n`;
48
- const deprecation = Tsdoc.deprecation(apiTypeAlias);
49
- if (deprecation) {
50
- const message = escapeMdxGenerics(linkProse(deprecation.message));
51
- content += `> ⚠️ **Deprecated:** ${message}\n\n`;
52
- }
53
- if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
54
- content += `${summary}\n\n`;
55
- content += generateAvailableFrom(packageName, availableFrom);
56
- const sourceLink = ApiItems.sourceLink(apiTypeAlias, sourceConfig);
57
- if (sourceLink) {
58
- content += `<div className="api-docs-toolbar">\n`;
59
- content += ` <div className="api-docs-toolbar-left">\n`;
60
- content += ` <SourceCode href="${sourceLink}" />\n`;
61
- content += ` </div>\n`;
62
- if (llmsPlugin?.enabled) {
63
- content += ` <div className="api-docs-toolbar-right">\n`;
64
- content += ` </div>\n`;
65
- }
66
- content += `</div>\n\n`;
67
- }
68
- if (apiTypeAlias.excerpt.text) {
69
- const signature = Signature.format(apiTypeAlias.excerpt).trim();
70
- let signatureWithImports = signature;
71
- const apiPackage = apiTypeAlias.getAssociatedPackage?.();
72
- if (apiPackage) {
73
- const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiTypeAlias);
74
- signatureWithImports = prependHiddenImports(signature, imports);
75
- }
76
- const displayCode = stripTwoslashDirectives(signatureWithImports);
77
- content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
78
- }
79
- const examples = Tsdoc.examples(apiTypeAlias);
80
- if (examples.length > 0) {
81
- content += `## Examples\n\n`;
82
- for (const example of examples) {
83
- const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
84
- const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
85
- api: packageName,
86
- blockType: "example"
87
- });
88
- if (prepared.isTypeScript) {
89
- const displayCode = stripTwoslashDirectives(formattedCode);
90
- content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
91
- } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
92
- }
93
- }
94
- const seeReferences = Tsdoc.seeReferences(apiTypeAlias);
95
- if (seeReferences.length > 0) {
96
- content += `## See Also\n\n`;
97
- for (const reference of seeReferences) {
98
- const refText = escapeMdxGenerics(linkProse(reference.text));
99
- content += `- ${refText}\n`;
100
- }
101
- content += `\n`;
102
- }
103
- return {
104
- routePath: `${baseRoute}/type/${name.toLowerCase()}`,
105
- content
106
- };
107
- }
108
- };
109
-
110
- //#endregion
111
- export { TypeAliasPageGenerator };
@@ -1,111 +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, Signature, Tsdoc } from "@tsdoctor/model";
5
-
6
- //#region src/markdown/page-generators/variable-page.ts
7
- /**
8
- * Generates MDX documentation pages for exported variables and constants.
9
- *
10
- * This class transforms API Extractor variable models into rich MDX documentation
11
- * pages with syntax-highlighted signatures and cross-linked 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. Variable signature block (const/let declaration with type)
20
- * 7. Examples section with Twoslash-enabled code blocks
21
- * 8. See Also references
22
- *
23
- * **Relationships:**
24
- * - Created and invoked by {@link ApiExtractorPlugin} during page generation
25
- * - Uses `Signature.format` from `@tsdoctor/model` for formatting type signatures
26
- * - Uses the `Tsdoc` / `ApiItems` modules from `@tsdoctor/model` for extracting documentation
27
- * - Uses the per-build prose linker (`linkProse`) for adding type reference links
28
- *
29
- * @see {@link FunctionPageGenerator} for function documentation
30
- * @see {@link EnumPageGenerator} for enum documentation
31
- */
32
- var VariablePageGenerator = class {
33
- /**
34
- * Generate a markdown page for a variable
35
- *
36
- * @param apiScope - API scope identifier for VFS lookup
37
- */
38
- async generate(apiVariable, baseRoute, packageName, singularName, apiScope, apiName, sourceConfig, suppressExampleErrors, llmsPlugin, availableFrom) {
39
- const shouldSuppressErrors = suppressExampleErrors ?? true;
40
- const name = apiVariable.displayName;
41
- const summary = Tsdoc.summary(apiVariable) || "No description available.";
42
- const releaseTag = Tsdoc.releaseTag(apiVariable);
43
- let content = generateFrontmatter(name, summary, singularName, apiName);
44
- content += `import { SourceCode } from "@rspress/core/theme";\n`;
45
- content += `import { ParametersTable } from "rspress-plugin-api-extractor/runtime";\n`;
46
- content += `import { ApiSignature, ApiExample } from "rspress-plugin-api-extractor/runtime";\n\n`;
47
- content += `# ${name}\n\n`;
48
- const deprecation = Tsdoc.deprecation(apiVariable);
49
- if (deprecation) {
50
- const message = escapeMdxGenerics(linkProse(deprecation.message));
51
- content += `> ⚠️ **Deprecated:** ${message}\n\n`;
52
- }
53
- if (releaseTag !== "Public") content += `\`${releaseTag}\`\n\n`;
54
- content += `${summary}\n\n`;
55
- content += generateAvailableFrom(packageName, availableFrom);
56
- const sourceLink = ApiItems.sourceLink(apiVariable, sourceConfig);
57
- if (sourceLink) {
58
- content += `<div className="api-docs-toolbar">\n`;
59
- content += ` <div className="api-docs-toolbar-left">\n`;
60
- content += ` <SourceCode href="${sourceLink}" />\n`;
61
- content += ` </div>\n`;
62
- if (llmsPlugin?.enabled) {
63
- content += ` <div className="api-docs-toolbar-right">\n`;
64
- content += ` </div>\n`;
65
- }
66
- content += `</div>\n\n`;
67
- }
68
- if (apiVariable.excerpt.text) {
69
- const signature = Signature.format(apiVariable.excerpt).trim();
70
- let signatureWithImports = signature;
71
- const apiPackage = apiVariable.getAssociatedPackage?.();
72
- if (apiPackage) {
73
- const imports = new TypeReferenceExtractor(apiPackage, packageName).extractImportsForApiItem(apiVariable);
74
- signatureWithImports = prependHiddenImports(signature, imports);
75
- }
76
- const displayCode = stripTwoslashDirectives(signatureWithImports);
77
- content += `<ApiSignature code={${JSON.stringify(displayCode)}} source={${JSON.stringify(signatureWithImports)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
78
- }
79
- const examples = Tsdoc.examples(apiVariable);
80
- if (examples.length > 0) {
81
- content += `## Examples\n\n`;
82
- for (const example of examples) {
83
- const prepared = prepareExampleCode(example, name, packageName, shouldSuppressErrors);
84
- const formattedCode = await formatExampleCode(prepared.code, prepared.language, {
85
- api: packageName,
86
- blockType: "example"
87
- });
88
- if (prepared.isTypeScript) {
89
- const displayCode = stripTwoslashDirectives(formattedCode);
90
- content += `<ApiExample code={${JSON.stringify(displayCode)}} source={${JSON.stringify(formattedCode)}} apiScope={${JSON.stringify(apiScope)}} />\n\n`;
91
- } else content += `\`\`\`${prepared.language}\n${formattedCode}\n\`\`\`\n\n`;
92
- }
93
- }
94
- const seeReferences = Tsdoc.seeReferences(apiVariable);
95
- if (seeReferences.length > 0) {
96
- content += `## See Also\n\n`;
97
- for (const reference of seeReferences) {
98
- const refText = escapeMdxGenerics(linkProse(reference.text));
99
- content += `- ${refText}\n`;
100
- }
101
- content += `\n`;
102
- }
103
- return {
104
- routePath: `${baseRoute}/variable/${name.toLowerCase()}`,
105
- content
106
- };
107
- }
108
- };
109
-
110
- //#endregion
111
- export { VariablePageGenerator };
@@ -1,22 +0,0 @@
1
- import { CrossLinker } from "@tsdoctor/model";
2
-
3
- //#region src/markdown/prose-linker.ts
4
- /**
5
- * Per-build prose cross-linker holder. Adapter wiring, not logic: the build
6
- * program installs the immutable `@tsdoctor/model` CrossLinker built from the
7
- * routes `prepareWorkItems` computed, and page generators link prose through
8
- * it. Page generators run synchronously outside any service context, hence
9
- * the module-level holder (the same shape as the sync-island event emitters).
10
- */
11
- let current = CrossLinker.empty;
12
- /** Install the cross-linker for the current API build from its route map. */
13
- function setProseLinker(routes) {
14
- current = CrossLinker.fromRoutes(routes);
15
- }
16
- /** Cross-link prose text with the currently installed linker. */
17
- function linkProse(text) {
18
- return current.link(text);
19
- }
20
-
21
- //#endregion
22
- export { linkProse, setProseLinker };
@@ -1,115 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { TsconfigLoaderSync } from "@effected/tsconfig-json";
4
-
5
- //#region src/tsconfig-parser.ts
6
- /**
7
- * Reading a `tsconfig.json` into the compiler options the plugin consumes.
8
- *
9
- * @remarks
10
- * A thin adapter over `@effected/tsconfig-json`'s `TsconfigLoaderSync`, which
11
- * owns `extends` chain resolution (including package specifiers), JSONC
12
- * parsing and relative-path handling. This module used to hand-roll all three
13
- * over TypeScript's `parseJsonConfigFileContent`.
14
- *
15
- * **The loader returns the tsconfig SPELLING, not the programmatic one.**
16
- * `target` is `"es2025"` rather than `ts.ScriptTarget.ES2025`, and `lib` is
17
- * `["esnext"]` rather than `["lib.esnext.d.ts"]`. That is fine, and it is why
18
- * the normalization seam had to land first: `toProgrammaticCompilerOptions`
19
- * (`twoslash-transformer.ts`) converts at ONE place, and
20
- * {@link TypeResolutionCompilerOptions} accepts both spellings by design. Do
21
- * not convert here — a second conversion site is exactly the drift that made
22
- * three of four resolution paths load zero lib files once already.
23
- *
24
- * @packageDocumentation
25
- */
26
- /**
27
- * Error thrown when tsconfig.json parsing fails.
28
- *
29
- * @remarks
30
- * Retained as the plugin's own type rather than surfacing the kit's
31
- * `TsconfigParseError`/`TsconfigExtendsError` directly: `typescript-config.ts`
32
- * branches on `instanceof TsConfigParseError` to decide whether a failure is
33
- * already reported, and both kit errors mean the same thing to that caller.
34
- */
35
- var TsConfigParseError = class extends Error {
36
- configPath;
37
- cause;
38
- constructor(configPath, message, cause) {
39
- super(`Failed to parse tsconfig at ${configPath}: ${message}`);
40
- this.configPath = configPath;
41
- this.cause = cause;
42
- this.name = "TsConfigParseError";
43
- }
44
- };
45
- /**
46
- * The sync host the kit loader reads through.
47
- *
48
- * @remarks
49
- * `node:path` satisfies `SyncPath` verbatim. The filesystem half is two
50
- * functions, so no shim module is needed.
51
- */
52
- const syncHost = {
53
- fileSystem: {
54
- exists: existsSync,
55
- readFile: (filePath) => readFileSync(filePath, "utf8")
56
- },
57
- path
58
- };
59
- /**
60
- * Parse a `tsconfig.json` and extract the compiler options used for type
61
- * resolution.
62
- *
63
- * @param configPath - Path to tsconfig.json (relative or absolute)
64
- * @param projectRoot - Project root directory for resolving relative paths
65
- * @returns The declared compiler options, in the tsconfig spelling
66
- * @throws TsConfigParseError if the config cannot be read or parsed
67
- *
68
- * @example
69
- * ```ts
70
- * const options = parseTsConfig("tsconfig.json", "/path/to/project");
71
- * // Returns: { target: "es2025", module: "nodenext", lib: ["esnext"], ... }
72
- * ```
73
- */
74
- function parseTsConfig(configPath, projectRoot) {
75
- const absolutePath = path.isAbsolute(configPath) ? configPath : path.resolve(projectRoot, configPath);
76
- if (!existsSync(absolutePath)) throw new TsConfigParseError(absolutePath, "File not found");
77
- let options;
78
- try {
79
- options = TsconfigLoaderSync.compilerOptions(absolutePath, syncHost);
80
- } catch (error) {
81
- throw new TsConfigParseError(absolutePath, error instanceof Error ? error.message : String(error), error);
82
- }
83
- return extractTypeResolutionOptions(options);
84
- }
85
- /**
86
- * Narrow the full compiler options to the ones the plugin actually consumes.
87
- *
88
- * @remarks
89
- * Deliberately a whitelist. Everything here reaches Twoslash's TypeScript
90
- * environment, and passing through options the plugin does not understand
91
- * would let a consumer's unrelated build setting change how examples
92
- * type-check.
93
- */
94
- function extractTypeResolutionOptions(options) {
95
- const result = {};
96
- const scalar = (value) => typeof value === "string" || typeof value === "number" ? value : void 0;
97
- const target = scalar(options.target);
98
- if (target !== void 0) result.target = target;
99
- const module_ = scalar(options.module);
100
- if (module_ !== void 0) result.module = module_;
101
- const moduleResolution = scalar(options.moduleResolution);
102
- if (moduleResolution !== void 0) result.moduleResolution = moduleResolution;
103
- const jsx = scalar(options.jsx);
104
- if (jsx !== void 0) result.jsx = jsx;
105
- if (typeof options.strict === "boolean") result.strict = options.strict;
106
- if (typeof options.skipLibCheck === "boolean") result.skipLibCheck = options.skipLibCheck;
107
- if (typeof options.esModuleInterop === "boolean") result.esModuleInterop = options.esModuleInterop;
108
- if (typeof options.allowSyntheticDefaultImports === "boolean") result.allowSyntheticDefaultImports = options.allowSyntheticDefaultImports;
109
- if (Array.isArray(options.lib) && options.lib.length > 0) result.lib = options.lib.map(String);
110
- if (Array.isArray(options.types) && options.types.length > 0) result.types = options.types.map(String);
111
- return result;
112
- }
113
-
114
- //#endregion
115
- export { TsConfigParseError, parseTsConfig };