rspress-plugin-api-extractor 0.13.3 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BuildEnv.js +0 -1
- package/build-program.js +5 -5
- package/build-stages.js +98 -282
- package/config-helpers.js +1 -1
- package/emit/mdx.js +311 -0
- package/emit/meta.js +62 -0
- package/index.d.ts +6 -70
- package/layers/build-metrics.js +1 -2
- package/layers/config-resolution.js +5 -8
- package/layers/type-environment.js +5 -4
- package/llms-program.js +3 -3
- package/markdown/helpers.js +10 -177
- package/observability/sinks/console-sink.js +1 -3
- package/observability/sinks/metrics-sink.js +0 -2
- package/package.json +9 -7
- package/path-derivation.js +1 -29
- package/plugin.js +3 -10
- package/prettier-formatter.js +27 -59
- package/remark-with-api.js +3 -2
- package/schemas/config.js +3 -29
- package/schemas/observability.js +8 -23
- package/schemas/performance.js +1 -7
- package/services/TwoslashCacheService.js +18 -14
- package/services/TypeRegistryService.js +4 -4
- package/shiki-transformer.js +12 -51
- package/twoslash-transformer.js +16 -49
- package/api-extracted-package.js +0 -471
- package/code-post-processor.js +0 -38
- package/frontmatter.js +0 -176
- package/llms-processing.js +0 -270
- package/markdown/page-generators/class-page.js +0 -364
- package/markdown/page-generators/enum-page.js +0 -152
- package/markdown/page-generators/function-page.js +0 -128
- package/markdown/page-generators/index-pages.js +0 -25
- package/markdown/page-generators/interface-page.js +0 -311
- package/markdown/page-generators/namespace-page.js +0 -278
- package/markdown/page-generators/type-alias-page.js +0 -111
- package/markdown/page-generators/variable-page.js +0 -111
- package/markdown/prose-linker.js +0 -22
- package/tsconfig-parser.js +0 -115
- package/twoslash-cache.js +0 -174
- package/twoslash-patterns.js +0 -87
- package/type-reference-extractor.js +0 -199
- package/typescript-config.js +0 -170
package/twoslash-cache.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { gunzipSync, gzipSync } from "node:zlib";
|
|
3
|
-
|
|
4
|
-
//#region src/twoslash-cache.ts
|
|
5
|
-
/**
|
|
6
|
-
* Persisted Twoslash result cache.
|
|
7
|
-
*
|
|
8
|
-
* Type-checking code blocks is by far the dominant cost of the render phase —
|
|
9
|
-
* measured at ~97% of it, concentrated in the minority of blocks that carry an
|
|
10
|
-
* `@example` (see `render-phase-instrumentation.md`). `@shikijs/twoslash`
|
|
11
|
-
* exposes a first-class `typesCache` seam for exactly this, so the work here is
|
|
12
|
-
* a keying scheme and a store rather than a new interception point.
|
|
13
|
-
*
|
|
14
|
-
* ## Soundness
|
|
15
|
-
*
|
|
16
|
-
* A Twoslash result depends on the code, the compiler options, the declarations
|
|
17
|
-
* it is checked against, and the compiler doing the checking. The keys cover
|
|
18
|
-
* all four: the per-entry key carries the code, its language and the compiler
|
|
19
|
-
* options; {@link twoslashEnvHash} carries the declarations and the TypeScript
|
|
20
|
-
* version.
|
|
21
|
-
*
|
|
22
|
-
* The TypeScript version is load-bearing and easy to overlook — `lib.d.ts`
|
|
23
|
-
* ships with the compiler and inference changes between releases, so an upgrade
|
|
24
|
-
* against unchanged declarations yields different hovers. Omitting it would let
|
|
25
|
-
* a warm cache serve results from the previous compiler and stay wrong until
|
|
26
|
-
* the API's own declarations happened to change.
|
|
27
|
-
*
|
|
28
|
-
* NOT covered, and covered instead by {@link TWOSLASH_CACHE_FORMAT}: the
|
|
29
|
-
* `@shikijs/twoslash` / `twoslash` renderer version, which determines the shape
|
|
30
|
-
* of the stored `nodes`. Bump the format constant when upgrading those, since
|
|
31
|
-
* nothing derives it automatically.
|
|
32
|
-
*
|
|
33
|
-
* ## Invalidation granularity
|
|
34
|
-
*
|
|
35
|
-
* The consequence of that soundness is coarse invalidation: any VFS change
|
|
36
|
-
* discards the whole generation, because a declaration change anywhere can
|
|
37
|
-
* legitimately change any block's inferred types. So this cache makes repeat
|
|
38
|
-
* builds over an UNCHANGED API nearly free — CI re-runs, prose-only edits,
|
|
39
|
-
* theme and config changes, rebuilding a site without touching the library —
|
|
40
|
-
* and does nothing for the build right after an API item changes.
|
|
41
|
-
*
|
|
42
|
-
* Sharpening that would need per-scope type environments, so one package's
|
|
43
|
-
* change stops invalidating every other package's blocks. That is fix (b) in
|
|
44
|
-
* `render-phase-instrumentation.md`, tracked as a correctness fix; it would
|
|
45
|
-
* make this cache substantially more effective on a multi-API site as a side
|
|
46
|
-
* effect.
|
|
47
|
-
*
|
|
48
|
-
* ## Synchronous by necessity
|
|
49
|
-
*
|
|
50
|
-
* `TwoslashTypesCache.read`/`write` are synchronous — they are called from
|
|
51
|
-
* inside Shiki's `preprocess` hook. Persistence is therefore load-once at
|
|
52
|
-
* startup and save-once at the end, against an in-memory map; there is no
|
|
53
|
-
* per-entry I/O. See `TwoslashCacheService`.
|
|
54
|
-
*/
|
|
55
|
-
/**
|
|
56
|
-
* Bumped when the stored shape changes, so an older blob is treated as absent
|
|
57
|
-
* rather than deserialized into the wrong shape.
|
|
58
|
-
*
|
|
59
|
-
* Also the manual lever for renderer changes: bump this when upgrading
|
|
60
|
-
* `@shikijs/twoslash` or `twoslash`, whose versions determine the shape of the
|
|
61
|
-
* stored `nodes` and are not derived into any key.
|
|
62
|
-
*/
|
|
63
|
-
const TWOSLASH_CACHE_FORMAT = 1;
|
|
64
|
-
function sha256(input) {
|
|
65
|
-
return createHash("sha256").update(input).digest("hex");
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Fingerprint the type environment a generation is checked against.
|
|
69
|
-
*
|
|
70
|
-
* Covers the declarations (`vfs`) and the compiler that interprets them
|
|
71
|
-
* (`toolchain`). The VFS is hashed over sorted `path\0content` pairs so the
|
|
72
|
-
* digest is stable against map iteration order.
|
|
73
|
-
*
|
|
74
|
-
* `toolchain` must carry the TypeScript version. The declarations alone do not
|
|
75
|
-
* determine the answer: `lib.d.ts` ships with the compiler and inference
|
|
76
|
-
* behaviour changes between releases, so upgrading TypeScript against unchanged
|
|
77
|
-
* declarations produces different hovers. Without the version in the key the
|
|
78
|
-
* warm cache would serve results computed by the previous compiler, and stay
|
|
79
|
-
* wrong until the API's own declarations happened to change.
|
|
80
|
-
*
|
|
81
|
-
* Compiler OPTIONS are deliberately not folded in here — they belong on the
|
|
82
|
-
* per-entry key, so one generation can hold results from the several
|
|
83
|
-
* configurations a multi-API site may declare.
|
|
84
|
-
*/
|
|
85
|
-
function twoslashEnvHash(vfs, toolchain) {
|
|
86
|
-
const hash = createHash("sha256");
|
|
87
|
-
hash.update(`format:${1}\0toolchain:${toolchain}\0`);
|
|
88
|
-
for (const key of [...vfs.keys()].sort()) hash.update(`${key}\0${vfs.get(key) ?? ""}\0`);
|
|
89
|
-
return hash.digest("hex");
|
|
90
|
-
}
|
|
91
|
-
/** JSON with object keys sorted, so equivalent options hash identically. */
|
|
92
|
-
function stableStringify(value) {
|
|
93
|
-
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
94
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
95
|
-
return `{${Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).join(",")}}`;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Per-entry key: the code, its language, and the compiler configuration it is
|
|
99
|
-
* checked under.
|
|
100
|
-
*
|
|
101
|
-
* The configuration matters because two APIs on one site may be documented
|
|
102
|
-
* under different `tsconfig`s — the same source checked under different options
|
|
103
|
-
* can produce different types, so it must not share a cache entry.
|
|
104
|
-
*/
|
|
105
|
-
function twoslashEntryKey(code, lang, compilerOptions) {
|
|
106
|
-
return sha256(`${lang ?? "ts"}\0${stableStringify(compilerOptions ?? {})}\0${code}`);
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* The cache key a whole generation is stored under. One blob per environment,
|
|
110
|
-
* so a changed environment reads as a miss rather than serving stale results.
|
|
111
|
-
*/
|
|
112
|
-
function twoslashBlobKey(envHash) {
|
|
113
|
-
return `twoslash/v${1}/${envHash}`;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Build a synchronous Twoslash cache over an in-memory map, optionally seeded
|
|
117
|
-
* with entries loaded from a previous build.
|
|
118
|
-
*/
|
|
119
|
-
function makeTwoslashCache(initial) {
|
|
120
|
-
const map = new Map(initial);
|
|
121
|
-
let hits = 0;
|
|
122
|
-
let misses = 0;
|
|
123
|
-
let dirty = false;
|
|
124
|
-
return {
|
|
125
|
-
read: (code, lang, options) => {
|
|
126
|
-
const found = map.get(twoslashEntryKey(code, lang, options?.compilerOptions));
|
|
127
|
-
if (found === void 0) {
|
|
128
|
-
misses += 1;
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
hits += 1;
|
|
132
|
-
return found;
|
|
133
|
-
},
|
|
134
|
-
write: (code, data, lang, options) => {
|
|
135
|
-
const value = {
|
|
136
|
-
nodes: data.nodes,
|
|
137
|
-
code: data.code,
|
|
138
|
-
...data.meta?.extension != null ? { meta: { extension: data.meta.extension } } : {}
|
|
139
|
-
};
|
|
140
|
-
map.set(twoslashEntryKey(code, lang, options?.compilerOptions), value);
|
|
141
|
-
dirty = true;
|
|
142
|
-
},
|
|
143
|
-
stats: () => ({
|
|
144
|
-
hits,
|
|
145
|
-
misses,
|
|
146
|
-
entries: map.size,
|
|
147
|
-
dirty
|
|
148
|
-
}),
|
|
149
|
-
entries: () => map
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
/** Serialize a generation for storage. Gzipped JSON — hover text compresses well. */
|
|
153
|
-
function encodeTwoslashCache(entries) {
|
|
154
|
-
return gzipSync(Buffer.from(JSON.stringify(Object.fromEntries(entries)), "utf-8"));
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Deserialize a stored generation.
|
|
158
|
-
*
|
|
159
|
-
* Returns an empty map for anything unreadable — a truncated blob, a format
|
|
160
|
-
* change, a corrupted file. A cache that cannot be read is a cache miss, never
|
|
161
|
-
* a build failure.
|
|
162
|
-
*/
|
|
163
|
-
function decodeTwoslashCache(blob) {
|
|
164
|
-
try {
|
|
165
|
-
const parsed = JSON.parse(gunzipSync(blob).toString("utf-8"));
|
|
166
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return /* @__PURE__ */ new Map();
|
|
167
|
-
return new Map(Object.entries(parsed));
|
|
168
|
-
} catch {
|
|
169
|
-
return /* @__PURE__ */ new Map();
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
//#endregion
|
|
174
|
-
export { decodeTwoslashCache, encodeTwoslashCache, makeTwoslashCache, twoslashBlobKey, twoslashEntryKey, twoslashEnvHash };
|
package/twoslash-patterns.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
//#region src/twoslash-patterns.ts
|
|
2
|
-
/**
|
|
3
|
-
* Twoslash directive detection patterns.
|
|
4
|
-
*
|
|
5
|
-
* These regexes mirror the upstream Twoslash source at:
|
|
6
|
-
* https://github.com/twoslashes/twoslash/blob/main/packages/twoslash/src/regexp.ts
|
|
7
|
-
*
|
|
8
|
-
* All patterns allow an optional space after `//` (e.g., both `// @noErrors`
|
|
9
|
-
* and `//@noErrors` are valid Twoslash syntax).
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Config directives: boolean flags and key-value pairs.
|
|
13
|
-
*
|
|
14
|
-
* Upstream: `reConfigBoolean` + `reConfigValue` + `reFilenamesMakers`
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```
|
|
18
|
-
* // @noErrors
|
|
19
|
-
* //@strict
|
|
20
|
-
* // @errors: 2304
|
|
21
|
-
* // @target: ES2020
|
|
22
|
-
* // @filename: example.ts
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
const RE_CONFIG = /^\/\/\s?@\w+/;
|
|
26
|
-
/**
|
|
27
|
-
* Annotation markers: query, completion, and highlight markers.
|
|
28
|
-
*
|
|
29
|
-
* Upstream: `reAnnonateMarkers` — `/^\s*\/\/\s*\^(\?|\||\^+)( .*)?$/gm`
|
|
30
|
-
*
|
|
31
|
-
* These are positioned under code lines with `^` characters for alignment.
|
|
32
|
-
* After `line.trim()`, leading whitespace is removed but internal spaces
|
|
33
|
-
* between `//` and `^` are preserved.
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```
|
|
37
|
-
* // ^? — query (show type info)
|
|
38
|
-
* // ^? — query with alignment spaces
|
|
39
|
-
* // ^| — completion (show autocomplete)
|
|
40
|
-
* // ^^^ — highlight range
|
|
41
|
-
* // ^^^^ description text
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
const RE_ANNOTATION = /^\/\/\s*\^[?|^]/;
|
|
45
|
-
/**
|
|
46
|
-
* Cut directives: control which code is visible in output.
|
|
47
|
-
*
|
|
48
|
-
* Upstream: `reCutBefore`, `reCutAfter`, `reCutStart`, `reCutEnd`
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```
|
|
52
|
-
* // ---cut---
|
|
53
|
-
* //---cut-before---
|
|
54
|
-
* // ---cut-after---
|
|
55
|
-
* // ---cut-start---
|
|
56
|
-
* // ---cut-end---
|
|
57
|
-
* ```
|
|
58
|
-
*/
|
|
59
|
-
const RE_CUT = /^\/\/\s?---cut/;
|
|
60
|
-
/**
|
|
61
|
-
* Test whether a trimmed line is any Twoslash directive.
|
|
62
|
-
*
|
|
63
|
-
* Covers all directive types: config flags, config values, filename markers,
|
|
64
|
-
* annotation markers (query/completion/highlight), and cut directives.
|
|
65
|
-
*
|
|
66
|
-
* @param trimmedLine - The line with leading/trailing whitespace removed
|
|
67
|
-
* @returns true if the line is a Twoslash directive
|
|
68
|
-
*/
|
|
69
|
-
function isTwoslashDirective(trimmedLine) {
|
|
70
|
-
return RE_CONFIG.test(trimmedLine) || RE_ANNOTATION.test(trimmedLine) || RE_CUT.test(trimmedLine);
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Classify a cut directive line.
|
|
74
|
-
*
|
|
75
|
-
* @param trimmedLine - The line with leading/trailing whitespace removed
|
|
76
|
-
* @returns The cut type, or null if not a cut directive
|
|
77
|
-
*/
|
|
78
|
-
function classifyCutDirective(trimmedLine) {
|
|
79
|
-
if (/^\/\/\s?---cut(-before)?---$/.test(trimmedLine)) return "cut-before";
|
|
80
|
-
if (/^\/\/\s?---cut-after---$/.test(trimmedLine)) return "cut-after";
|
|
81
|
-
if (/^\/\/\s?---cut-start---$/.test(trimmedLine)) return "cut-start";
|
|
82
|
-
if (/^\/\/\s?---cut-end---$/.test(trimmedLine)) return "cut-end";
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
//#endregion
|
|
87
|
-
export { classifyCutDirective, isTwoslashDirective };
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { ApiItemKind } from "@microsoft/api-extractor-model";
|
|
2
|
-
|
|
3
|
-
//#region src/type-reference-extractor.ts
|
|
4
|
-
/**
|
|
5
|
-
* Extracts type references from API Extractor models to generate import statements.
|
|
6
|
-
*
|
|
7
|
-
* This class analyzes API items and their excerpt tokens to identify external type
|
|
8
|
-
* references that need to be imported in the generated TypeScript declaration files.
|
|
9
|
-
*
|
|
10
|
-
* **How it works:**
|
|
11
|
-
* 1. Walks through all API items (classes, interfaces, functions, etc.)
|
|
12
|
-
* 2. Extracts type references from excerpt tokens
|
|
13
|
-
* 3. Filters out built-in types and internal references
|
|
14
|
-
* 4. Groups external references by package
|
|
15
|
-
* 5. Generates `import type` statements
|
|
16
|
-
*
|
|
17
|
-
* **Reference Types:**
|
|
18
|
-
* - **Built-in:** TypeScript types like `Promise`, `Record`, `NonNullable` (skipped)
|
|
19
|
-
* - **Internal:** References to types in the same package (skipped)
|
|
20
|
-
* - **External:** References to types from npm packages (imported)
|
|
21
|
-
*
|
|
22
|
-
* **Canonical Reference Format:**
|
|
23
|
-
* API Extractor uses canonical references like:
|
|
24
|
-
* - `"zod!ZodType:interface"` → External reference to `zod` package
|
|
25
|
-
* - `"mypackage!MyType:type"` → Internal reference (same package)
|
|
26
|
-
* - `"!Promise:interface"` → Built-in TypeScript type
|
|
27
|
-
* - `"!\"node:buffer\".__global.Buffer:interface"` → Node.js built-in (treated as built-in)
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* const extractor = new TypeReferenceExtractor(apiPackage, "my-package");
|
|
32
|
-
* const imports = extractor.extractImports();
|
|
33
|
-
*
|
|
34
|
-
* for (const stmt of imports) {
|
|
35
|
-
* console.log(`import type { ${[...stmt.symbols].join(", ")} } from "${stmt.packageName}";`);
|
|
36
|
-
* }
|
|
37
|
-
* // Output:
|
|
38
|
-
* // import type { ZodType } from "zod";
|
|
39
|
-
* // import type { Effect } from "@effect/schema";
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
var TypeReferenceExtractor = class {
|
|
43
|
-
apiPackage;
|
|
44
|
-
currentPackageName;
|
|
45
|
-
/**
|
|
46
|
-
* All type references found in the API package
|
|
47
|
-
*/
|
|
48
|
-
references = /* @__PURE__ */ new Map();
|
|
49
|
-
constructor(apiPackage, currentPackageName) {
|
|
50
|
-
this.apiPackage = apiPackage;
|
|
51
|
-
this.currentPackageName = currentPackageName;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Extract all type references from the API package and generate import statements.
|
|
55
|
-
* Returns an array of import statements grouped by package.
|
|
56
|
-
*/
|
|
57
|
-
extractImports() {
|
|
58
|
-
this.walkApiPackage();
|
|
59
|
-
return this.generateImportStatements();
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Extract type references for a specific entry point only.
|
|
63
|
-
* This enables per-entry-point import optimization for multi-entry packages.
|
|
64
|
-
*
|
|
65
|
-
* @param entryPoint - The specific entry point to extract imports for
|
|
66
|
-
* @returns Import statements containing only types used in this entry point
|
|
67
|
-
*/
|
|
68
|
-
extractImportsForEntryPoint(entryPoint) {
|
|
69
|
-
this.references.clear();
|
|
70
|
-
for (const member of entryPoint.members) this.walkApiItem(member);
|
|
71
|
-
return this.generateImportStatements();
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Extract type references for a single API item.
|
|
75
|
-
* This enables generating imports for individual signatures.
|
|
76
|
-
*
|
|
77
|
-
* @param apiItem - The specific API item to extract imports for
|
|
78
|
-
* @returns Import statements containing only types used in this item
|
|
79
|
-
*/
|
|
80
|
-
extractImportsForApiItem(apiItem) {
|
|
81
|
-
this.references.clear();
|
|
82
|
-
this.walkApiItem(apiItem);
|
|
83
|
-
return this.generateImportStatements();
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Generate import statements from collected references.
|
|
87
|
-
* Used by both extractImports() and extractImportsForEntryPoint().
|
|
88
|
-
*/
|
|
89
|
-
generateImportStatements() {
|
|
90
|
-
const packageMap = /* @__PURE__ */ new Map();
|
|
91
|
-
for (const ref of this.references.values()) {
|
|
92
|
-
if (ref.isBuiltIn || ref.isInternal) continue;
|
|
93
|
-
if (!packageMap.has(ref.packageName)) packageMap.set(ref.packageName, /* @__PURE__ */ new Set());
|
|
94
|
-
packageMap.get(ref.packageName)?.add(ref.symbolName);
|
|
95
|
-
}
|
|
96
|
-
const imports = [];
|
|
97
|
-
for (const [packageName, symbols] of packageMap.entries()) imports.push({
|
|
98
|
-
packageName,
|
|
99
|
-
symbols,
|
|
100
|
-
typeOnly: true
|
|
101
|
-
});
|
|
102
|
-
imports.sort((a, b) => a.packageName.localeCompare(b.packageName));
|
|
103
|
-
return imports;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Generate import statement strings from ImportStatement objects.
|
|
107
|
-
* Returns an array of formatted import statements.
|
|
108
|
-
*/
|
|
109
|
-
static formatImports(imports) {
|
|
110
|
-
const statements = [];
|
|
111
|
-
for (const stmt of imports) {
|
|
112
|
-
const sortedSymbols = Array.from(stmt.symbols).sort();
|
|
113
|
-
const statement = `${stmt.typeOnly ? "import type" : "import"} { ${sortedSymbols.join(", ")} } from "${stmt.packageName}";`;
|
|
114
|
-
statements.push(statement);
|
|
115
|
-
}
|
|
116
|
-
return statements;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Walk through the entire API package and extract all type references
|
|
120
|
-
*/
|
|
121
|
-
walkApiPackage() {
|
|
122
|
-
for (const entryPoint of this.apiPackage.entryPoints) for (const member of entryPoint.members) this.walkApiItem(member);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Recursively walk through an API item and its children to extract type references
|
|
126
|
-
*/
|
|
127
|
-
walkApiItem(apiItem) {
|
|
128
|
-
this.extractFromExcerpt(apiItem);
|
|
129
|
-
if ("members" in apiItem) {
|
|
130
|
-
const members = apiItem.members;
|
|
131
|
-
if (Array.isArray(members)) for (const member of members) this.walkApiItem(member);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Extract type references from an API item using its excerpt
|
|
136
|
-
*/
|
|
137
|
-
extractFromExcerpt(apiItem) {
|
|
138
|
-
const excerpt = this.getExcerpt(apiItem);
|
|
139
|
-
if (!excerpt) return;
|
|
140
|
-
this.extractFromExcerptTokens(excerpt);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Get the appropriate excerpt from an API item based on its kind
|
|
144
|
-
*/
|
|
145
|
-
getExcerpt(apiItem) {
|
|
146
|
-
const item = apiItem;
|
|
147
|
-
if (item.excerpt) return item.excerpt;
|
|
148
|
-
if (apiItem.kind === ApiItemKind.TypeAlias && item.typeExcerpt) return item.typeExcerpt;
|
|
149
|
-
if ((apiItem.kind === ApiItemKind.Property || apiItem.kind === ApiItemKind.PropertySignature) && item.propertyTypeExcerpt) return item.propertyTypeExcerpt;
|
|
150
|
-
if (item.returnTypeExcerpt) return item.returnTypeExcerpt;
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Extract type references from excerpt tokens
|
|
155
|
-
*/
|
|
156
|
-
extractFromExcerptTokens(excerpt) {
|
|
157
|
-
if (!excerpt.spannedTokens || excerpt.spannedTokens.length === 0) return;
|
|
158
|
-
for (const token of excerpt.spannedTokens) {
|
|
159
|
-
if (token.kind !== "Reference") continue;
|
|
160
|
-
const canonicalRef = token.canonicalReference?.toString();
|
|
161
|
-
if (!canonicalRef || typeof canonicalRef !== "string") continue;
|
|
162
|
-
const ref = this.parseCanonicalReference(canonicalRef, token.text);
|
|
163
|
-
if (ref) this.references.set(ref.canonicalReference, ref);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Parse a canonical reference string to extract type reference information.
|
|
168
|
-
*
|
|
169
|
-
* Canonical reference format: "packageName!symbolName:kind"
|
|
170
|
-
* Examples:
|
|
171
|
-
* - "zod!ZodType:interface" → External reference
|
|
172
|
-
* - "mypackage!MyType:type" → Internal reference
|
|
173
|
-
* - "!Promise:interface" → Built-in type
|
|
174
|
-
* - "!\"node:buffer\".__global.Buffer:interface" → Node.js built-in
|
|
175
|
-
*/
|
|
176
|
-
parseCanonicalReference(canonicalRef, symbolText) {
|
|
177
|
-
const exclamationIndex = canonicalRef.indexOf("!");
|
|
178
|
-
if (exclamationIndex === -1) return null;
|
|
179
|
-
const packagePart = canonicalRef.substring(0, exclamationIndex);
|
|
180
|
-
const rest = canonicalRef.substring(exclamationIndex + 1);
|
|
181
|
-
const colonIndex = rest.indexOf(":");
|
|
182
|
-
const symbolFromCanonical = colonIndex !== -1 ? rest.substring(0, colonIndex) : rest;
|
|
183
|
-
const isBuiltIn = packagePart === "" || packagePart.startsWith("\"");
|
|
184
|
-
const isInternal = packagePart === this.currentPackageName;
|
|
185
|
-
let symbolName;
|
|
186
|
-
if (symbolText.includes(".")) symbolName = symbolText.split(".")[0].trim();
|
|
187
|
-
else symbolName = symbolFromCanonical.trim();
|
|
188
|
-
return {
|
|
189
|
-
symbolName,
|
|
190
|
-
packageName: packagePart,
|
|
191
|
-
canonicalReference: canonicalRef,
|
|
192
|
-
isBuiltIn,
|
|
193
|
-
isInternal
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
//#endregion
|
|
199
|
-
export { TypeReferenceExtractor };
|
package/typescript-config.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { TsConfigParseError, parseTsConfig } from "./tsconfig-parser.js";
|
|
2
|
-
|
|
3
|
-
//#region src/typescript-config.ts
|
|
4
|
-
/**
|
|
5
|
-
* Default TypeScript compiler options for Twoslash and type resolution.
|
|
6
|
-
*
|
|
7
|
-
* These defaults are optimized for documentation:
|
|
8
|
-
* - Modern ES targets (ESNext)
|
|
9
|
-
* - Bundler module resolution for broad compatibility
|
|
10
|
-
* - Lenient settings (non-strict) since docs often show simplified examples
|
|
11
|
-
* - Skip lib checks for faster processing
|
|
12
|
-
*
|
|
13
|
-
* @remarks
|
|
14
|
-
* Numeric values correspond to TypeScript enums:
|
|
15
|
-
* - target: 99 = ESNext
|
|
16
|
-
* - module: 99 = ESNext
|
|
17
|
-
* - moduleResolution: 100 = Bundler
|
|
18
|
-
*/
|
|
19
|
-
const DEFAULT_COMPILER_OPTIONS = {
|
|
20
|
-
target: 99,
|
|
21
|
-
module: 99,
|
|
22
|
-
moduleResolution: 100,
|
|
23
|
-
lib: ["ESNext", "DOM"],
|
|
24
|
-
strict: false,
|
|
25
|
-
skipLibCheck: true,
|
|
26
|
-
esModuleInterop: true,
|
|
27
|
-
allowSyntheticDefaultImports: true
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Merge two TypeResolutionCompilerOptions objects.
|
|
31
|
-
* Properties from `override` take precedence over `base`.
|
|
32
|
-
*
|
|
33
|
-
* @param base - Base compiler options
|
|
34
|
-
* @param override - Options to merge on top (takes precedence)
|
|
35
|
-
* @returns Merged options
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```ts
|
|
39
|
-
* const base = { target: 99, lib: ["ESNext"] };
|
|
40
|
-
* const override = { lib: ["ESNext", "DOM"], strict: true };
|
|
41
|
-
* const merged = mergeCompilerOptions(base, override);
|
|
42
|
-
* // Result: { target: 99, lib: ["ESNext", "DOM"], strict: true }
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
function mergeCompilerOptions(base, override) {
|
|
46
|
-
if (!override) return { ...base };
|
|
47
|
-
const merged = { ...base };
|
|
48
|
-
if (override.target !== void 0) merged.target = override.target;
|
|
49
|
-
if (override.module !== void 0) merged.module = override.module;
|
|
50
|
-
if (override.moduleResolution !== void 0) merged.moduleResolution = override.moduleResolution;
|
|
51
|
-
if (override.lib !== void 0) merged.lib = override.lib;
|
|
52
|
-
if (override.strict !== void 0) merged.strict = override.strict;
|
|
53
|
-
if (override.skipLibCheck !== void 0) merged.skipLibCheck = override.skipLibCheck;
|
|
54
|
-
if (override.esModuleInterop !== void 0) merged.esModuleInterop = override.esModuleInterop;
|
|
55
|
-
if (override.allowSyntheticDefaultImports !== void 0) merged.allowSyntheticDefaultImports = override.allowSyntheticDefaultImports;
|
|
56
|
-
if (override.jsx !== void 0) merged.jsx = override.jsx;
|
|
57
|
-
if (override.types !== void 0) merged.types = override.types;
|
|
58
|
-
return merged;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Resolve a single TypeScriptConfig to compiler options (async version).
|
|
62
|
-
* Handles both path-based and function-based tsconfig.
|
|
63
|
-
*
|
|
64
|
-
* Follows the priority cascade:
|
|
65
|
-
* 1. Load tsconfig (from path or function)
|
|
66
|
-
* 2. Merge compilerOptions on top
|
|
67
|
-
*
|
|
68
|
-
* @param config - TypeScript config with optional tsconfig path/function and/or compilerOptions
|
|
69
|
-
* @param projectRoot - Project root for resolving relative tsconfig paths
|
|
70
|
-
* @returns Promise resolving to compiler options (not merged with defaults)
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* ```ts
|
|
74
|
-
* // Path-based tsconfig
|
|
75
|
-
* await resolveTypeScriptConfigSingleAsync({ tsconfig: "tsconfig.json" }, "/project");
|
|
76
|
-
*
|
|
77
|
-
* // Function-based tsconfig
|
|
78
|
-
* await resolveTypeScriptConfigSingleAsync({
|
|
79
|
-
* tsconfig: async () => ({ target: 99, lib: ["ESNext"] })
|
|
80
|
-
* }, "/project");
|
|
81
|
-
*
|
|
82
|
-
* // Both (compilerOptions override tsconfig)
|
|
83
|
-
* await resolveTypeScriptConfigSingleAsync({
|
|
84
|
-
* tsconfig: async () => ({ target: 99 }),
|
|
85
|
-
* compilerOptions: { strict: false }
|
|
86
|
-
* }, "/project");
|
|
87
|
-
* ```
|
|
88
|
-
*/
|
|
89
|
-
async function resolveTypeScriptConfigSingleAsync(config, projectRoot) {
|
|
90
|
-
if (!config) return {};
|
|
91
|
-
let options = {};
|
|
92
|
-
if (config.tsconfig) {
|
|
93
|
-
if (typeof config.tsconfig === "function") options = await config.tsconfig();
|
|
94
|
-
else {
|
|
95
|
-
const tsconfigPath = String(config.tsconfig);
|
|
96
|
-
try {
|
|
97
|
-
options = parseTsConfig(tsconfigPath, projectRoot);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
if (error instanceof TsConfigParseError) throw error;
|
|
100
|
-
throw new TsConfigParseError(tsconfigPath, error instanceof Error ? error.message : String(error), error);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (config.compilerOptions) options = mergeCompilerOptions(options, config.compilerOptions);
|
|
105
|
-
return options;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Resolve TypeScript compiler options from a cascade of configurations (async).
|
|
109
|
-
*
|
|
110
|
-
* Resolution order (later levels override earlier):
|
|
111
|
-
* 1. DEFAULT_COMPILER_OPTIONS (sensible defaults)
|
|
112
|
-
* 2. Global plugin config
|
|
113
|
-
* 3. API-level config
|
|
114
|
-
* 4. Version-level config
|
|
115
|
-
* 5. Per-package override (for external packages)
|
|
116
|
-
*
|
|
117
|
-
* At each level, if a TypeScriptConfig has both `tsconfig` and `compilerOptions`,
|
|
118
|
-
* the tsconfig is loaded first, then compilerOptions are merged on top.
|
|
119
|
-
*
|
|
120
|
-
* @param projectRoot - Project root directory for resolving relative paths
|
|
121
|
-
* @param global - Global plugin TypeScript configuration
|
|
122
|
-
* @param api - API-level TypeScript configuration
|
|
123
|
-
* @param version - Version-level TypeScript configuration
|
|
124
|
-
* @param packageOverride - Per-package TypeScript configuration override
|
|
125
|
-
* @returns Promise resolving to fully resolved compiler options
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* ```ts
|
|
129
|
-
* // Simple global config
|
|
130
|
-
* const options = await resolveTypeScriptConfig("/project", {
|
|
131
|
-
* tsconfig: "tsconfig.json"
|
|
132
|
-
* });
|
|
133
|
-
*
|
|
134
|
-
* // With async tsconfig loader
|
|
135
|
-
* const options = await resolveTypeScriptConfig("/project", {
|
|
136
|
-
* tsconfig: async () => ({ target: 99, lib: ["ESNext"] })
|
|
137
|
-
* });
|
|
138
|
-
*
|
|
139
|
-
* // With API override
|
|
140
|
-
* const options = await resolveTypeScriptConfig(
|
|
141
|
-
* "/project",
|
|
142
|
-
* { tsconfig: "tsconfig.json" },
|
|
143
|
-
* { compilerOptions: { strict: false } }
|
|
144
|
-
* );
|
|
145
|
-
*
|
|
146
|
-
* // Full cascade
|
|
147
|
-
* const options = await resolveTypeScriptConfig(
|
|
148
|
-
* "/project",
|
|
149
|
-
* { tsconfig: "tsconfig.json" }, // global
|
|
150
|
-
* { compilerOptions: { strict: false } }, // API
|
|
151
|
-
* { compilerOptions: { target: 9 } }, // version
|
|
152
|
-
* { compilerOptions: { module: 1 } } // package override
|
|
153
|
-
* );
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
|
-
async function resolveTypeScriptConfig(projectRoot, global, api, version, packageOverride) {
|
|
157
|
-
let options = { ...DEFAULT_COMPILER_OPTIONS };
|
|
158
|
-
const globalOptions = await resolveTypeScriptConfigSingleAsync(global, projectRoot);
|
|
159
|
-
options = mergeCompilerOptions(options, globalOptions);
|
|
160
|
-
const apiOptions = await resolveTypeScriptConfigSingleAsync(api, projectRoot);
|
|
161
|
-
options = mergeCompilerOptions(options, apiOptions);
|
|
162
|
-
const versionOptions = await resolveTypeScriptConfigSingleAsync(version, projectRoot);
|
|
163
|
-
options = mergeCompilerOptions(options, versionOptions);
|
|
164
|
-
const packageOptions = await resolveTypeScriptConfigSingleAsync(packageOverride, projectRoot);
|
|
165
|
-
options = mergeCompilerOptions(options, packageOptions);
|
|
166
|
-
return options;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
//#endregion
|
|
170
|
-
export { DEFAULT_COMPILER_OPTIONS, mergeCompilerOptions, resolveTypeScriptConfig, resolveTypeScriptConfigSingleAsync };
|