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
|
@@ -2,23 +2,22 @@ import { BuildId } from "../BuildEnv.js";
|
|
|
2
2
|
import { BuildMetrics } from "./build-metrics.js";
|
|
3
3
|
import { PluginEvent } from "../observability/events.js";
|
|
4
4
|
import { emit, wantsLevel } from "../observability/EventBus.js";
|
|
5
|
-
import { TypeReferenceExtractor } from "../type-reference-extractor.js";
|
|
6
5
|
import { withPhase } from "../observability/spans.js";
|
|
7
6
|
import { normalizeThemeConfig } from "../markdown/shiki-utils.js";
|
|
8
|
-
import { apiScopeOf, deriveOutputPaths, normalizeBaseRoute, unscopedName } from "../path-derivation.js";
|
|
9
7
|
import { classifyApiConfig, extractAutoDetectedPackages, isVersionConfig, mergeLlmsPluginConfig, validateExternalPackages } from "../config-utils.js";
|
|
10
|
-
import { ApiExtractedPackage } from "../api-extracted-package.js";
|
|
11
8
|
import { CategoryResolver } from "../category-resolver.js";
|
|
12
9
|
import { ConfigValidationError } from "../errors.js";
|
|
13
10
|
import { loadApiModel, loadPackageJson, loadVersionModel } from "../model-loader.js";
|
|
11
|
+
import { deriveOutputPaths } from "../path-derivation.js";
|
|
14
12
|
import { DEFAULT_CATEGORIES } from "../schemas/config.js";
|
|
15
13
|
import { PluginConfig } from "../services/PluginConfig.js";
|
|
16
14
|
import { TypeRegistryService } from "../services/TypeRegistryService.js";
|
|
17
15
|
import { emitVfsPayloadEvents, mergeApiResult } from "./api-results.js";
|
|
18
16
|
import { mergeExternalTypes } from "./external-types.js";
|
|
19
17
|
import { registerTypeEnvironments, resolveTsConfigTyped } from "./type-environment.js";
|
|
20
|
-
import
|
|
18
|
+
import { apiScopeOf, normalizeBaseRoute, unscopedName } from "@tsdoctor/pages";
|
|
21
19
|
import { Effect, Metric } from "effect";
|
|
20
|
+
import { ApiExtractedPackage, TypeReferenceExtractor } from "@tsdoctor/model";
|
|
22
21
|
import { deriveSiteUrl } from "@tsdoctor/seo";
|
|
23
22
|
import { hashContent } from "@tsdoctor/snapshot";
|
|
24
23
|
import { PackageManifest } from "@effected/package-json";
|
|
@@ -241,7 +240,7 @@ const makeConfigService = Effect.gen(function* () {
|
|
|
241
240
|
});
|
|
242
241
|
const externalPackages = api.externalPackages || extractAutoDetectedPackages(packageJson, api.autoDetectDependencies);
|
|
243
242
|
if (externalPackages && externalPackages.length > 0) yield* Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length);
|
|
244
|
-
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).
|
|
243
|
+
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).toVfs();
|
|
245
244
|
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
246
245
|
const resolvedOgImage = api.ogImage ?? options.ogImage;
|
|
247
246
|
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
@@ -262,7 +261,6 @@ const makeConfigService = Effect.gen(function* () {
|
|
|
262
261
|
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
263
262
|
...siteUrl != null ? { siteUrl } : {},
|
|
264
263
|
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
265
|
-
docsDir: path.dirname(outputDir),
|
|
266
264
|
...docsRoot != null ? { docsRoot } : {},
|
|
267
265
|
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
268
266
|
}
|
|
@@ -321,7 +319,7 @@ const makeConfigService = Effect.gen(function* () {
|
|
|
321
319
|
const autoDetectOptions = versionAutoDetectDependencies || api.autoDetectDependencies;
|
|
322
320
|
const externalPackages = versionExternalPackages || api.externalPackages || extractAutoDetectedPackages(packageJson, autoDetectOptions);
|
|
323
321
|
if (externalPackages && externalPackages.length > 0) yield* Metric.update(BuildMetrics.externalPackagesTotal, externalPackages.length);
|
|
324
|
-
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).
|
|
322
|
+
const vfs = ApiExtractedPackage.fromPackage(apiPackage, api.packageName).toVfs();
|
|
325
323
|
const vfsPayloads = prependImportsToVfs(vfs, apiPackage, api.packageName, wantTrace);
|
|
326
324
|
const resolvedOgImage = versionOgImage ?? api.ogImage ?? options.ogImage;
|
|
327
325
|
const resolvedTheme = normalizeThemeConfig(api.theme);
|
|
@@ -344,7 +342,6 @@ const makeConfigService = Effect.gen(function* () {
|
|
|
344
342
|
...resolvedLlms != null ? { llmsPlugin: resolvedLlms } : {},
|
|
345
343
|
...siteUrl != null ? { siteUrl } : {},
|
|
346
344
|
...resolvedOgImage != null ? { ogImage: resolvedOgImage } : {},
|
|
347
|
-
docsDir: path.dirname(outputDir),
|
|
348
345
|
...docsRoot != null ? { docsRoot } : {},
|
|
349
346
|
...resolvedTheme != null ? { theme: resolvedTheme } : {}
|
|
350
347
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { PluginEvent } from "../observability/events.js";
|
|
2
2
|
import { emit } from "../observability/EventBus.js";
|
|
3
|
-
import { resolveTypeScriptConfig } from "../typescript-config.js";
|
|
4
3
|
import { TwoslashEnvironments } from "../services/TwoslashEnvironments.js";
|
|
5
4
|
import { ConfigValidationError } from "../errors.js";
|
|
6
|
-
import { twoslashEnvHash } from "../twoslash-cache.js";
|
|
7
5
|
import { TwoslashCacheService } from "../services/TwoslashCacheService.js";
|
|
8
6
|
import { Effect } from "effect";
|
|
7
|
+
import { resolveTypeScriptConfig, twoslashEnvHash } from "@tsdoctor/vfs";
|
|
9
8
|
import ts from "typescript";
|
|
10
9
|
|
|
11
10
|
//#region src/layers/type-environment.ts
|
|
@@ -64,12 +63,14 @@ const resolveTsConfigTyped = (projectRoot, config) => Effect.tryPromise({
|
|
|
64
63
|
*/
|
|
65
64
|
const registerTypeEnvironments = (input) => Effect.gen(function* () {
|
|
66
65
|
const twoslashEnv = twoslashEnvHash(input.combinedVfs, `typescript@${ts.version}`);
|
|
67
|
-
const
|
|
66
|
+
const cacheSvc = yield* TwoslashCacheService;
|
|
67
|
+
const twoslashCache = yield* cacheSvc.open(twoslashEnv);
|
|
68
68
|
yield* emit(PluginEvent.TwoslashCacheLoaded({
|
|
69
69
|
ctx: {},
|
|
70
70
|
level: "debug",
|
|
71
71
|
envHash: twoslashEnv,
|
|
72
|
-
entries: twoslashCache.entries().size
|
|
72
|
+
entries: twoslashCache.entries().size,
|
|
73
|
+
degraded: cacheSvc.degraded
|
|
73
74
|
}));
|
|
74
75
|
const twoslashStartMs = performance.now();
|
|
75
76
|
const environments = yield* TwoslashEnvironments;
|
package/llms-program.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { PluginEvent } from "./observability/events.js";
|
|
2
2
|
import { emit } from "./observability/EventBus.js";
|
|
3
|
-
import { filterLlmsFullTxt, filterLlmsTxt, generatePackageLlmsFullTxt, generatePackageLlmsTxt, generateStructuredLlmsTxt, parseLlmsTxtLine } from "./llms-processing.js";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import { filterLlmsFullTxt, filterLlmsTxt, generatePackageLlmsFullTxt, generatePackageLlmsTxt, generateStructuredLlmsTxt, parseLlmsTxtLine } from "@tsdoctor/pages";
|
|
5
5
|
import { Effect, FileSystem } from "effect";
|
|
6
6
|
|
|
7
7
|
//#region src/llms-program.ts
|
|
8
8
|
/**
|
|
9
9
|
* Effect program for post-processing LLMs text files in afterBuild.
|
|
10
10
|
*
|
|
11
|
-
* Wires the pure processing functions from
|
|
11
|
+
* Wires the pure processing functions from @tsdoctor/pages into the
|
|
12
12
|
* plugin lifecycle, handling file I/O via the core `effect` FileSystem service.
|
|
13
13
|
*
|
|
14
14
|
* Responsibilities:
|
|
@@ -89,7 +89,7 @@ function buildPackagePointers(buildResults, prefix, packageRoutes) {
|
|
|
89
89
|
* Collect API page entries from the global llms.txt for a specific package.
|
|
90
90
|
*
|
|
91
91
|
* Parses the global llms.txt to find entries whose URLs match this package's
|
|
92
|
-
* generated API routes, building the LlmsTxtEntry array for per-package files.
|
|
92
|
+
* generated API routes, building the `LlmsTxtEntry` array for per-package files.
|
|
93
93
|
*/
|
|
94
94
|
function collectApiEntries(globalLlmsTxtContent, result) {
|
|
95
95
|
const base = result.baseRoute.endsWith("/") ? result.baseRoute : `${result.baseRoute}/`;
|
package/markdown/helpers.js
CHANGED
|
@@ -1,44 +1,18 @@
|
|
|
1
|
-
import { emitFrontmatterBlock } from "
|
|
2
|
-
import { classifyCutDirective, isTwoslashDirective } from "../twoslash-patterns.js";
|
|
3
|
-
import { formatCode } from "../prettier-formatter.js";
|
|
4
|
-
import { TypeReferenceExtractor } from "../type-reference-extractor.js";
|
|
1
|
+
import { emitFrontmatterBlock } from "@tsdoctor/model";
|
|
5
2
|
|
|
6
3
|
//#region src/markdown/helpers.ts
|
|
7
4
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
function generateAvailableFrom(packageName, availableFrom) {
|
|
12
|
-
if (!availableFrom || availableFrom.length <= 1) return "";
|
|
13
|
-
return `Available from: ${availableFrom.map((ep) => ep === "default" ? `\`${packageName}\`` : `\`${packageName}/${ep}\``).join(", ")}\n\n`;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Prepare example code for Twoslash rendering.
|
|
5
|
+
* Frontmatter assembly for generated pages: the structured title and the
|
|
6
|
+
* rendering of neutral `@tsdoctor/seo` head tags into RSPress `head` pairs.
|
|
17
7
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* This stays adapter-side on purpose. The snapshot frontmatter hash is
|
|
10
|
+
* taken over the FINAL assembled block in the generate stage, and the
|
|
11
|
+
* `children` spelling for a JSON-LD script body is RSPress's — the IR
|
|
12
|
+
* carries facts and a `HeadTag[]`, not a frontmatter block.
|
|
20
13
|
*
|
|
21
|
-
* @
|
|
22
|
-
* @param apiItemName - The name of the API item being documented
|
|
23
|
-
* @param packageName - The package name for imports
|
|
24
|
-
* @param suppressErrors - Whether to suppress all TypeScript errors (default: true)
|
|
25
|
-
* @returns Object with prepared code and whether it's TypeScript
|
|
14
|
+
* @packageDocumentation
|
|
26
15
|
*/
|
|
27
|
-
function prepareExampleCode(example, apiItemName, packageName, suppressErrors = true) {
|
|
28
|
-
const { language, code } = example;
|
|
29
|
-
if (!(language === "typescript" || language === "ts" || language === "javascript" || language === "js")) return {
|
|
30
|
-
code,
|
|
31
|
-
isTypeScript: false,
|
|
32
|
-
language
|
|
33
|
-
};
|
|
34
|
-
const importLine = `import { ${apiItemName} } from "${packageName}";`;
|
|
35
|
-
const finalCode = code.includes(`from "${packageName}"`) || code.includes(`from '${packageName}'`) ? code : `${importLine}\n${code}`;
|
|
36
|
-
return {
|
|
37
|
-
code: `${suppressErrors ? "// @noErrors\n" : ""}${finalCode}`,
|
|
38
|
-
isTypeScript: true,
|
|
39
|
-
language: "typescript"
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
16
|
/**
|
|
43
17
|
* Collapse newlines and runs of whitespace to single spaces, and trim.
|
|
44
18
|
*
|
|
@@ -53,28 +27,6 @@ function cleanYamlValue(value) {
|
|
|
53
27
|
return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
54
28
|
}
|
|
55
29
|
/**
|
|
56
|
-
* Escape generic type parameters in MDX by wrapping them in backticks.
|
|
57
|
-
*
|
|
58
|
-
* Prevents MDX from interpreting `<T>`, `<TEnv>`, etc. as JSX tags by
|
|
59
|
-
* wrapping them in inline code backticks.
|
|
60
|
-
*
|
|
61
|
-
* @param text - The text containing generic type parameters
|
|
62
|
-
* @returns Text with generics wrapped in backticks
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```ts
|
|
66
|
-
* escapeMdxGenerics("Returns Promise<T>"); // "Returns Promise`<T>`"
|
|
67
|
-
* escapeMdxGenerics("Map<K, V> extends..."); // "Map`<K, V>` extends..."
|
|
68
|
-
* escapeMdxGenerics("`Pipeline<I, O>`"); // "`Pipeline<I, O>`" (unchanged)
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
function escapeMdxGenerics(text) {
|
|
72
|
-
return text.split(/(`[^`]+`)/g).map((part) => {
|
|
73
|
-
if (part.startsWith("`") && part.endsWith("`")) return part;
|
|
74
|
-
return part.replace(/<([A-Z][A-Za-z0-9_]*(?:\s+extends\s+[^>]+)?(?:,\s*[A-Z][A-Za-z0-9_]*(?:\s+extends\s+[^>]+)?)*)>/g, "`<$1>`");
|
|
75
|
-
}).join("");
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
30
|
* Build a structured page title for API documentation.
|
|
79
31
|
*
|
|
80
32
|
* Creates a title in the format: `{entityName} | {singularName} | API | {apiName}`
|
|
@@ -142,125 +94,6 @@ function generateFrontmatter(entityName, description, singularName, apiName, tag
|
|
|
142
94
|
if (headEntries.length > 0) data.head = headEntries;
|
|
143
95
|
return emitFrontmatterBlock(data);
|
|
144
96
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Strip Twoslash directives from code for display purposes.
|
|
147
|
-
*
|
|
148
|
-
* Removes Twoslash directive comments like `// @noErrors`, `// @errors: 2304`,
|
|
149
|
-
* `// @filename: ...`, etc. from code so users see clean output and don't
|
|
150
|
-
* copy directives when using the copy button.
|
|
151
|
-
*
|
|
152
|
-
* Also handles cut directives:
|
|
153
|
-
* - `// ---cut---` - Removes this line and all lines before it
|
|
154
|
-
* - `// ---cut-before---` - Same as ---cut---
|
|
155
|
-
* - `// ---cut-after---` - Removes this line and all lines after it
|
|
156
|
-
*
|
|
157
|
-
* @param code - The code containing Twoslash directives
|
|
158
|
-
* @returns Code with Twoslash directives removed
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```ts
|
|
162
|
-
* const display = stripTwoslashDirectives("// @noErrors\nconst x = 1;");
|
|
163
|
-
* // Returns: "const x = 1;"
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
function stripTwoslashDirectives(code) {
|
|
167
|
-
const lines = code.split("\n");
|
|
168
|
-
let cutBeforeIndex = -1;
|
|
169
|
-
let cutAfterIndex = -1;
|
|
170
|
-
const cutRanges = [];
|
|
171
|
-
const cutStartStack = [];
|
|
172
|
-
for (let i = 0; i < lines.length; i++) {
|
|
173
|
-
const trimmed = lines[i].trim();
|
|
174
|
-
const cutType = classifyCutDirective(trimmed);
|
|
175
|
-
if (cutType === "cut-before") cutBeforeIndex = i;
|
|
176
|
-
else if (cutType === "cut-after") cutAfterIndex = i;
|
|
177
|
-
else if (cutType === "cut-start") cutStartStack.push(i);
|
|
178
|
-
else if (cutType === "cut-end") {
|
|
179
|
-
const startIdx = cutStartStack.pop();
|
|
180
|
-
if (startIdx !== void 0) cutRanges.push([startIdx, i]);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
let filteredLines = lines;
|
|
184
|
-
if (cutBeforeIndex >= 0) {
|
|
185
|
-
filteredLines = filteredLines.slice(cutBeforeIndex + 1);
|
|
186
|
-
if (cutAfterIndex >= 0) cutAfterIndex = cutAfterIndex - cutBeforeIndex - 1;
|
|
187
|
-
for (const range of cutRanges) {
|
|
188
|
-
range[0] -= cutBeforeIndex + 1;
|
|
189
|
-
range[1] -= cutBeforeIndex + 1;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if (cutAfterIndex >= 0) filteredLines = filteredLines.slice(0, cutAfterIndex);
|
|
193
|
-
const excludedLines = /* @__PURE__ */ new Set();
|
|
194
|
-
for (const [start, end] of cutRanges) for (let i = start; i <= end; i++) if (i >= 0 && i < filteredLines.length) excludedLines.add(i);
|
|
195
|
-
return filteredLines.filter((line, i) => {
|
|
196
|
-
if (excludedLines.has(i)) return false;
|
|
197
|
-
const trimmed = line.trim();
|
|
198
|
-
if (isTwoslashDirective(trimmed)) return false;
|
|
199
|
-
return true;
|
|
200
|
-
}).join("\n").trim();
|
|
201
|
-
}
|
|
202
|
-
/**
|
|
203
|
-
* Format import statements with cut directive for hidden imports.
|
|
204
|
-
*
|
|
205
|
-
* Prepends import statements followed by `// ---cut---` so Twoslash can
|
|
206
|
-
* resolve the types but the imports are hidden from rendered output.
|
|
207
|
-
*
|
|
208
|
-
* @param imports - Import statements to format
|
|
209
|
-
* @returns Formatted import block with cut directive, or empty string if no imports
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```ts
|
|
213
|
-
* const imports = [{ packageName: "zod", symbols: new Set(["ZodType"]), typeOnly: true }];
|
|
214
|
-
* const block = formatImportsWithCut(imports);
|
|
215
|
-
* // Returns:
|
|
216
|
-
* // import type { ZodType } from "zod";
|
|
217
|
-
* // // ---cut---
|
|
218
|
-
* ```
|
|
219
|
-
*/
|
|
220
|
-
function formatImportsWithCut(imports) {
|
|
221
|
-
if (imports.length === 0) return "";
|
|
222
|
-
return `${TypeReferenceExtractor.formatImports(imports).join("\n")}\n// ---cut---\n`;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Prepend hidden imports to code using the Twoslash cut directive.
|
|
226
|
-
*
|
|
227
|
-
* This enables type resolution for external types while hiding the import
|
|
228
|
-
* statements from rendered output. The existing `stripTwoslashDirectives()`
|
|
229
|
-
* function handles removing the cut block for clipboard copying.
|
|
230
|
-
*
|
|
231
|
-
* @param code - The code to prepend imports to
|
|
232
|
-
* @param imports - Import statements to add
|
|
233
|
-
* @returns Code with imports prepended (if any), or original code if no imports
|
|
234
|
-
*
|
|
235
|
-
* @example
|
|
236
|
-
* ```ts
|
|
237
|
-
* const code = "function foo(): RsbuildPlugin";
|
|
238
|
-
* const imports = [{ packageName: "@rsbuild/core", symbols: new Set(["RsbuildPlugin"]), typeOnly: true }];
|
|
239
|
-
* const result = prependHiddenImports(code, imports);
|
|
240
|
-
* // Returns:
|
|
241
|
-
* // import type { RsbuildPlugin } from "@rsbuild/core";
|
|
242
|
-
* // // ---cut---
|
|
243
|
-
* // function foo(): RsbuildPlugin
|
|
244
|
-
* ```
|
|
245
|
-
*/
|
|
246
|
-
function prependHiddenImports(code, imports) {
|
|
247
|
-
const importBlock = formatImportsWithCut(imports);
|
|
248
|
-
return importBlock ? importBlock + code : code;
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Format example code using Prettier for consistent styling.
|
|
252
|
-
*
|
|
253
|
-
* Wraps the Prettier formatter with error handling and context tracking.
|
|
254
|
-
* If formatting fails, returns the original code (fallthrough behavior).
|
|
255
|
-
*
|
|
256
|
-
* @param code - The code to format
|
|
257
|
-
* @param language - The code fence language (e.g., "typescript", "ts")
|
|
258
|
-
* @param _context - Optional context (reserved for future use)
|
|
259
|
-
* @returns The formatted code (or original if formatting fails)
|
|
260
|
-
*/
|
|
261
|
-
async function formatExampleCode(code, language, _context) {
|
|
262
|
-
return (await formatCode(code, language)).code;
|
|
263
|
-
}
|
|
264
97
|
|
|
265
98
|
//#endregion
|
|
266
|
-
export {
|
|
99
|
+
export { generateFrontmatter };
|
|
@@ -22,12 +22,10 @@ function render(event) {
|
|
|
22
22
|
case "SlowOperation": return `slow ${event.operation}: ${event.durationMs}ms (>${event.threshold}ms)`;
|
|
23
23
|
case "ConfigCascadeWarning": return event.ignored.length > 2 ? `${event.field}: using '${event.chosen}', ignoring ${event.ignored.length} alternatives (first configured value wins)` : `${event.field}: using '${event.chosen}', ignoring ${event.ignored.join(", ")}`;
|
|
24
24
|
case "ConfigValidationWarning": return `${event.field}: rejected '${event.value}'${event.reason ? ` — ${event.reason}` : ""}`;
|
|
25
|
-
case "DeprecatedConfigUsed": return `option '${event.key}' is deprecated; use ${event.replacement}`;
|
|
26
25
|
case "ModelLoaded": return `loaded model: ${event.itemCount} items, ${event.entryPoints} entry point(s) (${event.durationMs}ms)`;
|
|
27
|
-
case "ConfigResolved": return `resolved ${event.baseRoute}: ${event.categoryCount} categories, ${event.externalCount} external`;
|
|
28
26
|
case "TwoslashDiagnostic": return `Twoslash TS${event.code} in ${event.file}:${event.line}:${event.col}: ${event.message}`;
|
|
29
27
|
case "TwoslashCheckFailed": return `Twoslash check failed (TS${event.code}) in ${event.file}; ${event.fsMapKeys.length} VFS files`;
|
|
30
|
-
case "TwoslashCacheLoaded": return event.entries > 0 ? `Twoslash cache: restored ${event.entries} cached result(s)` : "Twoslash cache: cold (no cached results for this type environment)";
|
|
28
|
+
case "TwoslashCacheLoaded": return event.degraded ? "Twoslash cache: DEGRADED (unusable cache directory) — every block will be type-checked, every build" : event.entries > 0 ? `Twoslash cache: restored ${event.entries} cached result(s)` : "Twoslash cache: cold (no cached results for this type environment)";
|
|
31
29
|
case "TwoslashCacheSaved": {
|
|
32
30
|
const total = event.hits + event.misses;
|
|
33
31
|
const pct = total > 0 ? Math.round(event.hits / total * 100) : 0;
|
|
@@ -100,8 +100,6 @@ function makeMetricsSink(context) {
|
|
|
100
100
|
case "PhaseCompleted":
|
|
101
101
|
update(BuildMetrics.phaseDuration, event.durationMs);
|
|
102
102
|
both(BuildMetrics.phaseTimeMs, event.durationMs, { phase: event.phase });
|
|
103
|
-
break;
|
|
104
|
-
case "DefaultApplied": update(BuildMetrics.configDefaultsApplied, 1);
|
|
105
103
|
}
|
|
106
104
|
}
|
|
107
105
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rspress-plugin-api-extractor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "RSPress plugin for generating API documentation from TypeScript API Extractor models",
|
|
6
6
|
"keywords": [
|
|
@@ -41,22 +41,24 @@
|
|
|
41
41
|
"@effected/github": "^0.8.0",
|
|
42
42
|
"@effected/glob": "^0.4.0",
|
|
43
43
|
"@effected/jsonc": "^0.8.1",
|
|
44
|
-
"@effected/markdown": "^0.
|
|
44
|
+
"@effected/markdown": "^0.8.0",
|
|
45
45
|
"@effected/npm": "^0.12.1",
|
|
46
46
|
"@effected/package-json": "^0.13.0",
|
|
47
47
|
"@effected/semver": "^0.5.0",
|
|
48
|
-
"@effected/store": "^0.
|
|
49
|
-
"@effected/tsconfig-json": "^0.
|
|
48
|
+
"@effected/store": "^0.6.0",
|
|
49
|
+
"@effected/tsconfig-json": "^0.7.0",
|
|
50
50
|
"@effected/walker": "^0.5.0",
|
|
51
51
|
"@effected/xdg": "^0.3.0",
|
|
52
52
|
"@effected/yaml": "^0.12.0",
|
|
53
53
|
"@microsoft/api-extractor-model": "^7.33.11",
|
|
54
54
|
"@shikijs/twoslash": "^4.4.3",
|
|
55
55
|
"@tsdoctor/bundle": "0.2.2",
|
|
56
|
-
"@tsdoctor/model": "0.
|
|
57
|
-
"@tsdoctor/
|
|
56
|
+
"@tsdoctor/model": "0.6.0",
|
|
57
|
+
"@tsdoctor/pages": "0.1.0",
|
|
58
|
+
"@tsdoctor/registry": "0.3.1",
|
|
58
59
|
"@tsdoctor/seo": "0.1.1",
|
|
59
|
-
"@tsdoctor/snapshot": "0.2.
|
|
60
|
+
"@tsdoctor/snapshot": "0.2.3",
|
|
61
|
+
"@tsdoctor/vfs": "0.2.0",
|
|
60
62
|
"@typescript/vfs": "^1.6.4",
|
|
61
63
|
"clsx": "^2.1.1",
|
|
62
64
|
"effect": "4.0.0-rc.109",
|
package/path-derivation.js
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
3
|
//#region src/path-derivation.ts
|
|
4
|
-
/** Extract unscoped name from a potentially scoped package name */
|
|
5
|
-
function unscopedName(packageName) {
|
|
6
|
-
return packageName.startsWith("@") ? packageName.split("/")[1] ?? packageName : packageName;
|
|
7
|
-
}
|
|
8
|
-
/** Normalize baseRoute: ensure leading slash, strip trailing slash, preserve root "/" */
|
|
9
|
-
function normalizeBaseRoute(route) {
|
|
10
|
-
const withSlash = route.startsWith("/") ? route : `/${route}`;
|
|
11
|
-
const stripped = withSlash.endsWith("/") ? withSlash.slice(0, -1) : withSlash;
|
|
12
|
-
return stripped === "" ? "/" : stripped;
|
|
13
|
-
}
|
|
14
4
|
function deriveOutputPaths(input) {
|
|
15
5
|
const { docsRoot, baseRoute, apiFolder, locales, defaultLang, versions, defaultVersion } = input;
|
|
16
6
|
const results = [];
|
|
@@ -43,24 +33,6 @@ function deriveOutputPaths(input) {
|
|
|
43
33
|
}
|
|
44
34
|
return results;
|
|
45
35
|
}
|
|
46
|
-
/**
|
|
47
|
-
* The API scope key derived from a base route.
|
|
48
|
-
*
|
|
49
|
-
* @remarks
|
|
50
|
-
* Load-bearing and previously duplicated. Config resolution registers each
|
|
51
|
-
* API's Twoslash environment under this key and the build program looks it up
|
|
52
|
-
* by the same key; if the two derivations disagree, every lookup misses and
|
|
53
|
-
* `getTransformer` falls back to the build-wide environment. Per-scope
|
|
54
|
-
* type-checking degrades to build-wide with no error and nothing visibly
|
|
55
|
-
* wrong in the output — the failure mode is silent, which is why one
|
|
56
|
-
* definition matters more here than the duplication was costing.
|
|
57
|
-
*
|
|
58
|
-
* Falls back to the package name so a single-API site mounted at `/` still
|
|
59
|
-
* gets a non-empty scope.
|
|
60
|
-
*/
|
|
61
|
-
function apiScopeOf(baseRoute, packageName) {
|
|
62
|
-
return baseRoute.replace(/^\//, "").split("/")[0] || packageName;
|
|
63
|
-
}
|
|
64
36
|
|
|
65
37
|
//#endregion
|
|
66
|
-
export {
|
|
38
|
+
export { deriveOutputPaths };
|
package/plugin.js
CHANGED
|
@@ -6,9 +6,9 @@ import { clearTypeRoutes } from "./twoslash-transformer.js";
|
|
|
6
6
|
import { TwoslashEnvironments } from "./services/TwoslashEnvironments.js";
|
|
7
7
|
import { VfsRegistry } from "./vfs-registry.js";
|
|
8
8
|
import { generateApiDocs } from "./build-program.js";
|
|
9
|
-
import { deriveOutputPaths, normalizeBaseRoute, unscopedName } from "./path-derivation.js";
|
|
10
9
|
import { fromDir, fromParentDir } from "./config-helpers.js";
|
|
11
10
|
import { classifyApiConfig, mergeLlmsPluginConfig } from "./config-utils.js";
|
|
11
|
+
import { deriveOutputPaths } from "./path-derivation.js";
|
|
12
12
|
import { resolveObservability } from "./schemas/observability.js";
|
|
13
13
|
import { PluginOptions } from "./schemas/config.js";
|
|
14
14
|
import { TwoslashCacheService } from "./services/TwoslashCacheService.js";
|
|
@@ -27,6 +27,7 @@ import fsSync from "node:fs";
|
|
|
27
27
|
import os from "node:os";
|
|
28
28
|
import path from "node:path";
|
|
29
29
|
import { fileURLToPath } from "node:url";
|
|
30
|
+
import { normalizeBaseRoute, unscopedName } from "@tsdoctor/pages";
|
|
30
31
|
import { Effect, FileSystem, ManagedRuntime, Option, Ref, Schema } from "effect";
|
|
31
32
|
|
|
32
33
|
//#region src/plugin.ts
|
|
@@ -54,10 +55,8 @@ function ApiExtractorPluginImpl(rawOptions) {
|
|
|
54
55
|
const isInert = classifyApiConfig(options) === "disabled";
|
|
55
56
|
const envLogLevel = process.env.LOG_LEVEL?.toLowerCase();
|
|
56
57
|
const buildId = `${process.pid}-${performance.now().toString(36)}`;
|
|
57
|
-
const { resolved: obs
|
|
58
|
+
const { resolved: obs } = resolveObservability({
|
|
58
59
|
...options.observability ? { observability: options.observability } : {},
|
|
59
|
-
...options.logLevel ? { logLevel: options.logLevel } : {},
|
|
60
|
-
...options.performance ? { performance: { ...options.performance.thresholds !== void 0 ? { thresholds: options.performance.thresholds } : {} } } : {},
|
|
61
60
|
...envLogLevel ? { envLogLevel } : {},
|
|
62
61
|
cwd: process.cwd(),
|
|
63
62
|
buildId
|
|
@@ -184,12 +183,6 @@ function ApiExtractorPluginImpl(rawOptions) {
|
|
|
184
183
|
clearTypeRoutes();
|
|
185
184
|
fileContextMap.clear();
|
|
186
185
|
issuesSink.reset();
|
|
187
|
-
for (const dep of deprecations) emitSync(PluginEvent.DeprecatedConfigUsed({
|
|
188
|
-
ctx: { buildId },
|
|
189
|
-
level: "warn",
|
|
190
|
-
key: dep.key,
|
|
191
|
-
replacement: dep.replacement
|
|
192
|
-
}));
|
|
193
186
|
if (!isInert) try {
|
|
194
187
|
const rspressConfigSubset = {
|
|
195
188
|
...rspressMultiVersion != null ? { multiVersion: rspressMultiVersion } : {},
|
package/prettier-formatter.js
CHANGED
|
@@ -1,37 +1,17 @@
|
|
|
1
|
-
import { addLogicalBlankLines } from "./code-post-processor.js";
|
|
2
1
|
import { PluginEvent } from "./observability/events.js";
|
|
3
2
|
import { emitSync, syncBuildId } from "./observability/sync-emitter.js";
|
|
4
|
-
import {
|
|
3
|
+
import { formatExampleCode } from "@tsdoctor/pages";
|
|
4
|
+
import { Effect, Result } from "effect";
|
|
5
5
|
|
|
6
6
|
//#region src/prettier-formatter.ts
|
|
7
7
|
/* v8 ignore start -- Prettier integration wrapper, tested via page generator integration tests */
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
javascript: "babel",
|
|
16
|
-
js: "babel",
|
|
17
|
-
jsx: "babel",
|
|
18
|
-
node: "babel"
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Default Prettier options for consistent formatting
|
|
22
|
-
*/
|
|
23
|
-
const PRETTIER_OPTIONS = {
|
|
24
|
-
printWidth: 80,
|
|
25
|
-
tabWidth: 2,
|
|
26
|
-
useTabs: false,
|
|
27
|
-
semi: true,
|
|
28
|
-
singleQuote: false,
|
|
29
|
-
trailingComma: "es5",
|
|
30
|
-
bracketSpacing: true,
|
|
31
|
-
arrowParens: "always"
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Format code using Prettier
|
|
9
|
+
* Format code using Prettier.
|
|
10
|
+
*
|
|
11
|
+
* The formatting itself is `formatExampleCode` in `@tsdoctor/pages`, so both
|
|
12
|
+
* adapters format identically. This wrapper keeps the adapter's fallthrough
|
|
13
|
+
* contract: a typed `ExampleFormatError` becomes a `PrettierError` event on
|
|
14
|
+
* the bus and the original code is returned.
|
|
35
15
|
*
|
|
36
16
|
* @param code - The code to format
|
|
37
17
|
* @param language - The code fence language (e.g., "typescript", "ts", "js")
|
|
@@ -39,39 +19,27 @@ const PRETTIER_OPTIONS = {
|
|
|
39
19
|
*/
|
|
40
20
|
async function formatCode(code, language) {
|
|
41
21
|
const start = performance.now();
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
const result = await Effect.runPromise(Effect.result(formatExampleCode(code, language)));
|
|
23
|
+
const formatTime = performance.now() - start;
|
|
24
|
+
if (Result.isSuccess(result)) return {
|
|
25
|
+
code: result.success,
|
|
45
26
|
success: true,
|
|
46
|
-
formatTime
|
|
27
|
+
formatTime
|
|
28
|
+
};
|
|
29
|
+
const cause = result.failure.cause;
|
|
30
|
+
const errorMsg = cause instanceof Error ? cause.message : String(cause);
|
|
31
|
+
emitSync(PluginEvent.PrettierError({
|
|
32
|
+
ctx: { buildId: syncBuildId() },
|
|
33
|
+
file: "unknown",
|
|
34
|
+
reason: errorMsg,
|
|
35
|
+
level: "warn"
|
|
36
|
+
}));
|
|
37
|
+
return {
|
|
38
|
+
code,
|
|
39
|
+
success: false,
|
|
40
|
+
error: errorMsg,
|
|
41
|
+
formatTime
|
|
47
42
|
};
|
|
48
|
-
try {
|
|
49
|
-
const formatted = await format(code, {
|
|
50
|
-
...PRETTIER_OPTIONS,
|
|
51
|
-
parser
|
|
52
|
-
});
|
|
53
|
-
const formatTime = performance.now() - start;
|
|
54
|
-
return {
|
|
55
|
-
code: addLogicalBlankLines(formatted.trim()),
|
|
56
|
-
success: true,
|
|
57
|
-
formatTime
|
|
58
|
-
};
|
|
59
|
-
} catch (error) {
|
|
60
|
-
const formatTime = performance.now() - start;
|
|
61
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
62
|
-
emitSync(PluginEvent.PrettierError({
|
|
63
|
-
ctx: { buildId: syncBuildId() },
|
|
64
|
-
file: "unknown",
|
|
65
|
-
reason: errorMsg,
|
|
66
|
-
level: "warn"
|
|
67
|
-
}));
|
|
68
|
-
return {
|
|
69
|
-
code,
|
|
70
|
-
success: false,
|
|
71
|
-
error: errorMsg,
|
|
72
|
-
formatTime
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
43
|
}
|
|
76
44
|
|
|
77
45
|
//#endregion
|
package/remark-with-api.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { PluginEvent } from "./observability/events.js";
|
|
2
2
|
import { emitSync, syncBuildId, syncSlowCodeBlockMs } from "./observability/sync-emitter.js";
|
|
3
|
-
import { formatCode } from "./prettier-formatter.js";
|
|
4
|
-
import { stripTwoslashDirectives } from "./markdown/helpers.js";
|
|
5
3
|
import { DEFAULT_SHIKI_THEMES } from "./markdown/shiki-utils.js";
|
|
6
4
|
import { VfsRegistry } from "./vfs-registry.js";
|
|
7
5
|
import { setTwoslashFile } from "./twoslash-access.js";
|
|
8
6
|
import { createTwoslashTimingWrapper } from "./twoslash-timing-wrapper.js";
|
|
7
|
+
import { formatCode } from "./prettier-formatter.js";
|
|
8
|
+
import { stripTwoslashDirectives } from "@tsdoctor/pages";
|
|
9
9
|
import { codeToHast, hastToHtml } from "shiki";
|
|
10
10
|
import { visit } from "unist-util-visit";
|
|
11
11
|
|
|
12
12
|
//#region src/remark-with-api.ts
|
|
13
|
+
/* v8 ignore start -- remark plugin, requires MDX compilation context */
|
|
13
14
|
/**
|
|
14
15
|
* Supported languages for with-api code blocks
|
|
15
16
|
* Based on GitHub Linguist standard aliases:
|