@tsdoctor/vfs 0.1.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/Vfs.js ADDED
@@ -0,0 +1,47 @@
1
+ //#region src/Vfs.ts
2
+ /**
3
+ * Merge VFS maps left to right into a new map; later entries win on path
4
+ * collisions.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { mergeVfs } from "@tsdoctor/vfs";
9
+ *
10
+ * const combined = mergeVfs(vfsA, vfsB);
11
+ * ```
12
+ *
13
+ * @public
14
+ */
15
+ const mergeVfs = (...maps) => {
16
+ const out = /* @__PURE__ */ new Map();
17
+ for (const map of maps) for (const [path, content] of map) out.set(path, content);
18
+ return out;
19
+ };
20
+ /**
21
+ * Prefix every path in `entries` with `node_modules/<name>/`, normalizing
22
+ * away leading slashes.
23
+ *
24
+ * @public
25
+ */
26
+ const prefixVfs = (name, entries) => {
27
+ const out = /* @__PURE__ */ new Map();
28
+ for (const [path, content] of entries) out.set(`node_modules/${name}/${path.replace(/^\/+/, "")}`, content);
29
+ return out;
30
+ };
31
+ /**
32
+ * Whether a path names a TypeScript declaration file.
33
+ *
34
+ * @remarks
35
+ * The single spelling of this predicate. `TsEnvironment` uses it to pick a
36
+ * VFS map's root files, and `@tsdoctor/registry`'s module resolution uses it
37
+ * to decide whether a resolved path is a declaration. Two spellings of "is
38
+ * this a `.d.ts`" would be free to drift, and the drift would be silent — a
39
+ * root file quietly missing from a TypeScript environment degrades hovers
40
+ * without producing an error.
41
+ *
42
+ * @public
43
+ */
44
+ const isTypeDefinition = (filePath) => filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts");
45
+
46
+ //#endregion
47
+ export { isTypeDefinition, mergeVfs, prefixVfs };
@@ -0,0 +1,126 @@
1
+ import { Effect, FileSystem, Schema } from "effect";
2
+
3
+ //#region src/VirtualPackage.ts
4
+ /**
5
+ * A synthetic npm package built from locally supplied TypeScript declaration
6
+ * content, for inclusion in a {@link Vfs} without fetching from the CDN.
7
+ *
8
+ * @remarks
9
+ * Useful when you have locally generated `.d.ts` files — API Extractor
10
+ * output, hand-written ambient declarations — and want them in the same VFS
11
+ * `TypeRegistry` builds from remote packages. Instances are transient: they
12
+ * are never persisted to the disk cache.
13
+ *
14
+ * The class is deliberately subclass-friendly (the rspress consumer extends
15
+ * it): construct via `VirtualPackage.make(...)` or the statics, and extend
16
+ * with `class Mine extends VirtualPackage { ... }`.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { VirtualPackage } from "@tsdoctor/vfs";
21
+ *
22
+ * const pkg = VirtualPackage.create("@my-org/api-types", "1.0.0", "export interface User { id: string }");
23
+ * const vfs = pkg.toVfs();
24
+ * // node_modules/@my-org/api-types/package.json, node_modules/@my-org/api-types/index.d.ts
25
+ * ```
26
+ *
27
+ * @public
28
+ */
29
+ var VirtualPackage = class VirtualPackage extends Schema.Class("VirtualPackage")({
30
+ /** The package name (e.g. `"@my-org/api-types"`). */
31
+ name: Schema.String,
32
+ /** The package version. */
33
+ version: Schema.String,
34
+ /** Entry file names (e.g. `"index.d.ts"`) mapped to declaration source. */
35
+ entries: Schema.ReadonlyMap(Schema.String, Schema.String)
36
+ }) {
37
+ /**
38
+ * Single-entry factory: a virtual package whose sole entry point is
39
+ * `index.d.ts`.
40
+ */
41
+ static create(name, version, declarations) {
42
+ return VirtualPackage.make({
43
+ name,
44
+ version,
45
+ entries: /* @__PURE__ */ new Map([["index.d.ts", declarations]])
46
+ });
47
+ }
48
+ /**
49
+ * Multi-entry factory: one `.d.ts` per entry point, exposed through a
50
+ * synthetic `exports` map.
51
+ *
52
+ * @remarks
53
+ * An empty entries map is developer wiring, not input — it would produce a
54
+ * package whose `types` points at a file that does not exist — so it
55
+ * throws at construction (defect posture), as does an entry set whose
56
+ * names collide after extension normalization (see
57
+ * {@link VirtualPackage.toVfs}).
58
+ */
59
+ static createMultiEntry(name, version, entries) {
60
+ if (entries.size === 0) throw new Error(`VirtualPackage.createMultiEntry: "${name}" needs at least one entry file`);
61
+ return VirtualPackage.make({
62
+ name,
63
+ version,
64
+ entries
65
+ });
66
+ }
67
+ /**
68
+ * Load a single `.d.ts` file from disk as a virtual package with one
69
+ * `index.d.ts` entry.
70
+ *
71
+ * @remarks
72
+ * Reads through the platform-agnostic `FileSystem` service; the
73
+ * `PlatformError` surfaces typed.
74
+ */
75
+ static fromFile(name, version, filePath) {
76
+ return Effect.gen(function* () {
77
+ const content = yield* (yield* FileSystem.FileSystem).readFileString(filePath);
78
+ return VirtualPackage.create(name, version, content);
79
+ }).pipe(Effect.withSpan("VirtualPackage.fromFile"));
80
+ }
81
+ /**
82
+ * The package's {@link Vfs}: a synthetic `package.json` plus every entry
83
+ * file, each path prefixed `node_modules/<name>/`.
84
+ *
85
+ * @remarks
86
+ * The `package.json` uses `types` for a single entry and an `exports` map
87
+ * for multiple entries, so TypeScript module resolution works against the
88
+ * generated VFS.
89
+ */
90
+ toVfs() {
91
+ const vfs = /* @__PURE__ */ new Map();
92
+ const prefix = `node_modules/${this.name}`;
93
+ vfs.set(`${prefix}/package.json`, this.toPackageJson());
94
+ for (const [fileName, content] of this.entries) {
95
+ if (fileName === "package.json") throw new Error(`VirtualPackage: "${this.name}" cannot define package.json as an entry`);
96
+ vfs.set(`${prefix}/${fileName}`, content);
97
+ }
98
+ return vfs;
99
+ }
100
+ toPackageJson() {
101
+ if (this.entries.size === 0) throw new Error(`VirtualPackage: "${this.name}" has no entry files — nothing to point types at`);
102
+ const manifest = {
103
+ name: this.name,
104
+ version: this.version
105
+ };
106
+ if (this.entries.size === 1) {
107
+ const [only] = this.entries.keys();
108
+ manifest.types = only ?? "index.d.ts";
109
+ } else {
110
+ manifest.exports = {};
111
+ const sources = /* @__PURE__ */ new Map();
112
+ for (const fileName of this.entries.keys()) {
113
+ const baseName = fileName.replace(/\.d\.(m|c)?ts$/, "");
114
+ const key = baseName === "index" ? "." : `./${baseName}`;
115
+ const previous = sources.get(key);
116
+ if (previous !== void 0) throw new Error(`VirtualPackage: "${this.name}" entries "${previous}" and "${fileName}" both normalize to the export key "${key}"`);
117
+ sources.set(key, fileName);
118
+ manifest.exports[key] = { types: `./${fileName}` };
119
+ }
120
+ }
121
+ return JSON.stringify(manifest, null, 2);
122
+ }
123
+ };
124
+
125
+ //#endregion
126
+ export { VirtualPackage };
package/index.d.ts ADDED
@@ -0,0 +1,505 @@
1
+ import { CompilerOptions, ProgrammaticCompilerOptions } from "@effected/tsconfig-json";
2
+ import { Effect, FileSystem, PlatformError, Result, Schema } from "effect";
3
+ import { VirtualTypeScriptEnvironment } from "@typescript/vfs";
4
+ import { PathLike } from "node:fs";
5
+ //#region src/TypeResolutionOptions.d.ts
6
+ /**
7
+ * The compiler options that may influence how a documentation example
8
+ * type-checks.
9
+ *
10
+ * @remarks
11
+ * Deliberately a **whitelist**, picked from `@effected/tsconfig-json`'s
12
+ * `CompilerOptions` rather than restated. Everything here reaches the
13
+ * TypeScript environment, and passing through options the tool does not
14
+ * understand would let a consumer's unrelated build setting silently change
15
+ * how their examples type-check.
16
+ *
17
+ * Picking from the kit's own field schemas is what keeps this a policy
18
+ * statement rather than a second definition of tsconfig: the accepted values,
19
+ * their spellings and their case-insensitivity are the kit's to own, and this
20
+ * module owns only the choice of WHICH options are in scope.
21
+ *
22
+ * @public
23
+ */
24
+ declare const TypeResolutionCompilerOptions: Schema.Struct<{
25
+ readonly target: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["es5", "es6", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020", "es2021", "es2022", "es2023", "es2024", "es2025", "esnext"]>, Schema.String, never, never>>;
26
+ readonly module: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["none", "commonjs", "amd", "umd", "system", "es6", "es2015", "es2020", "es2022", "esnext", "node16", "node18", "node20", "nodenext", "preserve"]>, Schema.String, never, never>>;
27
+ readonly moduleResolution: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["node10", "node", "classic", "node16", "nodenext", "bundler"]>, Schema.String, never, never>>;
28
+ readonly jsx: Schema.optionalKey<Schema.decodeTo<Schema.Literals<readonly ["preserve", "react-native", "react-jsx", "react-jsxdev", "react"]>, Schema.String, never, never>>;
29
+ readonly lib: Schema.optionalKey<Schema.$Array<Schema.decodeTo<Schema.Literals<readonly ["es5", "es6", "es7", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020", "es2021", "es2022", "es2023", "es2024", "es2025", "esnext", "dom", "dom.iterable", "dom.asynciterable", "webworker", "webworker.importscripts", "webworker.iterable", "webworker.asynciterable", "scripthost", "es2015.core", "es2015.collection", "es2015.generator", "es2015.iterable", "es2015.promise", "es2015.proxy", "es2015.reflect", "es2015.symbol", "es2015.symbol.wellknown", "es2016.array.include", "es2016.intl", "es2017.arraybuffer", "es2017.date", "es2017.object", "es2017.sharedmemory", "es2017.string", "es2017.intl", "es2017.typedarrays", "es2018.asyncgenerator", "es2018.asynciterable", "es2018.intl", "es2018.promise", "es2018.regexp", "es2019.array", "es2019.object", "es2019.string", "es2019.symbol", "es2019.intl", "es2020.bigint", "es2020.date", "es2020.promise", "es2020.sharedmemory", "es2020.string", "es2020.symbol.wellknown", "es2020.intl", "es2020.number", "es2021.promise", "es2021.string", "es2021.weakref", "es2021.intl", "es2022.array", "es2022.error", "es2022.intl", "es2022.object", "es2022.string", "es2022.regexp", "es2023.array", "es2023.collection", "es2023.intl", "es2024.arraybuffer", "es2024.collection", "es2024.object", "es2024.promise", "es2024.regexp", "es2024.sharedmemory", "es2024.string", "es2025.collection", "es2025.float16", "es2025.intl", "es2025.iterator", "es2025.promise", "es2025.regexp", "esnext.asynciterable", "esnext.symbol", "esnext.bigint", "esnext.weakref", "esnext.object", "esnext.regexp", "esnext.string", "esnext.float16", "esnext.iterator", "esnext.promise", "esnext.array", "esnext.collection", "esnext.date", "esnext.decorators", "esnext.disposable", "esnext.error", "esnext.intl", "esnext.sharedmemory", "esnext.temporal", "esnext.typedarrays", "decorators", "decorators.legacy"]>, Schema.String, never, never>>>;
30
+ readonly types: Schema.optionalKey<Schema.$Array<Schema.String>>;
31
+ readonly typeRoots: Schema.optionalKey<Schema.$Array<Schema.String>>;
32
+ readonly strict: Schema.optionalKey<Schema.Boolean>;
33
+ readonly skipLibCheck: Schema.optionalKey<Schema.Boolean>;
34
+ readonly esModuleInterop: Schema.optionalKey<Schema.Boolean>;
35
+ readonly allowSyntheticDefaultImports: Schema.optionalKey<Schema.Boolean>;
36
+ }>;
37
+ /**
38
+ * The decoded whitelist: canonical tsconfig spellings, every field optional.
39
+ *
40
+ * @public
41
+ */
42
+ type TypeResolutionCompilerOptions = typeof TypeResolutionCompilerOptions.Type;
43
+ /**
44
+ * Decode compiler options written in either spelling into the whitelist.
45
+ *
46
+ * @remarks
47
+ * Accepts the tsconfig spelling a user writes (`target: "es2025"`, and
48
+ * case-insensitively `lib: ["ESNext", "DOM"]`) and the programmatic spelling a
49
+ * caller holding `ts.CompilerOptions` has (`target: ts.ScriptTarget.ES2025`),
50
+ * because a consumer configuring a documentation build in TypeScript
51
+ * reasonably produces either.
52
+ *
53
+ * **Fails rather than guesses.** A value with no entry in the enum tables — a
54
+ * numeric target from a future TypeScript, a misspelled module kind — is
55
+ * rejected on the error channel instead of being passed through. Degrading to
56
+ * a default here would type-check every example against a configuration the
57
+ * user did not ask for, and produce confidently wrong output with no error:
58
+ * the failure mode this seam exists to prevent.
59
+ *
60
+ * @public
61
+ */
62
+ declare const decodeCompilerOptions: (input: unknown) => Result.Result<TypeResolutionCompilerOptions, Schema.SchemaError>;
63
+ /**
64
+ * Convert whitelisted options to the numeric-enum form the TypeScript compiler
65
+ * takes.
66
+ *
67
+ * @remarks
68
+ * The ONE conversion site between the tsconfig spelling and the programmatic
69
+ * one. Two consequences follow from it being single:
70
+ *
71
+ * - Any environment fingerprint MUST be computed on the ENCODED value.
72
+ * Otherwise `{lib:["ESNext"]}` and `{lib:["lib.esnext.d.ts"]}` build two
73
+ * identical TypeScript environments under different keys.
74
+ * - There is no cast here. The whitelist is a subset of the kit's own
75
+ * `CompilerOptions`, so it is assignable to the encoder by construction —
76
+ * which is precisely what a hand-rolled options type could not be.
77
+ *
78
+ * @public
79
+ */
80
+ declare const toProgrammaticCompilerOptions: (options: TypeResolutionCompilerOptions) => ProgrammaticCompilerOptions;
81
+ //#endregion
82
+ //#region src/TsconfigParser.d.ts
83
+ /**
84
+ * Error thrown when tsconfig.json parsing fails.
85
+ *
86
+ * @remarks
87
+ * Retained as the plugin's own type rather than surfacing the kit's
88
+ * `TsconfigParseError`/`TsconfigExtendsError` directly: the adapter's
89
+ * `typescript-config.ts` branches on `instanceof TsConfigParseError` to decide
90
+ * whether a failure is already reported, and both kit errors mean the same
91
+ * thing to that caller. It now also carries a decode failure from
92
+ * {@link decodeCompilerOptions}, which is the same thing again: a tsconfig
93
+ * this tool cannot act on.
94
+ *
95
+ * @public
96
+ */
97
+ declare class TsConfigParseError extends Error {
98
+ readonly configPath: string;
99
+ readonly cause?: unknown | undefined;
100
+ constructor(configPath: string, message: string, cause?: unknown | undefined);
101
+ }
102
+ /**
103
+ * Parse a `tsconfig.json` and extract the compiler options used for type
104
+ * resolution.
105
+ *
106
+ * @param configPath - Path to tsconfig.json (relative or absolute)
107
+ * @param projectRoot - Project root directory for resolving relative paths
108
+ * @returns The declared compiler options, in the tsconfig spelling
109
+ * @throws TsConfigParseError if the config cannot be read or parsed
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const options = parseTsConfig("tsconfig.json", "/path/to/project");
114
+ * // Returns: { target: "es2025", module: "nodenext", lib: ["esnext"], ... }
115
+ * ```
116
+ *
117
+ * @public
118
+ */
119
+ declare function parseTsConfig(configPath: string, projectRoot: string): TypeResolutionCompilerOptions;
120
+ //#endregion
121
+ //#region src/Vfs.d.ts
122
+ /**
123
+ * The package's currency type: a virtual file system mapping
124
+ * `node_modules/`-prefixed paths to file contents.
125
+ */
126
+ /**
127
+ * A virtual file system: file paths (prefixed `node_modules/<package>/`)
128
+ * mapped to their string contents.
129
+ *
130
+ * @remarks
131
+ * This is the value every loading operation produces and every TypeScript
132
+ * integration consumes. Maps from multiple packages merge with {@link mergeVfs};
133
+ * `@typescript/vfs` consumes the merged map directly (see `TsEnvironment`).
134
+ *
135
+ * @public
136
+ */
137
+ type Vfs = Map<string, string>;
138
+ /**
139
+ * Merge VFS maps left to right into a new map; later entries win on path
140
+ * collisions.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { mergeVfs } from "@tsdoctor/vfs";
145
+ *
146
+ * const combined = mergeVfs(vfsA, vfsB);
147
+ * ```
148
+ *
149
+ * @public
150
+ */
151
+ declare const mergeVfs: (...maps: ReadonlyArray<ReadonlyMap<string, string>>) => Vfs;
152
+ /**
153
+ * Prefix every path in `entries` with `node_modules/<name>/`, normalizing
154
+ * away leading slashes.
155
+ *
156
+ * @public
157
+ */
158
+ declare const prefixVfs: (name: string, entries: ReadonlyMap<string, string>) => Vfs;
159
+ /**
160
+ * Whether a path names a TypeScript declaration file.
161
+ *
162
+ * @remarks
163
+ * The single spelling of this predicate. `TsEnvironment` uses it to pick a
164
+ * VFS map's root files, and `@tsdoctor/registry`'s module resolution uses it
165
+ * to decide whether a resolved path is a declaration. Two spellings of "is
166
+ * this a `.d.ts`" would be free to drift, and the drift would be silent — a
167
+ * root file quietly missing from a TypeScript environment degrades hovers
168
+ * without producing an error.
169
+ *
170
+ * @public
171
+ */
172
+ declare const isTypeDefinition: (filePath: string) => boolean;
173
+ //#endregion
174
+ //#region src/TsEnvironment.d.ts
175
+ declare const TsEnvironmentError_base: Schema.Class<TsEnvironmentError, Schema.TaggedStruct<"TsEnvironmentError", {
176
+ /** The underlying failure, preserved structurally. */
177
+ readonly cause: Schema.Defect;
178
+ }>, import("effect/Cause").YieldableError>;
179
+ /**
180
+ * Raised when building a virtual TypeScript environment fails — including
181
+ * when the optional `typescript` / `@typescript/vfs` /
182
+ * `@effected/tsconfig-json` peers are not installed.
183
+ *
184
+ * @public
185
+ */
186
+ declare class TsEnvironmentError extends TsEnvironmentError_base {
187
+ get message(): string;
188
+ }
189
+ /**
190
+ * Options for {@link TsEnvironment.make}.
191
+ *
192
+ * @public
193
+ */
194
+ interface TsEnvironmentOptions {
195
+ /** The virtual file system to typecheck against. */
196
+ readonly vfs: Vfs;
197
+ /**
198
+ * Compiler options for the language service, in tsconfig JSON form
199
+ * (`{ target: "es2022" }`, not `ts.ScriptTarget.ES2022`). Enum-valued
200
+ * fields are converted to the compiler's numeric enums internally, so this
201
+ * type has no dependency on the `typescript` package.
202
+ */
203
+ readonly compilerOptions: CompilerOptions.Type;
204
+ /**
205
+ * The directory VFS paths are rooted under and the filesystem fallback
206
+ * root. Defaults to `process.cwd()` (which v3 hardcoded).
207
+ */
208
+ readonly projectRoot?: string;
209
+ }
210
+ /**
211
+ * The `@typescript/vfs` seam: builds a `VirtualTypeScriptEnvironment` over a
212
+ * {@link Vfs} plus the TypeScript default lib files.
213
+ *
214
+ * @remarks
215
+ * The ONLY module touching the optional `typescript` / `@typescript/vfs` /
216
+ * `@effected/tsconfig-json` peers, and it loads all three lazily inside
217
+ * {@link TsEnvironment.make} — a consumer that never calls it never loads
218
+ * the compiler, and a missing peer fails typed as
219
+ * {@link TsEnvironmentError} instead of crashing at import time. Keep every
220
+ * one of them behind that dynamic `import()`: a static value import here is
221
+ * reachable from `index.ts`, so it would turn an omitted optional peer into
222
+ * an `ERR_MODULE_NOT_FOUND` on the entry graph for consumers who never
223
+ * touch this module. Only the type-only `CompilerOptions` import is safe
224
+ * statically, because it erases. The underlying `createDefaultMapFromNodeModules` /
225
+ * `createFSBackedSystem` read the real filesystem through TypeScript's own
226
+ * `sys`, outside the Effect `FileSystem` service — accepted and documented;
227
+ * this module is why the package is integrated tier on its own surface.
228
+ *
229
+ * No cache map (v3's `createTypeScriptCache` returned a one-entry `Map`
230
+ * keyed by `JSON.stringify(compilerOptions)`): a consumer that wants keyed
231
+ * reuse holds its own map.
232
+ *
233
+ * `VirtualTypeScriptEnvironment` is deliberately not re-exported — import
234
+ * the type from `@typescript/vfs`, which consumers of this module already
235
+ * declare.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * import { TsEnvironment } from "@tsdoctor/vfs";
240
+ *
241
+ * const environment = TsEnvironment.make({
242
+ * vfs,
243
+ * compilerOptions: { strict: true, target: "es2022" },
244
+ * });
245
+ * ```
246
+ *
247
+ * @public
248
+ */
249
+ declare class TsEnvironment {
250
+ private constructor();
251
+ /** Build a `VirtualTypeScriptEnvironment` over a {@link Vfs}. */
252
+ static make(options: TsEnvironmentOptions): Effect.Effect<VirtualTypeScriptEnvironment, TsEnvironmentError>;
253
+ }
254
+ //#endregion
255
+ //#region src/TypeScriptConfig.d.ts
256
+ /**
257
+ * How a caller points at TypeScript configuration: a `tsconfig.json`, inline
258
+ * compiler options, or both.
259
+ *
260
+ * @remarks
261
+ * When both are given the tsconfig is loaded first and the inline options merge
262
+ * on top, so a caller can adopt a project's configuration and override one
263
+ * field without restating it.
264
+ *
265
+ * @public
266
+ */
267
+ interface TypeScriptConfig {
268
+ /**
269
+ * A `tsconfig.json` path, or a function returning compiler options.
270
+ *
271
+ * @remarks
272
+ * The function's options are **user input**, so they are typed loosely and
273
+ * decoded rather than trusted: a caller writing configuration in TypeScript
274
+ * may reasonably return either the tsconfig spelling (`target: "es2025"`) or
275
+ * the programmatic one (`target: ts.ScriptTarget.ES2025`).
276
+ */
277
+ tsconfig?: PathLike | (() => Promise<CompilerOptionsInput>);
278
+ /** User-supplied compiler options, in either spelling. Decoded, not trusted. */
279
+ compilerOptions?: CompilerOptionsInput;
280
+ }
281
+ /**
282
+ * Compiler options as a user may write them, before decoding.
283
+ *
284
+ * @remarks
285
+ * Deliberately loose. `@tsdoctor/vfs`'s `decodeCompilerOptions` is what turns
286
+ * this into a `TypeResolutionCompilerOptions`, accepting either spelling
287
+ * and REJECTING a value it cannot map rather than passing it through. This type
288
+ * exists so the untrusted shape and the decoded one cannot be confused at a
289
+ * call site — the previous single type was both, and user input reached the
290
+ * compiler through a cast.
291
+ *
292
+ * @public
293
+ */
294
+ type CompilerOptionsInput = Readonly<Record<string, unknown>>;
295
+ /**
296
+ * Default TypeScript compiler options for Twoslash and type resolution.
297
+ *
298
+ * These defaults are optimized for documentation:
299
+ * - Modern ES targets (ESNext)
300
+ * - Bundler module resolution for broad compatibility
301
+ * - Lenient settings (non-strict) since docs often show simplified examples
302
+ * - Skip lib checks for faster processing
303
+ *
304
+ * @remarks
305
+ * Numeric values correspond to TypeScript enums:
306
+ * - target: 99 = ESNext
307
+ * - module: 99 = ESNext
308
+ * - moduleResolution: 100 = Bundler
309
+ *
310
+ * @public
311
+ */
312
+ declare const DEFAULT_COMPILER_OPTIONS: TypeResolutionCompilerOptions;
313
+ /**
314
+ * Merge one set of compiler options over another, later winning per key.
315
+ *
316
+ * @remarks
317
+ * Arrays (`lib`, `types`) are REPLACED wholesale rather than concatenated,
318
+ * matching TypeScript's own `extends` semantics: declaring `lib` means "these
319
+ * libraries", not "these as well as the defaults".
320
+ *
321
+ * @param base - the options to start from
322
+ * @param override - the options to layer on top, or `undefined` for a copy of `base`
323
+ * @returns a new object; neither argument is mutated
324
+ *
325
+ * @public
326
+ */
327
+ declare function mergeCompilerOptions(base: TypeResolutionCompilerOptions, override: TypeResolutionCompilerOptions | undefined): TypeResolutionCompilerOptions;
328
+ /**
329
+ * Resolve a single TypeScriptConfig to compiler options (sync version).
330
+ * Only handles path-based tsconfig - use resolveTypeScriptConfigSingleAsync for function-based.
331
+ *
332
+ * Follows the priority cascade:
333
+ * 1. Parse tsconfig.json if specified (path only, not function)
334
+ * 2. Merge compilerOptions on top
335
+ *
336
+ * @param config - TypeScript config with optional tsconfig path and/or compilerOptions
337
+ * @param projectRoot - Project root for resolving relative tsconfig paths
338
+ * @returns Resolved compiler options (not merged with defaults)
339
+ *
340
+ * @example
341
+ * ```ts
342
+ * // Just tsconfig
343
+ * resolveTypeScriptConfigSingle({ tsconfig: "tsconfig.json" }, "/project");
344
+ *
345
+ * // Just compilerOptions
346
+ * resolveTypeScriptConfigSingle({ compilerOptions: { target: "esnext" } }, "/project");
347
+ *
348
+ * // Both (compilerOptions override tsconfig)
349
+ * resolveTypeScriptConfigSingle({
350
+ * tsconfig: "tsconfig.json",
351
+ * compilerOptions: { strict: false }
352
+ * }, "/project");
353
+ * ```
354
+ *
355
+ * @public
356
+ */
357
+ declare function resolveTypeScriptConfigSingle(config: TypeScriptConfig | undefined, projectRoot: string): TypeResolutionCompilerOptions;
358
+ /**
359
+ * Resolve a single TypeScriptConfig to compiler options (async version).
360
+ * Handles both path-based and function-based tsconfig.
361
+ *
362
+ * Follows the priority cascade:
363
+ * 1. Load tsconfig (from path or function)
364
+ * 2. Merge compilerOptions on top
365
+ *
366
+ * @param config - TypeScript config with optional tsconfig path/function and/or compilerOptions
367
+ * @param projectRoot - Project root for resolving relative tsconfig paths
368
+ * @returns Promise resolving to compiler options (not merged with defaults)
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * // Path-based tsconfig
373
+ * await resolveTypeScriptConfigSingleAsync({ tsconfig: "tsconfig.json" }, "/project");
374
+ *
375
+ * // Function-based tsconfig
376
+ * await resolveTypeScriptConfigSingleAsync({
377
+ * tsconfig: async () => ({ target: 99, lib: ["ESNext"] })
378
+ * }, "/project");
379
+ *
380
+ * // Both (compilerOptions override tsconfig)
381
+ * await resolveTypeScriptConfigSingleAsync({
382
+ * tsconfig: async () => ({ target: 99 }),
383
+ * compilerOptions: { strict: false }
384
+ * }, "/project");
385
+ * ```
386
+ *
387
+ * @public
388
+ */
389
+ declare function resolveTypeScriptConfigSingleAsync(config: TypeScriptConfig | undefined, projectRoot: string): Promise<TypeResolutionCompilerOptions>;
390
+ /**
391
+ * Resolve TypeScript compiler options from a cascade of configurations (async).
392
+ *
393
+ * Resolution order (later levels override earlier):
394
+ * 1. DEFAULT_COMPILER_OPTIONS (sensible defaults)
395
+ * 2. Global config
396
+ * 3. API-level config
397
+ *
398
+ * At each level, if a TypeScriptConfig has both `tsconfig` and `compilerOptions`,
399
+ * the tsconfig is loaded first, then compilerOptions are merged on top.
400
+ *
401
+ * @param projectRoot - Project root directory for resolving relative paths
402
+ * @param global - Global plugin TypeScript configuration
403
+ * @param api - API-level TypeScript configuration
404
+ * @returns Promise resolving to fully resolved compiler options
405
+ *
406
+ * @example
407
+ * ```ts
408
+ * // Simple global config
409
+ * const options = await resolveTypeScriptConfig("/project", {
410
+ * tsconfig: "tsconfig.json"
411
+ * });
412
+ *
413
+ * // With async tsconfig loader
414
+ * const options = await resolveTypeScriptConfig("/project", {
415
+ * tsconfig: async () => ({ target: 99, lib: ["ESNext"] })
416
+ * });
417
+ *
418
+ * // With API override
419
+ * const options = await resolveTypeScriptConfig(
420
+ * "/project",
421
+ * { tsconfig: "tsconfig.json" },
422
+ * { compilerOptions: { strict: false } }
423
+ * );
424
+ * ```
425
+ *
426
+ * @public
427
+ */
428
+ declare function resolveTypeScriptConfig(projectRoot: string, global?: TypeScriptConfig, api?: TypeScriptConfig): Promise<TypeResolutionCompilerOptions>;
429
+ //#endregion
430
+ //#region src/VirtualPackage.d.ts
431
+ declare const VirtualPackage_base: Schema.Class<VirtualPackage, Schema.Struct<{
432
+ /** The package name (e.g. `"@my-org/api-types"`). */
433
+ readonly name: Schema.String;
434
+ /** The package version. */
435
+ readonly version: Schema.String;
436
+ /** Entry file names (e.g. `"index.d.ts"`) mapped to declaration source. */
437
+ readonly entries: Schema.$ReadonlyMap<Schema.String, Schema.String>;
438
+ }>, {}>;
439
+ /**
440
+ * A synthetic npm package built from locally supplied TypeScript declaration
441
+ * content, for inclusion in a {@link Vfs} without fetching from the CDN.
442
+ *
443
+ * @remarks
444
+ * Useful when you have locally generated `.d.ts` files — API Extractor
445
+ * output, hand-written ambient declarations — and want them in the same VFS
446
+ * `TypeRegistry` builds from remote packages. Instances are transient: they
447
+ * are never persisted to the disk cache.
448
+ *
449
+ * The class is deliberately subclass-friendly (the rspress consumer extends
450
+ * it): construct via `VirtualPackage.make(...)` or the statics, and extend
451
+ * with `class Mine extends VirtualPackage { ... }`.
452
+ *
453
+ * @example
454
+ * ```ts
455
+ * import { VirtualPackage } from "@tsdoctor/vfs";
456
+ *
457
+ * const pkg = VirtualPackage.create("@my-org/api-types", "1.0.0", "export interface User { id: string }");
458
+ * const vfs = pkg.toVfs();
459
+ * // node_modules/@my-org/api-types/package.json, node_modules/@my-org/api-types/index.d.ts
460
+ * ```
461
+ *
462
+ * @public
463
+ */
464
+ declare class VirtualPackage extends VirtualPackage_base {
465
+ /**
466
+ * Single-entry factory: a virtual package whose sole entry point is
467
+ * `index.d.ts`.
468
+ */
469
+ static create(name: string, version: string, declarations: string): VirtualPackage;
470
+ /**
471
+ * Multi-entry factory: one `.d.ts` per entry point, exposed through a
472
+ * synthetic `exports` map.
473
+ *
474
+ * @remarks
475
+ * An empty entries map is developer wiring, not input — it would produce a
476
+ * package whose `types` points at a file that does not exist — so it
477
+ * throws at construction (defect posture), as does an entry set whose
478
+ * names collide after extension normalization (see
479
+ * {@link VirtualPackage.toVfs}).
480
+ */
481
+ static createMultiEntry(name: string, version: string, entries: ReadonlyMap<string, string>): VirtualPackage;
482
+ /**
483
+ * Load a single `.d.ts` file from disk as a virtual package with one
484
+ * `index.d.ts` entry.
485
+ *
486
+ * @remarks
487
+ * Reads through the platform-agnostic `FileSystem` service; the
488
+ * `PlatformError` surfaces typed.
489
+ */
490
+ static fromFile(name: string, version: string, filePath: string): Effect.Effect<VirtualPackage, PlatformError.PlatformError, FileSystem.FileSystem>;
491
+ /**
492
+ * The package's {@link Vfs}: a synthetic `package.json` plus every entry
493
+ * file, each path prefixed `node_modules/<name>/`.
494
+ *
495
+ * @remarks
496
+ * The `package.json` uses `types` for a single entry and an `exports` map
497
+ * for multiple entries, so TypeScript module resolution works against the
498
+ * generated VFS.
499
+ */
500
+ toVfs(): Vfs;
501
+ private toPackageJson;
502
+ }
503
+ //#endregion
504
+ export { type CompilerOptionsInput, DEFAULT_COMPILER_OPTIONS, TsConfigParseError, TsEnvironment, TsEnvironmentError, type TsEnvironmentOptions, TypeResolutionCompilerOptions, type TypeScriptConfig, type Vfs, VirtualPackage, decodeCompilerOptions, isTypeDefinition, mergeCompilerOptions, mergeVfs, parseTsConfig, prefixVfs, resolveTypeScriptConfig, resolveTypeScriptConfigSingle, resolveTypeScriptConfigSingleAsync, toProgrammaticCompilerOptions };
505
+ //# sourceMappingURL=index.d.ts.map
package/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import { TypeResolutionCompilerOptions, decodeCompilerOptions, toProgrammaticCompilerOptions } from "./TypeResolutionOptions.js";
2
+ import { TsConfigParseError, parseTsConfig } from "./TsconfigParser.js";
3
+ import { isTypeDefinition, mergeVfs, prefixVfs } from "./Vfs.js";
4
+ import { TsEnvironment, TsEnvironmentError } from "./TsEnvironment.js";
5
+ import { DEFAULT_COMPILER_OPTIONS, mergeCompilerOptions, resolveTypeScriptConfig, resolveTypeScriptConfigSingle, resolveTypeScriptConfigSingleAsync } from "./TypeScriptConfig.js";
6
+ import { VirtualPackage } from "./VirtualPackage.js";
7
+
8
+ export { DEFAULT_COMPILER_OPTIONS, TsConfigParseError, TsEnvironment, TsEnvironmentError, TypeResolutionCompilerOptions, VirtualPackage, decodeCompilerOptions, isTypeDefinition, mergeCompilerOptions, mergeVfs, parseTsConfig, prefixVfs, resolveTypeScriptConfig, resolveTypeScriptConfigSingle, resolveTypeScriptConfigSingleAsync, toProgrammaticCompilerOptions };