rspress-plugin-api-extractor 0.14.0 → 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 +4 -4
- package/build-stages.js +98 -281
- package/config-helpers.js +1 -1
- package/emit/mdx.js +311 -0
- package/emit/meta.js +62 -0
- package/index.d.ts +1 -42
- package/layers/build-metrics.js +1 -2
- package/layers/config-resolution.js +3 -5
- package/layers/type-environment.js +1 -2
- package/llms-program.js +3 -3
- package/markdown/helpers.js +10 -176
- package/observability/sinks/console-sink.js +0 -1
- package/observability/sinks/metrics-sink.js +0 -2
- package/package.json +6 -5
- package/path-derivation.js +1 -29
- package/plugin.js +2 -1
- package/prettier-formatter.js +27 -59
- package/remark-with-api.js +3 -2
- package/schemas/config.js +1 -18
- package/schemas/observability.js +0 -2
- package/schemas/performance.js +0 -1
- package/services/TwoslashCacheService.js +1 -1
- package/twoslash-transformer.js +14 -2
- package/code-post-processor.js +0 -38
- package/llms-processing.js +0 -270
- package/markdown/page-generators/class-page.js +0 -363
- package/markdown/page-generators/enum-page.js +0 -152
- package/markdown/page-generators/function-page.js +0 -127
- package/markdown/page-generators/index-pages.js +0 -25
- package/markdown/page-generators/interface-page.js +0 -310
- package/markdown/page-generators/namespace-page.js +0 -277
- package/markdown/page-generators/type-alias-page.js +0 -110
- package/markdown/page-generators/variable-page.js +0 -110
- package/markdown/prose-linker.js +0 -22
- package/twoslash-cache.js +0 -174
- package/twoslash-patterns.js +0 -87
package/markdown/helpers.js
CHANGED
|
@@ -1,43 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { formatCode } from "../prettier-formatter.js";
|
|
3
|
-
import { TypeReferenceExtractor, emitFrontmatterBlock } from "@tsdoctor/model";
|
|
1
|
+
import { emitFrontmatterBlock } from "@tsdoctor/model";
|
|
4
2
|
|
|
5
3
|
//#region src/markdown/helpers.ts
|
|
6
4
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*/
|
|
10
|
-
function generateAvailableFrom(packageName, availableFrom) {
|
|
11
|
-
if (!availableFrom || availableFrom.length <= 1) return "";
|
|
12
|
-
return `Available from: ${availableFrom.map((ep) => ep === "default" ? `\`${packageName}\`` : `\`${packageName}/${ep}\``).join(", ")}\n\n`;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* 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.
|
|
16
7
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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.
|
|
19
13
|
*
|
|
20
|
-
* @
|
|
21
|
-
* @param apiItemName - The name of the API item being documented
|
|
22
|
-
* @param packageName - The package name for imports
|
|
23
|
-
* @param suppressErrors - Whether to suppress all TypeScript errors (default: true)
|
|
24
|
-
* @returns Object with prepared code and whether it's TypeScript
|
|
14
|
+
* @packageDocumentation
|
|
25
15
|
*/
|
|
26
|
-
function prepareExampleCode(example, apiItemName, packageName, suppressErrors = true) {
|
|
27
|
-
const { language, code } = example;
|
|
28
|
-
if (!(language === "typescript" || language === "ts" || language === "javascript" || language === "js")) return {
|
|
29
|
-
code,
|
|
30
|
-
isTypeScript: false,
|
|
31
|
-
language
|
|
32
|
-
};
|
|
33
|
-
const importLine = `import { ${apiItemName} } from "${packageName}";`;
|
|
34
|
-
const finalCode = code.includes(`from "${packageName}"`) || code.includes(`from '${packageName}'`) ? code : `${importLine}\n${code}`;
|
|
35
|
-
return {
|
|
36
|
-
code: `${suppressErrors ? "// @noErrors\n" : ""}${finalCode}`,
|
|
37
|
-
isTypeScript: true,
|
|
38
|
-
language: "typescript"
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
16
|
/**
|
|
42
17
|
* Collapse newlines and runs of whitespace to single spaces, and trim.
|
|
43
18
|
*
|
|
@@ -52,28 +27,6 @@ function cleanYamlValue(value) {
|
|
|
52
27
|
return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
53
28
|
}
|
|
54
29
|
/**
|
|
55
|
-
* Escape generic type parameters in MDX by wrapping them in backticks.
|
|
56
|
-
*
|
|
57
|
-
* Prevents MDX from interpreting `<T>`, `<TEnv>`, etc. as JSX tags by
|
|
58
|
-
* wrapping them in inline code backticks.
|
|
59
|
-
*
|
|
60
|
-
* @param text - The text containing generic type parameters
|
|
61
|
-
* @returns Text with generics wrapped in backticks
|
|
62
|
-
*
|
|
63
|
-
* @example
|
|
64
|
-
* ```ts
|
|
65
|
-
* escapeMdxGenerics("Returns Promise<T>"); // "Returns Promise`<T>`"
|
|
66
|
-
* escapeMdxGenerics("Map<K, V> extends..."); // "Map`<K, V>` extends..."
|
|
67
|
-
* escapeMdxGenerics("`Pipeline<I, O>`"); // "`Pipeline<I, O>`" (unchanged)
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
function escapeMdxGenerics(text) {
|
|
71
|
-
return text.split(/(`[^`]+`)/g).map((part) => {
|
|
72
|
-
if (part.startsWith("`") && part.endsWith("`")) return part;
|
|
73
|
-
return part.replace(/<([A-Z][A-Za-z0-9_]*(?:\s+extends\s+[^>]+)?(?:,\s*[A-Z][A-Za-z0-9_]*(?:\s+extends\s+[^>]+)?)*)>/g, "`<$1>`");
|
|
74
|
-
}).join("");
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
30
|
* Build a structured page title for API documentation.
|
|
78
31
|
*
|
|
79
32
|
* Creates a title in the format: `{entityName} | {singularName} | API | {apiName}`
|
|
@@ -141,125 +94,6 @@ function generateFrontmatter(entityName, description, singularName, apiName, tag
|
|
|
141
94
|
if (headEntries.length > 0) data.head = headEntries;
|
|
142
95
|
return emitFrontmatterBlock(data);
|
|
143
96
|
}
|
|
144
|
-
/**
|
|
145
|
-
* Strip Twoslash directives from code for display purposes.
|
|
146
|
-
*
|
|
147
|
-
* Removes Twoslash directive comments like `// @noErrors`, `// @errors: 2304`,
|
|
148
|
-
* `// @filename: ...`, etc. from code so users see clean output and don't
|
|
149
|
-
* copy directives when using the copy button.
|
|
150
|
-
*
|
|
151
|
-
* Also handles cut directives:
|
|
152
|
-
* - `// ---cut---` - Removes this line and all lines before it
|
|
153
|
-
* - `// ---cut-before---` - Same as ---cut---
|
|
154
|
-
* - `// ---cut-after---` - Removes this line and all lines after it
|
|
155
|
-
*
|
|
156
|
-
* @param code - The code containing Twoslash directives
|
|
157
|
-
* @returns Code with Twoslash directives removed
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* ```ts
|
|
161
|
-
* const display = stripTwoslashDirectives("// @noErrors\nconst x = 1;");
|
|
162
|
-
* // Returns: "const x = 1;"
|
|
163
|
-
* ```
|
|
164
|
-
*/
|
|
165
|
-
function stripTwoslashDirectives(code) {
|
|
166
|
-
const lines = code.split("\n");
|
|
167
|
-
let cutBeforeIndex = -1;
|
|
168
|
-
let cutAfterIndex = -1;
|
|
169
|
-
const cutRanges = [];
|
|
170
|
-
const cutStartStack = [];
|
|
171
|
-
for (let i = 0; i < lines.length; i++) {
|
|
172
|
-
const trimmed = lines[i].trim();
|
|
173
|
-
const cutType = classifyCutDirective(trimmed);
|
|
174
|
-
if (cutType === "cut-before") cutBeforeIndex = i;
|
|
175
|
-
else if (cutType === "cut-after") cutAfterIndex = i;
|
|
176
|
-
else if (cutType === "cut-start") cutStartStack.push(i);
|
|
177
|
-
else if (cutType === "cut-end") {
|
|
178
|
-
const startIdx = cutStartStack.pop();
|
|
179
|
-
if (startIdx !== void 0) cutRanges.push([startIdx, i]);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
let filteredLines = lines;
|
|
183
|
-
if (cutBeforeIndex >= 0) {
|
|
184
|
-
filteredLines = filteredLines.slice(cutBeforeIndex + 1);
|
|
185
|
-
if (cutAfterIndex >= 0) cutAfterIndex = cutAfterIndex - cutBeforeIndex - 1;
|
|
186
|
-
for (const range of cutRanges) {
|
|
187
|
-
range[0] -= cutBeforeIndex + 1;
|
|
188
|
-
range[1] -= cutBeforeIndex + 1;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
if (cutAfterIndex >= 0) filteredLines = filteredLines.slice(0, cutAfterIndex);
|
|
192
|
-
const excludedLines = /* @__PURE__ */ new Set();
|
|
193
|
-
for (const [start, end] of cutRanges) for (let i = start; i <= end; i++) if (i >= 0 && i < filteredLines.length) excludedLines.add(i);
|
|
194
|
-
return filteredLines.filter((line, i) => {
|
|
195
|
-
if (excludedLines.has(i)) return false;
|
|
196
|
-
const trimmed = line.trim();
|
|
197
|
-
if (isTwoslashDirective(trimmed)) return false;
|
|
198
|
-
return true;
|
|
199
|
-
}).join("\n").trim();
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Format import statements with cut directive for hidden imports.
|
|
203
|
-
*
|
|
204
|
-
* Prepends import statements followed by `// ---cut---` so Twoslash can
|
|
205
|
-
* resolve the types but the imports are hidden from rendered output.
|
|
206
|
-
*
|
|
207
|
-
* @param imports - Import statements to format
|
|
208
|
-
* @returns Formatted import block with cut directive, or empty string if no imports
|
|
209
|
-
*
|
|
210
|
-
* @example
|
|
211
|
-
* ```ts
|
|
212
|
-
* const imports = [{ packageName: "zod", symbols: new Set(["ZodType"]), typeOnly: true }];
|
|
213
|
-
* const block = formatImportsWithCut(imports);
|
|
214
|
-
* // Returns:
|
|
215
|
-
* // import type { ZodType } from "zod";
|
|
216
|
-
* // // ---cut---
|
|
217
|
-
* ```
|
|
218
|
-
*/
|
|
219
|
-
function formatImportsWithCut(imports) {
|
|
220
|
-
if (imports.length === 0) return "";
|
|
221
|
-
return `${TypeReferenceExtractor.formatImports(imports).join("\n")}\n// ---cut---\n`;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Prepend hidden imports to code using the Twoslash cut directive.
|
|
225
|
-
*
|
|
226
|
-
* This enables type resolution for external types while hiding the import
|
|
227
|
-
* statements from rendered output. The existing `stripTwoslashDirectives()`
|
|
228
|
-
* function handles removing the cut block for clipboard copying.
|
|
229
|
-
*
|
|
230
|
-
* @param code - The code to prepend imports to
|
|
231
|
-
* @param imports - Import statements to add
|
|
232
|
-
* @returns Code with imports prepended (if any), or original code if no imports
|
|
233
|
-
*
|
|
234
|
-
* @example
|
|
235
|
-
* ```ts
|
|
236
|
-
* const code = "function foo(): RsbuildPlugin";
|
|
237
|
-
* const imports = [{ packageName: "@rsbuild/core", symbols: new Set(["RsbuildPlugin"]), typeOnly: true }];
|
|
238
|
-
* const result = prependHiddenImports(code, imports);
|
|
239
|
-
* // Returns:
|
|
240
|
-
* // import type { RsbuildPlugin } from "@rsbuild/core";
|
|
241
|
-
* // // ---cut---
|
|
242
|
-
* // function foo(): RsbuildPlugin
|
|
243
|
-
* ```
|
|
244
|
-
*/
|
|
245
|
-
function prependHiddenImports(code, imports) {
|
|
246
|
-
const importBlock = formatImportsWithCut(imports);
|
|
247
|
-
return importBlock ? importBlock + code : code;
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Format example code using Prettier for consistent styling.
|
|
251
|
-
*
|
|
252
|
-
* Wraps the Prettier formatter with error handling and context tracking.
|
|
253
|
-
* If formatting fails, returns the original code (fallthrough behavior).
|
|
254
|
-
*
|
|
255
|
-
* @param code - The code to format
|
|
256
|
-
* @param language - The code fence language (e.g., "typescript", "ts")
|
|
257
|
-
* @param _context - Optional context (reserved for future use)
|
|
258
|
-
* @returns The formatted code (or original if formatting fails)
|
|
259
|
-
*/
|
|
260
|
-
async function formatExampleCode(code, language, _context) {
|
|
261
|
-
return (await formatCode(code, language)).code;
|
|
262
|
-
}
|
|
263
97
|
|
|
264
98
|
//#endregion
|
|
265
|
-
export {
|
|
99
|
+
export { generateFrontmatter };
|
|
@@ -23,7 +23,6 @@ function render(event) {
|
|
|
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
25
|
case "ModelLoaded": return `loaded model: ${event.itemCount} items, ${event.entryPoints} entry point(s) (${event.durationMs}ms)`;
|
|
26
|
-
case "ConfigResolved": return `resolved ${event.baseRoute}: ${event.categoryCount} categories, ${event.externalCount} external`;
|
|
27
26
|
case "TwoslashDiagnostic": return `Twoslash TS${event.code} in ${event.file}:${event.line}:${event.col}: ${event.message}`;
|
|
28
27
|
case "TwoslashCheckFailed": return `Twoslash check failed (TS${event.code}) in ${event.file}; ${event.fsMapKeys.length} VFS files`;
|
|
29
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)";
|
|
@@ -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,7 +41,7 @@
|
|
|
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",
|
|
@@ -53,11 +53,12 @@
|
|
|
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
60
|
"@tsdoctor/snapshot": "0.2.3",
|
|
60
|
-
"@tsdoctor/vfs": "0.
|
|
61
|
+
"@tsdoctor/vfs": "0.2.0",
|
|
61
62
|
"@typescript/vfs": "^1.6.4",
|
|
62
63
|
"clsx": "^2.1.1",
|
|
63
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
|
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:
|
package/schemas/config.js
CHANGED
|
@@ -9,24 +9,9 @@ import { ApiItemKind } from "@microsoft/api-extractor-model";
|
|
|
9
9
|
* an async loader function, or a URL.
|
|
10
10
|
*/
|
|
11
11
|
const ModelInput = Schema.declare((input) => typeof input === "string" || typeof input === "function" || input instanceof URL);
|
|
12
|
-
/**
|
|
13
|
-
* Verbosity level for plugin build output.
|
|
14
|
-
*
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
const LogLevel = Schema.Literals([
|
|
18
|
-
"none",
|
|
19
|
-
"info",
|
|
20
|
-
"verbose",
|
|
21
|
-
"debug",
|
|
22
|
-
"warn",
|
|
23
|
-
"error"
|
|
24
|
-
]);
|
|
25
12
|
const ExternalPackageSpec = Schema.Struct({
|
|
26
13
|
name: Schema.String,
|
|
27
|
-
version: Schema.String
|
|
28
|
-
tsconfig: Schema.optional(ModelInput),
|
|
29
|
-
compilerOptions: Schema.optional(Schema.Unknown)
|
|
14
|
+
version: Schema.String
|
|
30
15
|
});
|
|
31
16
|
const AutoDetectDependencies = Schema.Struct({
|
|
32
17
|
dependencies: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
|
|
@@ -67,8 +52,6 @@ const CategoryConfig = Schema.Struct({
|
|
|
67
52
|
folderName: Schema.String,
|
|
68
53
|
/** API item kinds included in this category. */
|
|
69
54
|
itemKinds: Schema.optional(Schema.mutable(Schema.Array(ApiItemKindSchema))),
|
|
70
|
-
/** TSDoc modifier tag that marks items for this category. */
|
|
71
|
-
tsdocModifier: Schema.optional(Schema.String),
|
|
72
55
|
/** Whether the sidebar section is collapsible. Defaults to `true`. */
|
|
73
56
|
collapsible: Schema.Boolean.pipe(Schema.withDecodingDefault(Effect.succeed(true))),
|
|
74
57
|
/** Whether the sidebar section starts collapsed. Defaults to `true`. */
|
package/schemas/observability.js
CHANGED
|
@@ -22,7 +22,6 @@ const DEFAULT_THRESHOLDS = {
|
|
|
22
22
|
slowPageGeneration: 500,
|
|
23
23
|
slowApiLoad: 1e3,
|
|
24
24
|
slowFileOperation: 50,
|
|
25
|
-
slowHttpRequest: 2e3,
|
|
26
25
|
slowDbOperation: 100
|
|
27
26
|
};
|
|
28
27
|
function normalizeLevel(value) {
|
|
@@ -43,7 +42,6 @@ function resolveObservability(input) {
|
|
|
43
42
|
slowPageGeneration: merged.slowPageGeneration ?? DEFAULT_THRESHOLDS.slowPageGeneration,
|
|
44
43
|
slowApiLoad: merged.slowApiLoad ?? DEFAULT_THRESHOLDS.slowApiLoad,
|
|
45
44
|
slowFileOperation: merged.slowFileOperation ?? DEFAULT_THRESHOLDS.slowFileOperation,
|
|
46
|
-
slowHttpRequest: merged.slowHttpRequest ?? DEFAULT_THRESHOLDS.slowHttpRequest,
|
|
47
45
|
slowDbOperation: merged.slowDbOperation ?? DEFAULT_THRESHOLDS.slowDbOperation
|
|
48
46
|
};
|
|
49
47
|
const pi = input.observability?.progressInterval;
|
package/schemas/performance.js
CHANGED
|
@@ -6,7 +6,6 @@ const PerformanceThresholds = Schema.Struct({
|
|
|
6
6
|
slowPageGeneration: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(500))),
|
|
7
7
|
slowApiLoad: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(1e3))),
|
|
8
8
|
slowFileOperation: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(50))),
|
|
9
|
-
slowHttpRequest: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(2e3))),
|
|
10
9
|
slowDbOperation: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(100)))
|
|
11
10
|
});
|
|
12
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AppDirsLive, PlatformLive } from "../layers/xdg.js";
|
|
2
|
-
import { decodeTwoslashCache, encodeTwoslashCache, makeTwoslashCache, twoslashBlobKey } from "../twoslash-cache.js";
|
|
3
2
|
import { Context, Effect, Layer, Option, Path } from "effect";
|
|
3
|
+
import { decodeTwoslashCache, encodeTwoslashCache, makeTwoslashCache, twoslashBlobKey } from "@tsdoctor/vfs";
|
|
4
4
|
import { Cache } from "@effected/store";
|
|
5
5
|
import { AppDirs } from "@effected/xdg";
|
|
6
6
|
|
package/twoslash-transformer.js
CHANGED
|
@@ -10,9 +10,9 @@ import { toHast } from "mdast-util-to-hast";
|
|
|
10
10
|
/* v8 ignore start -- Shiki/Twoslash integration, requires full highlighter setup for testing */
|
|
11
11
|
/**
|
|
12
12
|
* Module-level type routes map for resolving link references.
|
|
13
|
-
*
|
|
13
|
+
* Populated per build by `addTypeRoutes` and reset by `clearTypeRoutes`.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
const typeRoutes = /* @__PURE__ */ new Map();
|
|
16
16
|
/**
|
|
17
17
|
* Transform TSDoc link tag syntax to markdown links or plain text.
|
|
18
18
|
*
|
|
@@ -396,6 +396,18 @@ var TwoslashEnvironmentRegistry = class {
|
|
|
396
396
|
this.handleTwoslashError(error, code, file);
|
|
397
397
|
}
|
|
398
398
|
};
|
|
399
|
+
/**
|
|
400
|
+
* Cross-link routes used to turn type names in hover docs into links.
|
|
401
|
+
*
|
|
402
|
+
* @remarks
|
|
403
|
+
* These were `static` members of the old singleton, but they are cross-link
|
|
404
|
+
* DATA, not type-checking state — they only ever read and wrote the
|
|
405
|
+
* module-level `typeRoutes` map above, and they share their concern with
|
|
406
|
+
* the prose cross-linker rather than with the environment registry. They
|
|
407
|
+
* are deliberately NOT part of {@link TwoslashEnvironments}: folding them in
|
|
408
|
+
* would widen the service's surface with state that has nothing to do with
|
|
409
|
+
* compiler configurations.
|
|
410
|
+
*/
|
|
399
411
|
/** Merge routes in, so a multi-API build accumulates every scope's names. */
|
|
400
412
|
function addTypeRoutes(routes) {
|
|
401
413
|
for (const [name, route] of routes) typeRoutes.set(name, route);
|
package/code-post-processor.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { isTwoslashDirective } from "./twoslash-patterns.js";
|
|
2
|
-
|
|
3
|
-
//#region src/code-post-processor.ts
|
|
4
|
-
/**
|
|
5
|
-
* Add logical blank lines between code sections for visual clarity.
|
|
6
|
-
*
|
|
7
|
-
* Runs after Prettier formatting to insert breathing room between
|
|
8
|
-
* imports, comments, and return statements without affecting the
|
|
9
|
-
* structural formatting Prettier already applied.
|
|
10
|
-
*/
|
|
11
|
-
function addLogicalBlankLines(code) {
|
|
12
|
-
const lines = code.split("\n");
|
|
13
|
-
const result = [];
|
|
14
|
-
let inMultiLineImport = false;
|
|
15
|
-
for (let i = 0; i < lines.length; i++) {
|
|
16
|
-
const line = lines[i];
|
|
17
|
-
const trimmed = line.trim();
|
|
18
|
-
const wasInMultiLineImport = inMultiLineImport;
|
|
19
|
-
if (!inMultiLineImport && trimmed.startsWith("import ") && !trimmed.endsWith(";")) inMultiLineImport = true;
|
|
20
|
-
else if (inMultiLineImport && trimmed.endsWith(";")) inMultiLineImport = false;
|
|
21
|
-
const isCurrentImport = trimmed.startsWith("import ") || wasInMultiLineImport;
|
|
22
|
-
if (result.length > 0 && !isCurrentImport) {
|
|
23
|
-
const prevTrimmed = result[result.length - 1].trim();
|
|
24
|
-
if (prevTrimmed !== "") {
|
|
25
|
-
const isDirective = isTwoslashDirective(trimmed);
|
|
26
|
-
const prevIsImportEnd = prevTrimmed.startsWith("import ") && prevTrimmed.endsWith(";") || /}\s*from\s+/.test(prevTrimmed) && prevTrimmed.endsWith(";");
|
|
27
|
-
if (prevIsImportEnd && trimmed !== "" && !isDirective) result.push("");
|
|
28
|
-
if (trimmed.startsWith("//") && !isDirective && !prevTrimmed.startsWith("//") && !prevIsImportEnd) result.push("");
|
|
29
|
-
if (/^return[\s;(]/.test(trimmed) && !prevTrimmed.startsWith("//")) result.push("");
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
result.push(line);
|
|
33
|
-
}
|
|
34
|
-
return result.join("\n");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
//#endregion
|
|
38
|
-
export { addLogicalBlankLines };
|