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.
Files changed (37) hide show
  1. package/BuildEnv.js +0 -1
  2. package/build-program.js +4 -4
  3. package/build-stages.js +98 -281
  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 +1 -42
  8. package/layers/build-metrics.js +1 -2
  9. package/layers/config-resolution.js +3 -5
  10. package/layers/type-environment.js +1 -2
  11. package/llms-program.js +3 -3
  12. package/markdown/helpers.js +10 -176
  13. package/observability/sinks/console-sink.js +0 -1
  14. package/observability/sinks/metrics-sink.js +0 -2
  15. package/package.json +6 -5
  16. package/path-derivation.js +1 -29
  17. package/plugin.js +2 -1
  18. package/prettier-formatter.js +27 -59
  19. package/remark-with-api.js +3 -2
  20. package/schemas/config.js +1 -18
  21. package/schemas/observability.js +0 -2
  22. package/schemas/performance.js +0 -1
  23. package/services/TwoslashCacheService.js +1 -1
  24. package/twoslash-transformer.js +14 -2
  25. package/code-post-processor.js +0 -38
  26. package/llms-processing.js +0 -270
  27. package/markdown/page-generators/class-page.js +0 -363
  28. package/markdown/page-generators/enum-page.js +0 -152
  29. package/markdown/page-generators/function-page.js +0 -127
  30. package/markdown/page-generators/index-pages.js +0 -25
  31. package/markdown/page-generators/interface-page.js +0 -310
  32. package/markdown/page-generators/namespace-page.js +0 -277
  33. package/markdown/page-generators/type-alias-page.js +0 -110
  34. package/markdown/page-generators/variable-page.js +0 -110
  35. package/markdown/prose-linker.js +0 -22
  36. package/twoslash-cache.js +0 -174
  37. package/twoslash-patterns.js +0 -87
@@ -1,43 +1,18 @@
1
- import { classifyCutDirective, isTwoslashDirective } from "../twoslash-patterns.js";
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
- * Generate an "Available from" line for items exported from multiple entry points.
8
- * Returns empty string if only one entry point or none provided.
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
- * Prepares the code with imports and error directives but does NOT render HTML.
18
- * Use this for raw markdown output or as input to pre-rendering.
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
- * @param example - The example with language and code
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 { escapeMdxGenerics, formatExampleCode, generateAvailableFrom, generateFrontmatter, prepareExampleCode, prependHiddenImports, stripTwoslashDirectives };
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.14.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.7.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.5.0",
57
- "@tsdoctor/registry": "0.3.0",
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.1.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",
@@ -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 { apiScopeOf, deriveOutputPaths, normalizeBaseRoute, unscopedName };
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
@@ -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 { format } from "prettier";
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
- * Map code fence languages to Prettier parsers
10
- */
11
- const LANGUAGE_TO_PARSER = {
12
- typescript: "typescript",
13
- ts: "typescript",
14
- tsx: "typescript",
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 parser = LANGUAGE_TO_PARSER[language.toLowerCase()];
43
- if (!parser) return {
44
- code,
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: performance.now() - start
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
@@ -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`. */
@@ -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;
@@ -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
 
@@ -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
- * This is set by TwoslashManager.setTypeRoutes() before initialization.
13
+ * Populated per build by `addTypeRoutes` and reset by `clearTypeRoutes`.
14
14
  */
15
- let typeRoutes = /* @__PURE__ */ new Map();
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);
@@ -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 };