@tsdoctor/registry 0.2.2 → 0.3.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/README.md +4 -8
- package/TypeRegistry.js +1 -1
- package/index.d.ts +6 -207
- package/index.js +1 -4
- package/internal/resolution.js +2 -2
- package/package.json +7 -16
- package/TsEnvironment.js +0 -95
- package/Vfs.js +0 -33
- package/VirtualPackage.js +0 -126
package/README.md
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
|
-
TypeScript
|
|
8
|
+
External TypeScript type loading for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN into a `Vfs`, the virtual file system Twoslash-style documentation tooling type-checks against.
|
|
9
9
|
|
|
10
10
|
## Why @tsdoctor/registry
|
|
11
11
|
|
|
12
|
-
Documentation tooling that typechecks code samples needs the declaration files for whatever packages those samples import, and needs them without a real `node_modules`. Fetching them by hand means writing a CDN client, a disk cache with expiry, and a module resolver that understands `exports`, `typesVersions` and the legacy `types` field. This package is those three things behind one service, with typed errors and no hidden IO — every filesystem, HTTP and database dependency is provided by you at the edge.
|
|
12
|
+
Documentation tooling that typechecks code samples needs the declaration files for whatever packages those samples import, and needs them without a real `node_modules`. Fetching them by hand means writing a CDN client, a disk cache with expiry, and a module resolver that understands `exports`, `typesVersions` and the legacy `types` field. This package is those three things behind one service, with typed errors and no hidden IO — every filesystem, HTTP and database dependency is provided by you at the edge. What it produces is a `Vfs` from [`@tsdoctor/vfs`](../vfs), which also owns the virtual-package and `@typescript/vfs` environment primitives — this package fills a VFS, it does not define one.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -21,13 +21,11 @@ npm install @tsdoctor/registry effect @effect/platform-node @effected/store @eff
|
|
|
21
21
|
pnpm add @tsdoctor/registry effect @effect/platform-node @effected/store @effected/semver
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Requires Node.js >=24.11.0. Those four peers are required.
|
|
24
|
+
Requires Node.js >=24.11.0. Those four peers are required. `@effected/xdg` is optional and pulls in only with the feature that uses it:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
# for TypeCache.layerXdg
|
|
28
28
|
npm install @effected/xdg
|
|
29
|
-
# for TsEnvironment.make
|
|
30
|
-
npm install @effected/tsconfig-json typescript @typescript/vfs
|
|
31
29
|
```
|
|
32
30
|
|
|
33
31
|
Every dependency here is a peer rather than a bundled dependency, including `@effected/semver`, whose types appear in no exported signature. That is deliberate: each `@effected/*` package pins an exact `effect` version as its own peer, so bundling one would create a second resolution site that can land on a different `effect` build than yours and fail at import. As peers they all resolve in your closure, against your `effect`.
|
|
@@ -75,10 +73,8 @@ await Effect.runPromise(program.pipe(Effect.provide(RegistryLayer)));
|
|
|
75
73
|
- `TypeCache` — a two-plane cache: declaration files on disk, per-package metadata in an `@effected/store` `Cache` with native TTL expiry and pruning.
|
|
76
74
|
- `PackageFetcher` — the jsDelivr-backed CDN client, requiring only an `HttpClient`.
|
|
77
75
|
- `TypeResolver` — static resolution of import specifiers and type entry points against a package manifest, covering `exports`, `typesVersions` and legacy fields.
|
|
78
|
-
- `TsEnvironment` — builds a `VirtualTypeScriptEnvironment` over a VFS from tsconfig-JSON compiler options, loading the optional `typescript` peers lazily so a consumer that never calls it never loads the compiler.
|
|
79
|
-
- `VirtualPackage` — synthesizes a package from locally supplied declaration content, for API Extractor output and hand-written ambient types.
|
|
80
76
|
- `RegistryEvent` and `RegistryObserver` — an opt-in, zero-cost progress channel; the library logs nothing on its own.
|
|
81
|
-
- Typed errors throughout: `FetchError`, `PackageNotFoundError`, `VersionNotFoundError`, `TypeCacheError`, `BatchLoadError
|
|
77
|
+
- Typed errors throughout: `FetchError`, `PackageNotFoundError`, `VersionNotFoundError`, `TypeCacheError`, `BatchLoadError`.
|
|
82
78
|
|
|
83
79
|
## Documentation
|
|
84
80
|
|
package/TypeRegistry.js
CHANGED
|
@@ -3,8 +3,8 @@ import { emit } from "./RegistryEvent.js";
|
|
|
3
3
|
import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
|
|
4
4
|
import { TypeCache, TypeCacheMetadata } from "./TypeCache.js";
|
|
5
5
|
import { TypeResolver } from "./TypeResolver.js";
|
|
6
|
-
import { mergeVfs } from "./Vfs.js";
|
|
7
6
|
import { Context, DateTime, Effect, Layer, Option, Schema, Semaphore } from "effect";
|
|
7
|
+
import { mergeVfs } from "@tsdoctor/vfs";
|
|
8
8
|
import { Range, SemVer } from "@effected/semver";
|
|
9
9
|
|
|
10
10
|
//#region src/TypeRegistry.ts
|
package/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { Cause, Context, Duration, Effect, FileSystem, Layer, Option, Path,
|
|
1
|
+
import { Cause, Context, Duration, Effect, FileSystem, Layer, Option, Path, Schema } from "effect";
|
|
2
2
|
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
3
|
-
import { CompilerOptions } from "@effected/tsconfig-json";
|
|
4
|
-
import { VirtualTypeScriptEnvironment } from "@typescript/vfs";
|
|
5
3
|
import { Cache } from "@effected/store";
|
|
6
4
|
import { AppDirs, AppDirsError } from "@effected/xdg";
|
|
5
|
+
import { Vfs } from "@tsdoctor/vfs";
|
|
7
6
|
//#region src/PackageSpec.d.ts
|
|
8
7
|
declare const PackageSpec_base: Schema.Class<PackageSpec, Schema.Struct<{
|
|
9
8
|
/** The npm package name (e.g. `"zod"`, `"@effect/schema"`). */
|
|
@@ -365,132 +364,6 @@ declare class RegistryObserver extends RegistryObserver_base {
|
|
|
365
364
|
static readonly layerNoop: Layer.Layer<RegistryObserver>;
|
|
366
365
|
}
|
|
367
366
|
//#endregion
|
|
368
|
-
//#region src/Vfs.d.ts
|
|
369
|
-
/**
|
|
370
|
-
* The package's currency type: a virtual file system mapping
|
|
371
|
-
* `node_modules/`-prefixed paths to file contents.
|
|
372
|
-
*/
|
|
373
|
-
/**
|
|
374
|
-
* A virtual file system: file paths (prefixed `node_modules/<package>/`)
|
|
375
|
-
* mapped to their string contents.
|
|
376
|
-
*
|
|
377
|
-
* @remarks
|
|
378
|
-
* This is the value every loading operation produces and every TypeScript
|
|
379
|
-
* integration consumes. Maps from multiple packages merge with {@link mergeVfs};
|
|
380
|
-
* `@typescript/vfs` consumes the merged map directly (see `TsEnvironment`).
|
|
381
|
-
*
|
|
382
|
-
* @public
|
|
383
|
-
*/
|
|
384
|
-
type Vfs = Map<string, string>;
|
|
385
|
-
/**
|
|
386
|
-
* The v3 name for {@link Vfs}, kept as an alias for the consumer migration.
|
|
387
|
-
*
|
|
388
|
-
* @public
|
|
389
|
-
*/
|
|
390
|
-
type VirtualFileSystem = Vfs;
|
|
391
|
-
/**
|
|
392
|
-
* Merge VFS maps left to right into a new map; later entries win on path
|
|
393
|
-
* collisions.
|
|
394
|
-
*
|
|
395
|
-
* @example
|
|
396
|
-
* ```ts
|
|
397
|
-
* import { mergeVfs } from "@tsdoctor/registry";
|
|
398
|
-
*
|
|
399
|
-
* const combined = mergeVfs(vfsA, vfsB);
|
|
400
|
-
* ```
|
|
401
|
-
*
|
|
402
|
-
* @public
|
|
403
|
-
*/
|
|
404
|
-
declare const mergeVfs: (...maps: ReadonlyArray<ReadonlyMap<string, string>>) => Vfs;
|
|
405
|
-
/**
|
|
406
|
-
* Prefix every path in `entries` with `node_modules/<name>/`, normalizing
|
|
407
|
-
* away leading slashes.
|
|
408
|
-
*
|
|
409
|
-
* @public
|
|
410
|
-
*/
|
|
411
|
-
declare const prefixVfs: (name: string, entries: ReadonlyMap<string, string>) => Vfs;
|
|
412
|
-
//#endregion
|
|
413
|
-
//#region src/TsEnvironment.d.ts
|
|
414
|
-
declare const TsEnvironmentError_base: Schema.Class<TsEnvironmentError, Schema.TaggedStruct<"TsEnvironmentError", {
|
|
415
|
-
/** The underlying failure, preserved structurally. */
|
|
416
|
-
readonly cause: Schema.Defect;
|
|
417
|
-
}>, import("effect/Cause").YieldableError>;
|
|
418
|
-
/**
|
|
419
|
-
* Raised when building a virtual TypeScript environment fails — including
|
|
420
|
-
* when the optional `typescript` / `@typescript/vfs` /
|
|
421
|
-
* `@effected/tsconfig-json` peers are not installed.
|
|
422
|
-
*
|
|
423
|
-
* @public
|
|
424
|
-
*/
|
|
425
|
-
declare class TsEnvironmentError extends TsEnvironmentError_base {
|
|
426
|
-
get message(): string;
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Options for {@link TsEnvironment.make}.
|
|
430
|
-
*
|
|
431
|
-
* @public
|
|
432
|
-
*/
|
|
433
|
-
interface TsEnvironmentOptions {
|
|
434
|
-
/** The virtual file system to typecheck against. */
|
|
435
|
-
readonly vfs: Vfs;
|
|
436
|
-
/**
|
|
437
|
-
* Compiler options for the language service, in tsconfig JSON form
|
|
438
|
-
* (`{ target: "es2022" }`, not `ts.ScriptTarget.ES2022`). Enum-valued
|
|
439
|
-
* fields are converted to the compiler's numeric enums internally, so this
|
|
440
|
-
* type has no dependency on the `typescript` package.
|
|
441
|
-
*/
|
|
442
|
-
readonly compilerOptions: CompilerOptions.Type;
|
|
443
|
-
/**
|
|
444
|
-
* The directory VFS paths are rooted under and the filesystem fallback
|
|
445
|
-
* root. Defaults to `process.cwd()` (which v3 hardcoded).
|
|
446
|
-
*/
|
|
447
|
-
readonly projectRoot?: string;
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* The `@typescript/vfs` seam: builds a `VirtualTypeScriptEnvironment` over a
|
|
451
|
-
* {@link Vfs} plus the TypeScript default lib files.
|
|
452
|
-
*
|
|
453
|
-
* @remarks
|
|
454
|
-
* The ONLY module touching the optional `typescript` / `@typescript/vfs` /
|
|
455
|
-
* `@effected/tsconfig-json` peers, and it loads all three lazily inside
|
|
456
|
-
* {@link TsEnvironment.make} — a consumer that never calls it never loads
|
|
457
|
-
* the compiler, and a missing peer fails typed as
|
|
458
|
-
* {@link TsEnvironmentError} instead of crashing at import time. Keep every
|
|
459
|
-
* one of them behind that dynamic `import()`: a static value import here is
|
|
460
|
-
* reachable from `index.ts`, so it would turn an omitted optional peer into
|
|
461
|
-
* an `ERR_MODULE_NOT_FOUND` on the entry graph for consumers who never
|
|
462
|
-
* touch this module. Only the type-only `CompilerOptions` import is safe
|
|
463
|
-
* statically, because it erases. The underlying `createDefaultMapFromNodeModules` /
|
|
464
|
-
* `createFSBackedSystem` read the real filesystem through TypeScript's own
|
|
465
|
-
* `sys`, outside the Effect `FileSystem` service — accepted and documented;
|
|
466
|
-
* this module is why the package is integrated tier on its own surface.
|
|
467
|
-
*
|
|
468
|
-
* No cache map (v3's `createTypeScriptCache` returned a one-entry `Map`
|
|
469
|
-
* keyed by `JSON.stringify(compilerOptions)`): a consumer that wants keyed
|
|
470
|
-
* reuse holds its own map.
|
|
471
|
-
*
|
|
472
|
-
* `VirtualTypeScriptEnvironment` is deliberately not re-exported — import
|
|
473
|
-
* the type from `@typescript/vfs`, which consumers of this module already
|
|
474
|
-
* declare.
|
|
475
|
-
*
|
|
476
|
-
* @example
|
|
477
|
-
* ```ts
|
|
478
|
-
* import { TsEnvironment } from "@tsdoctor/registry";
|
|
479
|
-
*
|
|
480
|
-
* const environment = TsEnvironment.make({
|
|
481
|
-
* vfs,
|
|
482
|
-
* compilerOptions: { strict: true, target: "es2022" },
|
|
483
|
-
* });
|
|
484
|
-
* ```
|
|
485
|
-
*
|
|
486
|
-
* @public
|
|
487
|
-
*/
|
|
488
|
-
declare class TsEnvironment {
|
|
489
|
-
private constructor();
|
|
490
|
-
/** Build a `VirtualTypeScriptEnvironment` over a {@link Vfs}. */
|
|
491
|
-
static make(options: TsEnvironmentOptions): Effect.Effect<VirtualTypeScriptEnvironment, TsEnvironmentError>;
|
|
492
|
-
}
|
|
493
|
-
//#endregion
|
|
494
367
|
//#region src/TypeCache.d.ts
|
|
495
368
|
declare const TypeCacheMetadata_base: Schema.Class<TypeCacheMetadata, Schema.Struct<{
|
|
496
369
|
/** The pinned version the files on disk belong to. */
|
|
@@ -629,7 +502,7 @@ interface TypeCacheShape {
|
|
|
629
502
|
readonly readMetadata: (pkg: PackageSpec) => Effect.Effect<Option.Option<TypeCacheMetadata>, TypeCacheError>;
|
|
630
503
|
/** Write the package's metadata entry, forwarding its `ttl` to the store. */
|
|
631
504
|
readonly writeMetadata: (pkg: PackageSpec, metadata: TypeCacheMetadata) => Effect.Effect<void, TypeCacheError>;
|
|
632
|
-
/** Build the package's
|
|
505
|
+
/** Build the package's `Vfs`: every cached file keyed `node_modules/<name>/<path>`. */
|
|
633
506
|
readonly getVfs: (pkg: PackageSpec) => Effect.Effect<Vfs, TypeCacheError>;
|
|
634
507
|
/**
|
|
635
508
|
* Remove the package: metadata first, then files.
|
|
@@ -842,7 +715,7 @@ interface TypeRegistryShape {
|
|
|
842
715
|
readonly ttl?: Duration.Duration;
|
|
843
716
|
}) => Effect.Effect<void, FetchError | PackageNotFoundError | TypeCacheError>;
|
|
844
717
|
/**
|
|
845
|
-
* Build the
|
|
718
|
+
* Build the `Vfs` for one package, fetching it first when missing or
|
|
846
719
|
* stale (the stale-vs-miss ladder: live metadata → hit; files on disk with
|
|
847
720
|
* no live metadata → stale, refetched when `autoFetch`, served as-is
|
|
848
721
|
* otherwise; nothing → miss, fetched or failed typed on
|
|
@@ -850,7 +723,7 @@ interface TypeRegistryShape {
|
|
|
850
723
|
*/
|
|
851
724
|
readonly getPackageVfs: (pkg: PackageSpec, options?: PackageVfsOptions) => Effect.Effect<Vfs, FetchError | PackageNotFoundError | TypeCacheError>;
|
|
852
725
|
/**
|
|
853
|
-
* Build a merged
|
|
726
|
+
* Build a merged `Vfs` for several packages, best-effort.
|
|
854
727
|
*
|
|
855
728
|
* @remarks
|
|
856
729
|
* Loads concurrently (limit 5), accumulates per-package failures, merges
|
|
@@ -912,79 +785,5 @@ declare class TypeRegistry extends TypeRegistry_base {
|
|
|
912
785
|
static readonly layer: Layer.Layer<TypeRegistry, never, TypeCache | PackageFetcher>;
|
|
913
786
|
}
|
|
914
787
|
//#endregion
|
|
915
|
-
|
|
916
|
-
declare const VirtualPackage_base: Schema.Class<VirtualPackage, Schema.Struct<{
|
|
917
|
-
/** The package name (e.g. `"@my-org/api-types"`). */
|
|
918
|
-
readonly name: Schema.String;
|
|
919
|
-
/** The package version. */
|
|
920
|
-
readonly version: Schema.String;
|
|
921
|
-
/** Entry file names (e.g. `"index.d.ts"`) mapped to declaration source. */
|
|
922
|
-
readonly entries: Schema.$ReadonlyMap<Schema.String, Schema.String>;
|
|
923
|
-
}>, {}>;
|
|
924
|
-
/**
|
|
925
|
-
* A synthetic npm package built from locally supplied TypeScript declaration
|
|
926
|
-
* content, for inclusion in a {@link Vfs} without fetching from the CDN.
|
|
927
|
-
*
|
|
928
|
-
* @remarks
|
|
929
|
-
* Useful when you have locally generated `.d.ts` files — API Extractor
|
|
930
|
-
* output, hand-written ambient declarations — and want them in the same VFS
|
|
931
|
-
* `TypeRegistry` builds from remote packages. Instances are transient: they
|
|
932
|
-
* are never persisted to the disk cache.
|
|
933
|
-
*
|
|
934
|
-
* The class is deliberately subclass-friendly (the rspress consumer extends
|
|
935
|
-
* it): construct via `VirtualPackage.make(...)` or the statics, and extend
|
|
936
|
-
* with `class Mine extends VirtualPackage { ... }`.
|
|
937
|
-
*
|
|
938
|
-
* @example
|
|
939
|
-
* ```ts
|
|
940
|
-
* import { VirtualPackage } from "@tsdoctor/registry";
|
|
941
|
-
*
|
|
942
|
-
* const pkg = VirtualPackage.create("@my-org/api-types", "1.0.0", "export interface User { id: string }");
|
|
943
|
-
* const vfs = pkg.toVfs();
|
|
944
|
-
* // node_modules/@my-org/api-types/package.json, node_modules/@my-org/api-types/index.d.ts
|
|
945
|
-
* ```
|
|
946
|
-
*
|
|
947
|
-
* @public
|
|
948
|
-
*/
|
|
949
|
-
declare class VirtualPackage extends VirtualPackage_base {
|
|
950
|
-
/**
|
|
951
|
-
* Single-entry factory: a virtual package whose sole entry point is
|
|
952
|
-
* `index.d.ts`.
|
|
953
|
-
*/
|
|
954
|
-
static create(name: string, version: string, declarations: string): VirtualPackage;
|
|
955
|
-
/**
|
|
956
|
-
* Multi-entry factory: one `.d.ts` per entry point, exposed through a
|
|
957
|
-
* synthetic `exports` map.
|
|
958
|
-
*
|
|
959
|
-
* @remarks
|
|
960
|
-
* An empty entries map is developer wiring, not input — it would produce a
|
|
961
|
-
* package whose `types` points at a file that does not exist — so it
|
|
962
|
-
* throws at construction (defect posture), as does an entry set whose
|
|
963
|
-
* names collide after extension normalization (see
|
|
964
|
-
* {@link VirtualPackage.toVfs}).
|
|
965
|
-
*/
|
|
966
|
-
static createMultiEntry(name: string, version: string, entries: ReadonlyMap<string, string>): VirtualPackage;
|
|
967
|
-
/**
|
|
968
|
-
* Load a single `.d.ts` file from disk as a virtual package with one
|
|
969
|
-
* `index.d.ts` entry.
|
|
970
|
-
*
|
|
971
|
-
* @remarks
|
|
972
|
-
* Reads through the platform-agnostic `FileSystem` service; the
|
|
973
|
-
* `PlatformError` surfaces typed.
|
|
974
|
-
*/
|
|
975
|
-
static fromFile(name: string, version: string, filePath: string): Effect.Effect<VirtualPackage, PlatformError.PlatformError, FileSystem.FileSystem>;
|
|
976
|
-
/**
|
|
977
|
-
* The package's {@link Vfs}: a synthetic `package.json` plus every entry
|
|
978
|
-
* file, each path prefixed `node_modules/<name>/`.
|
|
979
|
-
*
|
|
980
|
-
* @remarks
|
|
981
|
-
* The `package.json` uses `types` for a single entry and an `exports` map
|
|
982
|
-
* for multiple entries, so TypeScript module resolution works against the
|
|
983
|
-
* generated VFS.
|
|
984
|
-
*/
|
|
985
|
-
toVfs(): Vfs;
|
|
986
|
-
private toPackageJson;
|
|
987
|
-
}
|
|
988
|
-
//#endregion
|
|
989
|
-
export { BatchLoadError, type CachePruneResult, FetchError, PackageFetcher, type PackageFetcherShape, PackageManifest, PackageNotFoundError, PackageSpec, type PackageVersions, type PackageVfsOptions, RegistryEvent, RegistryObserver, type RegistryObserverShape, ResolvedModule, TsEnvironment, TsEnvironmentError, type TsEnvironmentOptions, TypeCache, TypeCacheError, TypeCacheMetadata, type TypeCacheShape, TypeRegistry, type TypeRegistryShape, TypeResolver, VersionNotFoundError, type Vfs, type VirtualFileSystem, VirtualPackage, mergeVfs, prefixVfs };
|
|
788
|
+
export { BatchLoadError, type CachePruneResult, FetchError, PackageFetcher, type PackageFetcherShape, PackageManifest, PackageNotFoundError, PackageSpec, type PackageVersions, type PackageVfsOptions, RegistryEvent, RegistryObserver, type RegistryObserverShape, ResolvedModule, TypeCache, TypeCacheError, TypeCacheMetadata, type TypeCacheShape, TypeRegistry, type TypeRegistryShape, TypeResolver, VersionNotFoundError };
|
|
990
789
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { RegistryEvent, RegistryObserver } from "./RegistryEvent.js";
|
|
2
2
|
import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
|
|
3
3
|
import { PackageSpec } from "./PackageSpec.js";
|
|
4
|
-
import { TsEnvironment, TsEnvironmentError } from "./TsEnvironment.js";
|
|
5
4
|
import { TypeCache, TypeCacheError, TypeCacheMetadata } from "./TypeCache.js";
|
|
6
5
|
import { ResolvedModule, TypeResolver } from "./TypeResolver.js";
|
|
7
|
-
import { mergeVfs, prefixVfs } from "./Vfs.js";
|
|
8
6
|
import { BatchLoadError, TypeRegistry } from "./TypeRegistry.js";
|
|
9
|
-
import { VirtualPackage } from "./VirtualPackage.js";
|
|
10
7
|
|
|
11
|
-
export { BatchLoadError, FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, PackageSpec, RegistryEvent, RegistryObserver, ResolvedModule,
|
|
8
|
+
export { BatchLoadError, FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, PackageSpec, RegistryEvent, RegistryObserver, ResolvedModule, TypeCache, TypeCacheError, TypeCacheMetadata, TypeRegistry, TypeResolver, VersionNotFoundError };
|
package/internal/resolution.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isTypeDefinition } from "@tsdoctor/vfs";
|
|
2
|
+
|
|
1
3
|
//#region src/internal/resolution.ts
|
|
2
4
|
/**
|
|
3
5
|
* Exports-map resolution machinery. Every input here — `exports` values,
|
|
@@ -13,8 +15,6 @@ const DUNDER_KEYS = /* @__PURE__ */ new Set([
|
|
|
13
15
|
"constructor",
|
|
14
16
|
"prototype"
|
|
15
17
|
]);
|
|
16
|
-
/** Whether a path names a TypeScript declaration file. */
|
|
17
|
-
const isTypeDefinition = (filePath) => filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts");
|
|
18
18
|
/** Normalize backslashes to forward slashes. */
|
|
19
19
|
const normalizePath = (path) => path.replace(/\\/g, "/");
|
|
20
20
|
/**
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsdoctor/registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "TypeScript
|
|
5
|
+
"description": "External TypeScript type loading for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN into a virtual file system.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"typescript",
|
|
8
8
|
"vfs",
|
|
@@ -37,28 +37,19 @@
|
|
|
37
37
|
},
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@tsdoctor/vfs": "0.1.0"
|
|
42
|
+
},
|
|
40
43
|
"peerDependencies": {
|
|
41
44
|
"@effect/platform-node": "4.0.0-rc.109",
|
|
42
45
|
"@effected/semver": "^0.5.0",
|
|
43
|
-
"@effected/store": "^0.
|
|
44
|
-
"@effected/tsconfig-json": "^0.6.1",
|
|
46
|
+
"@effected/store": "^0.6.0",
|
|
45
47
|
"@effected/xdg": "^0.3.0",
|
|
46
|
-
"
|
|
47
|
-
"effect": "4.0.0-rc.109",
|
|
48
|
-
"typescript": "^6.0.3"
|
|
48
|
+
"effect": "4.0.0-rc.109"
|
|
49
49
|
},
|
|
50
50
|
"peerDependenciesMeta": {
|
|
51
|
-
"@effected/tsconfig-json": {
|
|
52
|
-
"optional": true
|
|
53
|
-
},
|
|
54
51
|
"@effected/xdg": {
|
|
55
52
|
"optional": true
|
|
56
|
-
},
|
|
57
|
-
"@typescript/vfs": {
|
|
58
|
-
"optional": true
|
|
59
|
-
},
|
|
60
|
-
"typescript": {
|
|
61
|
-
"optional": true
|
|
62
53
|
}
|
|
63
54
|
},
|
|
64
55
|
"engines": {
|
package/TsEnvironment.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { isTypeDefinition } from "./internal/resolution.js";
|
|
2
|
-
import { Effect, Schema } from "effect";
|
|
3
|
-
|
|
4
|
-
//#region src/TsEnvironment.ts
|
|
5
|
-
/**
|
|
6
|
-
* Raised when building a virtual TypeScript environment fails — including
|
|
7
|
-
* when the optional `typescript` / `@typescript/vfs` /
|
|
8
|
-
* `@effected/tsconfig-json` peers are not installed.
|
|
9
|
-
*
|
|
10
|
-
* @public
|
|
11
|
-
*/
|
|
12
|
-
var TsEnvironmentError = class extends Schema.TaggedError()("TsEnvironmentError", {
|
|
13
|
-
/** The underlying failure, preserved structurally. */
|
|
14
|
-
cause: Schema.Defect() }) {
|
|
15
|
-
get message() {
|
|
16
|
-
return "Failed to create the virtual TypeScript environment";
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* The `@typescript/vfs` seam: builds a `VirtualTypeScriptEnvironment` over a
|
|
21
|
-
* {@link Vfs} plus the TypeScript default lib files.
|
|
22
|
-
*
|
|
23
|
-
* @remarks
|
|
24
|
-
* The ONLY module touching the optional `typescript` / `@typescript/vfs` /
|
|
25
|
-
* `@effected/tsconfig-json` peers, and it loads all three lazily inside
|
|
26
|
-
* {@link TsEnvironment.make} — a consumer that never calls it never loads
|
|
27
|
-
* the compiler, and a missing peer fails typed as
|
|
28
|
-
* {@link TsEnvironmentError} instead of crashing at import time. Keep every
|
|
29
|
-
* one of them behind that dynamic `import()`: a static value import here is
|
|
30
|
-
* reachable from `index.ts`, so it would turn an omitted optional peer into
|
|
31
|
-
* an `ERR_MODULE_NOT_FOUND` on the entry graph for consumers who never
|
|
32
|
-
* touch this module. Only the type-only `CompilerOptions` import is safe
|
|
33
|
-
* statically, because it erases. The underlying `createDefaultMapFromNodeModules` /
|
|
34
|
-
* `createFSBackedSystem` read the real filesystem through TypeScript's own
|
|
35
|
-
* `sys`, outside the Effect `FileSystem` service — accepted and documented;
|
|
36
|
-
* this module is why the package is integrated tier on its own surface.
|
|
37
|
-
*
|
|
38
|
-
* No cache map (v3's `createTypeScriptCache` returned a one-entry `Map`
|
|
39
|
-
* keyed by `JSON.stringify(compilerOptions)`): a consumer that wants keyed
|
|
40
|
-
* reuse holds its own map.
|
|
41
|
-
*
|
|
42
|
-
* `VirtualTypeScriptEnvironment` is deliberately not re-exported — import
|
|
43
|
-
* the type from `@typescript/vfs`, which consumers of this module already
|
|
44
|
-
* declare.
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```ts
|
|
48
|
-
* import { TsEnvironment } from "@tsdoctor/registry";
|
|
49
|
-
*
|
|
50
|
-
* const environment = TsEnvironment.make({
|
|
51
|
-
* vfs,
|
|
52
|
-
* compilerOptions: { strict: true, target: "es2022" },
|
|
53
|
-
* });
|
|
54
|
-
* ```
|
|
55
|
-
*
|
|
56
|
-
* @public
|
|
57
|
-
*/
|
|
58
|
-
var TsEnvironment = class {
|
|
59
|
-
constructor() {}
|
|
60
|
-
/** Build a `VirtualTypeScriptEnvironment` over a {@link Vfs}. */
|
|
61
|
-
static make(options) {
|
|
62
|
-
return Effect.gen(function* () {
|
|
63
|
-
const [tsModule, tsVfs, { TsEnumCodec }] = yield* Effect.tryPromise({
|
|
64
|
-
try: () => Promise.all([
|
|
65
|
-
import("typescript"),
|
|
66
|
-
import("@typescript/vfs"),
|
|
67
|
-
import("@effected/tsconfig-json")
|
|
68
|
-
]),
|
|
69
|
-
catch: (cause) => new TsEnvironmentError({ cause })
|
|
70
|
-
});
|
|
71
|
-
return yield* Effect.try({
|
|
72
|
-
try: () => {
|
|
73
|
-
const typescript = tsModule.default;
|
|
74
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
75
|
-
const compilerOptions = TsEnumCodec.encodeCompilerOptions(options.compilerOptions);
|
|
76
|
-
const executing = typescript.sys?.getExecutingFilePath?.();
|
|
77
|
-
const libDirectory = executing === void 0 ? void 0 : executing.slice(0, Math.max(executing.lastIndexOf("/"), executing.lastIndexOf("\\")));
|
|
78
|
-
const system = new Map(tsVfs.createDefaultMapFromNodeModules(compilerOptions, typescript, libDirectory));
|
|
79
|
-
const rootFiles = [];
|
|
80
|
-
for (const [path, content] of options.vfs) {
|
|
81
|
-
const rooted = path.startsWith("/") ? path : `${projectRoot}/${path}`;
|
|
82
|
-
system.set(rooted, content);
|
|
83
|
-
if (isTypeDefinition(rooted)) rootFiles.push(rooted);
|
|
84
|
-
}
|
|
85
|
-
const sys = tsVfs.createFSBackedSystem(system, projectRoot, typescript, libDirectory);
|
|
86
|
-
return tsVfs.createVirtualTypeScriptEnvironment(sys, rootFiles, typescript, compilerOptions);
|
|
87
|
-
},
|
|
88
|
-
catch: (cause) => new TsEnvironmentError({ cause })
|
|
89
|
-
});
|
|
90
|
-
}).pipe(Effect.withSpan("TsEnvironment.make"));
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
//#endregion
|
|
95
|
-
export { TsEnvironment, TsEnvironmentError };
|
package/Vfs.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
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/registry";
|
|
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
|
-
//#endregion
|
|
33
|
-
export { mergeVfs, prefixVfs };
|
package/VirtualPackage.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
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/registry";
|
|
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 };
|