@effected/tsconfig-json 0.6.0 → 0.7.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.
@@ -0,0 +1,52 @@
1
+ import { CompilerOptions } from "./CompilerOptions.js";
2
+ import { TsEnumCodec } from "./TsEnumCodec.js";
3
+ import { Schema, SchemaTransformation } from "effect";
4
+
5
+ //#region src/CompilerOptionsFromProgrammatic.ts
6
+ const normalizeIn = (input) => TsEnumCodec.decodeCompilerOptions(input);
7
+ const encodeOut = (encoded) => TsEnumCodec.encodeCompilerOptions(encoded);
8
+ /**
9
+ * A codec between the **programmatic** `compilerOptions` shape TypeScript's own
10
+ * API uses and this package's decoded {@link (CompilerOptions:namespace).Type}.
11
+ *
12
+ * Decoding accepts the numeric-enum spelling (`{ target: ts.ScriptTarget.ES2025 }`),
13
+ * the canonical string spelling, case-varying strings (`"ESNext"`), any mixture of
14
+ * the three in one object, and `lib` entries in any of their three spellings
15
+ * (`"esnext"`, `"lib.esnext.d.ts"`, an absolute path to the lib file) — producing
16
+ * validated {@link (CompilerOptions:namespace).Type} with canonical lowercase enum
17
+ * strings and short-form `lib`. Unknown and dead keys pass through, exactly as
18
+ * {@link (CompilerOptions:variable)} itself allows. Decoding is idempotent on
19
+ * already-canonical input.
20
+ *
21
+ * Encoding is {@link TsEnumCodec.encodeCompilerOptions}: numeric enum values and
22
+ * `lib` in the file-name form (`lib.esnext.d.ts`).
23
+ *
24
+ * @remarks
25
+ * A numeric value with no table entry — a future TypeScript enum member — survives
26
+ * normalization as a number and then **fails decode** with a typed schema issue,
27
+ * rather than passing through. That is deliberate: this is the validating door
28
+ * {@link TsEnumCodec.decodeCompilerOptions} is not, which is why that function's
29
+ * return type stays the wider `Record<string, unknown>`.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { Schema } from "effect";
34
+ * import { CompilerOptionsFromProgrammatic } from "@effected/tsconfig-json";
35
+ *
36
+ * // { target: "es2025", strict: true, lib: ["esnext"] }
37
+ * Schema.decodeUnknownSync(CompilerOptionsFromProgrammatic)({
38
+ * target: 12,
39
+ * strict: true,
40
+ * lib: ["lib.esnext.d.ts"],
41
+ * });
42
+ * ```
43
+ *
44
+ * @public
45
+ */
46
+ const CompilerOptionsFromProgrammatic = Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.decodeTo(CompilerOptions, SchemaTransformation.transform({
47
+ decode: normalizeIn,
48
+ encode: encodeOut
49
+ })));
50
+
51
+ //#endregion
52
+ export { CompilerOptionsFromProgrammatic };
package/README.md CHANGED
@@ -114,7 +114,7 @@ const compilerOptions = TsconfigLoaderSync.compilerOptions("./tsconfig.json", op
114
114
  TypeScript 7's native `tsc` ships a version-only stub as its `.` export — there is no JS compiler API behind it. A package that drives the typechecker as a library (`@typescript/vfs`, the language service, the Program API) can neither type against nor runtime-load a TypeScript 7 install, and the obvious fix of pinning the dev `typescript` back to 6 forfeits the native `tsc` and leaves toolchain peers on `^7` unmet. The recipe that keeps both starts here, because this package is what removes the compile-time half of the dependency.
115
115
 
116
116
  1. Type against the tsconfig JSON form, not the compiler. Public option types use `CompilerOptions.Type` — `{ target: "es2022" }`, strings all the way down — so nothing in `src` or the tests imports `typescript`, not even as a type. The dev `typescript` stays on 7, native `tsc --noEmit` still runs the typecheck, and the toolchain's `^7` peer stays satisfied.
117
- 2. Convert at a single runtime seam. `TsEnumCodec.encodeCompilerOptions` maps the string form to the numeric enums a real compiler expects and returns `ProgrammaticCompilerOptions`, the shape a `ts.CompilerOptions`-typed API takes without a cast. Only that one call site knows a compiler exists.
117
+ 2. Convert at a single runtime seam. `TsEnumCodec.encodeCompilerOptions` maps the string form to the numeric enums a real compiler expects and returns `ProgrammaticCompilerOptions`, the shape a `ts.CompilerOptions`-typed API takes without a cast. Only that one call site knows a compiler exists. The inbound direction is `CompilerOptionsFromProgrammatic`, a schema rather than a function: hand it a live `ts.CompilerOptions`, a numeric enum literal or the output of `encodeCompilerOptions` and validated `CompilerOptions.Type` comes back, with an unmappable numeric failing decode instead of leaking through.
118
118
  3. Alias a classic install for the test runtime, where the JS API is genuinely needed: a dev-only `"typescript-classic": "npm:typescript@^6.0.3"` plus a vitest `resolve.alias` mapping `typescript` to it. The consumer-facing peer stays an optional `^6` — runtime-only, for callers of the compiler-touching module.
119
119
  4. Pass `tsLibDirectory` explicitly when you drive `@typescript/vfs`. It locates `lib.*.d.ts` through `require.resolve("typescript")`, which under this setup resolves the TypeScript 7 install, and that install has no lib directory: the map comes back **empty**, silently, with no error to follow. Derive the directory from the module you actually loaded.
120
120
 
@@ -159,6 +159,7 @@ console.log(fsMap.size);
159
159
  - `ResolvedTsconfig` — the pure merge engine behind `resolve`: per-field merge semantics, path-option absolutization against the declaring config's directory, final `${configDir}` substitution and `pathsBase` provenance, with no filesystem access at all.
160
160
  - `TsconfigDiscovery.findNearest` — the nearest `tsconfig.json` (or any filename via `options.filename`) at or above a starting directory, over `@effected/walker`; one unreadable ancestor cannot hide a config above it.
161
161
  - `TsEnumCodec` — the string↔numeric enum tables as plain data with zero `typescript` imports. `encodeCompilerOptions` returns the exported `ProgrammaticCompilerOptions` type — the numeric shape `ts.CompilerOptions` expects, so you hand it to a `ts.CompilerOptions`-shaped API without a cast — with `lib` entries in the file-name form the compiler resolves verbatim; `decodeCompilerOptions` reverses it.
162
+ - `CompilerOptionsFromProgrammatic` — the same tables as a validating codec, for callers holding TypeScript's programmatic spelling. Decode accepts numeric enums, canonical strings, case-varying strings, any mixture of the three and `lib` in any of its three spellings; encode returns the numeric form. A numeric with no table entry fails decode as a typed schema issue rather than passing through, which is the check `TsEnumCodec.decodeCompilerOptions` deliberately does not make.
162
163
  - `PortableTsconfig.make` — an allow-list projection down to machine-independent type-semantics options, with `composite: false` and `noEmit: true` forced: the slice a virtual TypeScript environment (Twoslash, API Extractor, an in-memory language service) can safely inherit. An optional `{ includeTypes }` argument carries the source config's `types` package names onto the portable shape too, for a caller whose virtual environment can resolve `@types` packages itself; `typeRoots` stays dropped either way, since it names machine-specific, config-location-dependent directories.
163
164
  - `JsxConfig.fromCompilerOptions` — the JSX transform a bundler can configure, projected from decoded options: `react-jsx` / `react-jsxdev` select the automatic runtime with its import source (defaulting to `react`, tsc's own default), `react` selects classic, and `preserve`, `react-native` or an absent `jsx` yield `Option.none()`.
164
165
  - Typed failures everywhere: a malformed file is a `TsconfigParseError` carrying its path, a broken chain is a `TsconfigExtendsError` with a `not-found` / `cycle` / `depth` / `empty` reason and the full resolution chain, and IO errors flow through as `PlatformError`. Nothing fails as a defect.
package/index.d.ts CHANGED
@@ -190,6 +190,59 @@ declare namespace CompilerOptions {
190
190
  type Encoded = typeof CompilerOptions.Encoded;
191
191
  }
192
192
  //#endregion
193
+ //#region src/CompilerOptionsFromProgrammatic.d.ts
194
+ /**
195
+ * The untyped record this codec accepts on its encoded side. Exported because
196
+ * it names the codec's encoded type in the public signature; the values stay
197
+ * `unknown` rather than {@link ProgrammaticCompilerOptionsValue} because decode
198
+ * validates them and must be able to receive anything, including the
199
+ * unmappable numeric it exists to reject.
200
+ *
201
+ * @public
202
+ */
203
+ interface ProgrammaticRecord {
204
+ readonly [key: string]: unknown;
205
+ }
206
+ /**
207
+ * A codec between the **programmatic** `compilerOptions` shape TypeScript's own
208
+ * API uses and this package's decoded {@link (CompilerOptions:namespace).Type}.
209
+ *
210
+ * Decoding accepts the numeric-enum spelling (`{ target: ts.ScriptTarget.ES2025 }`),
211
+ * the canonical string spelling, case-varying strings (`"ESNext"`), any mixture of
212
+ * the three in one object, and `lib` entries in any of their three spellings
213
+ * (`"esnext"`, `"lib.esnext.d.ts"`, an absolute path to the lib file) — producing
214
+ * validated {@link (CompilerOptions:namespace).Type} with canonical lowercase enum
215
+ * strings and short-form `lib`. Unknown and dead keys pass through, exactly as
216
+ * {@link (CompilerOptions:variable)} itself allows. Decoding is idempotent on
217
+ * already-canonical input.
218
+ *
219
+ * Encoding is {@link TsEnumCodec.encodeCompilerOptions}: numeric enum values and
220
+ * `lib` in the file-name form (`lib.esnext.d.ts`).
221
+ *
222
+ * @remarks
223
+ * A numeric value with no table entry — a future TypeScript enum member — survives
224
+ * normalization as a number and then **fails decode** with a typed schema issue,
225
+ * rather than passing through. That is deliberate: this is the validating door
226
+ * {@link TsEnumCodec.decodeCompilerOptions} is not, which is why that function's
227
+ * return type stays the wider `Record<string, unknown>`.
228
+ *
229
+ * @example
230
+ * ```ts
231
+ * import { Schema } from "effect";
232
+ * import { CompilerOptionsFromProgrammatic } from "@effected/tsconfig-json";
233
+ *
234
+ * // { target: "es2025", strict: true, lib: ["esnext"] }
235
+ * Schema.decodeUnknownSync(CompilerOptionsFromProgrammatic)({
236
+ * target: 12,
237
+ * strict: true,
238
+ * lib: ["lib.esnext.d.ts"],
239
+ * });
240
+ * ```
241
+ *
242
+ * @public
243
+ */
244
+ declare const CompilerOptionsFromProgrammatic: Schema.Codec<typeof CompilerOptions.Type, ProgrammaticRecord>;
245
+ //#endregion
193
246
  //#region src/JsxConfig.d.ts
194
247
  declare const JsxConfig_base: Schema.Class<JsxConfig, Schema.Struct<{
195
248
  /** The JSX transform runtime: `"automatic"` (`react-jsx` / `react-jsxdev`) or `"classic"` (`react`). */
@@ -1299,5 +1352,5 @@ declare class TsEnumCodec {
1299
1352
  static readonly decodeCompilerOptions: (numeric: Readonly<Record<string, unknown>>) => Record<string, unknown>;
1300
1353
  }
1301
1354
  //#endregion
1302
- export { CompilerOptions, type EnumFamily, FallbackPolling, type FindNearestOptions, Jsx, JsxConfig, Lib, Module, ModuleDetection, ModuleResolution, NewLine, PortableTsconfig, type PortableTsconfigOptions, type ProgrammaticCompilerOptions, type ProgrammaticCompilerOptionsValue, Reference, ResolvedTsconfig, type SyncFileSystem, type SyncPath, Target, TsEnumCodec, TsconfigDiscovery, TsconfigExtendsError, TsconfigJson, TsconfigJsonFromString, TsconfigLoader, TsconfigLoaderSync, type TsconfigLoaderSyncOptions, TsconfigParseError, TypeAcquisition, WatchDirectory, WatchFile, WatchOptions };
1355
+ export { CompilerOptions, CompilerOptionsFromProgrammatic, type EnumFamily, FallbackPolling, type FindNearestOptions, Jsx, JsxConfig, Lib, Module, ModuleDetection, ModuleResolution, NewLine, PortableTsconfig, type PortableTsconfigOptions, type ProgrammaticCompilerOptions, type ProgrammaticCompilerOptionsValue, type ProgrammaticRecord, Reference, ResolvedTsconfig, type SyncFileSystem, type SyncPath, Target, TsEnumCodec, TsconfigDiscovery, TsconfigExtendsError, TsconfigJson, TsconfigJsonFromString, TsconfigLoader, TsconfigLoaderSync, type TsconfigLoaderSyncOptions, TsconfigParseError, TypeAcquisition, WatchDirectory, WatchFile, WatchOptions };
1303
1356
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { CompilerOptions, Jsx, Lib, Module, ModuleDetection, ModuleResolution, NewLine, Target } from "./CompilerOptions.js";
2
+ import { TsEnumCodec } from "./TsEnumCodec.js";
3
+ import { CompilerOptionsFromProgrammatic } from "./CompilerOptionsFromProgrammatic.js";
2
4
  import { JsxConfig } from "./JsxConfig.js";
3
5
  import { PortableTsconfig } from "./PortableTsconfig.js";
4
6
  import { ResolvedTsconfig } from "./ResolvedTsconfig.js";
@@ -6,6 +8,5 @@ import { TsconfigDiscovery } from "./TsconfigDiscovery.js";
6
8
  import { FallbackPolling, Reference, TsconfigJson, TsconfigJsonFromString, TsconfigParseError, TypeAcquisition, WatchDirectory, WatchFile, WatchOptions } from "./TsconfigJson.js";
7
9
  import { TsconfigExtendsError, TsconfigLoader } from "./TsconfigLoader.js";
8
10
  import { TsconfigLoaderSync } from "./TsconfigLoaderSync.js";
9
- import { TsEnumCodec } from "./TsEnumCodec.js";
10
11
 
11
- export { CompilerOptions, FallbackPolling, Jsx, JsxConfig, Lib, Module, ModuleDetection, ModuleResolution, NewLine, PortableTsconfig, Reference, ResolvedTsconfig, Target, TsEnumCodec, TsconfigDiscovery, TsconfigExtendsError, TsconfigJson, TsconfigJsonFromString, TsconfigLoader, TsconfigLoaderSync, TsconfigParseError, TypeAcquisition, WatchDirectory, WatchFile, WatchOptions };
12
+ export { CompilerOptions, CompilerOptionsFromProgrammatic, FallbackPolling, Jsx, JsxConfig, Lib, Module, ModuleDetection, ModuleResolution, NewLine, PortableTsconfig, Reference, ResolvedTsconfig, Target, TsEnumCodec, TsconfigDiscovery, TsconfigExtendsError, TsconfigJson, TsconfigJsonFromString, TsconfigLoader, TsconfigLoaderSync, TsconfigParseError, TypeAcquisition, WatchDirectory, WatchFile, WatchOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/tsconfig-json",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "description": "Composable tsconfig.json handling for Effect: schemas, extends-chain resolution, and config discovery.",
6
6
  "keywords": [
@@ -39,7 +39,7 @@
39
39
  "./package.json": "./package.json"
40
40
  },
41
41
  "peerDependencies": {
42
- "@effected/jsonc": "^0.7.0",
42
+ "@effected/jsonc": "^0.8.1",
43
43
  "@effected/walker": "^0.5.0",
44
44
  "effect": "4.0.0-rc.109"
45
45
  },
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.58.12"
8
+ "packageVersion": "7.59.0"
9
9
  }
10
10
  ]
11
11
  }