@effected/tsconfig-json 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/CompilerOptions.js +345 -0
- package/LICENSE +21 -0
- package/PortableTsconfig.js +138 -0
- package/README.md +96 -0
- package/ResolvedTsconfig.js +245 -0
- package/TsEnumCodec.js +203 -0
- package/TsconfigDiscovery.js +29 -0
- package/TsconfigJson.js +122 -0
- package/TsconfigLoader.js +160 -0
- package/index.d.ts +645 -0
- package/index.js +9 -0
- package/internal/extendsTarget.js +234 -0
- package/package.json +48 -0
- package/tsdoc-metadata.json +11 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { Schema, SchemaTransformation } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/CompilerOptions.ts
|
|
4
|
+
/**
|
|
5
|
+
* Case-insensitive literal-union decode; canonical lowercase encode. Module-
|
|
6
|
+
* internal per the task brief — every exported enum schema below is built
|
|
7
|
+
* from it, but the helper itself is not part of the public surface.
|
|
8
|
+
*/
|
|
9
|
+
const caseInsensitiveLiterals = (literals) => Schema.String.pipe(Schema.decodeTo(Schema.Literals(literals), SchemaTransformation.transform({
|
|
10
|
+
decode: (s) => s.toLowerCase(),
|
|
11
|
+
encode: (s) => s
|
|
12
|
+
})));
|
|
13
|
+
/**
|
|
14
|
+
* `compilerOptions.target` — the ECMAScript target. `es5` is deprecated in TS
|
|
15
|
+
* 6.0; `es3` has no literal (dead per R1.3 — a `target: "es3"` value fails
|
|
16
|
+
* decode against this schema rather than silently passing through, since
|
|
17
|
+
* `target` itself is a live, typed field).
|
|
18
|
+
*
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
const Target = caseInsensitiveLiterals([
|
|
22
|
+
"es5",
|
|
23
|
+
"es6",
|
|
24
|
+
"es2015",
|
|
25
|
+
"es2016",
|
|
26
|
+
"es2017",
|
|
27
|
+
"es2018",
|
|
28
|
+
"es2019",
|
|
29
|
+
"es2020",
|
|
30
|
+
"es2021",
|
|
31
|
+
"es2022",
|
|
32
|
+
"es2023",
|
|
33
|
+
"es2024",
|
|
34
|
+
"es2025",
|
|
35
|
+
"esnext"
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
38
|
+
* `compilerOptions.module` — the module output format. `none`, `amd`, `umd`
|
|
39
|
+
* and `system` are deprecated in TS 6.0.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
const Module = caseInsensitiveLiterals([
|
|
44
|
+
"none",
|
|
45
|
+
"commonjs",
|
|
46
|
+
"amd",
|
|
47
|
+
"umd",
|
|
48
|
+
"system",
|
|
49
|
+
"es6",
|
|
50
|
+
"es2015",
|
|
51
|
+
"es2020",
|
|
52
|
+
"es2022",
|
|
53
|
+
"esnext",
|
|
54
|
+
"node16",
|
|
55
|
+
"node18",
|
|
56
|
+
"node20",
|
|
57
|
+
"nodenext",
|
|
58
|
+
"preserve"
|
|
59
|
+
]);
|
|
60
|
+
/**
|
|
61
|
+
* `compilerOptions.moduleResolution`. `node10`, `node` and `classic` are
|
|
62
|
+
* deprecated in TS 6.0.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
const ModuleResolution = caseInsensitiveLiterals([
|
|
67
|
+
"node10",
|
|
68
|
+
"node",
|
|
69
|
+
"classic",
|
|
70
|
+
"node16",
|
|
71
|
+
"nodenext",
|
|
72
|
+
"bundler"
|
|
73
|
+
]);
|
|
74
|
+
/**
|
|
75
|
+
* `compilerOptions.jsx`. There is no `none` literal — tsc's option map has
|
|
76
|
+
* only these five.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
79
|
+
*/
|
|
80
|
+
const Jsx = caseInsensitiveLiterals([
|
|
81
|
+
"preserve",
|
|
82
|
+
"react-native",
|
|
83
|
+
"react-jsx",
|
|
84
|
+
"react-jsxdev",
|
|
85
|
+
"react"
|
|
86
|
+
]);
|
|
87
|
+
/** `compilerOptions.newLine`. @public */
|
|
88
|
+
const NewLine = caseInsensitiveLiterals(["crlf", "lf"]);
|
|
89
|
+
/** `compilerOptions.moduleDetection`. @public */
|
|
90
|
+
const ModuleDetection = caseInsensitiveLiterals([
|
|
91
|
+
"auto",
|
|
92
|
+
"legacy",
|
|
93
|
+
"force"
|
|
94
|
+
]);
|
|
95
|
+
/**
|
|
96
|
+
* `compilerOptions.lib` member values — the complete TS 6.0.3 set, lowercase
|
|
97
|
+
* canonical, per R1.2.
|
|
98
|
+
*
|
|
99
|
+
* @public
|
|
100
|
+
*/
|
|
101
|
+
const Lib = caseInsensitiveLiterals([
|
|
102
|
+
"es5",
|
|
103
|
+
"es6",
|
|
104
|
+
"es7",
|
|
105
|
+
"es2015",
|
|
106
|
+
"es2016",
|
|
107
|
+
"es2017",
|
|
108
|
+
"es2018",
|
|
109
|
+
"es2019",
|
|
110
|
+
"es2020",
|
|
111
|
+
"es2021",
|
|
112
|
+
"es2022",
|
|
113
|
+
"es2023",
|
|
114
|
+
"es2024",
|
|
115
|
+
"es2025",
|
|
116
|
+
"esnext",
|
|
117
|
+
"dom",
|
|
118
|
+
"dom.iterable",
|
|
119
|
+
"dom.asynciterable",
|
|
120
|
+
"webworker",
|
|
121
|
+
"webworker.importscripts",
|
|
122
|
+
"webworker.iterable",
|
|
123
|
+
"webworker.asynciterable",
|
|
124
|
+
"scripthost",
|
|
125
|
+
"es2015.core",
|
|
126
|
+
"es2015.collection",
|
|
127
|
+
"es2015.generator",
|
|
128
|
+
"es2015.iterable",
|
|
129
|
+
"es2015.promise",
|
|
130
|
+
"es2015.proxy",
|
|
131
|
+
"es2015.reflect",
|
|
132
|
+
"es2015.symbol",
|
|
133
|
+
"es2015.symbol.wellknown",
|
|
134
|
+
"es2016.array.include",
|
|
135
|
+
"es2016.intl",
|
|
136
|
+
"es2017.arraybuffer",
|
|
137
|
+
"es2017.date",
|
|
138
|
+
"es2017.object",
|
|
139
|
+
"es2017.sharedmemory",
|
|
140
|
+
"es2017.string",
|
|
141
|
+
"es2017.intl",
|
|
142
|
+
"es2017.typedarrays",
|
|
143
|
+
"es2018.asyncgenerator",
|
|
144
|
+
"es2018.asynciterable",
|
|
145
|
+
"es2018.intl",
|
|
146
|
+
"es2018.promise",
|
|
147
|
+
"es2018.regexp",
|
|
148
|
+
"es2019.array",
|
|
149
|
+
"es2019.object",
|
|
150
|
+
"es2019.string",
|
|
151
|
+
"es2019.symbol",
|
|
152
|
+
"es2019.intl",
|
|
153
|
+
"es2020.bigint",
|
|
154
|
+
"es2020.date",
|
|
155
|
+
"es2020.promise",
|
|
156
|
+
"es2020.sharedmemory",
|
|
157
|
+
"es2020.string",
|
|
158
|
+
"es2020.symbol.wellknown",
|
|
159
|
+
"es2020.intl",
|
|
160
|
+
"es2020.number",
|
|
161
|
+
"es2021.promise",
|
|
162
|
+
"es2021.string",
|
|
163
|
+
"es2021.weakref",
|
|
164
|
+
"es2021.intl",
|
|
165
|
+
"es2022.array",
|
|
166
|
+
"es2022.error",
|
|
167
|
+
"es2022.intl",
|
|
168
|
+
"es2022.object",
|
|
169
|
+
"es2022.string",
|
|
170
|
+
"es2022.regexp",
|
|
171
|
+
"es2023.array",
|
|
172
|
+
"es2023.collection",
|
|
173
|
+
"es2023.intl",
|
|
174
|
+
"es2024.arraybuffer",
|
|
175
|
+
"es2024.collection",
|
|
176
|
+
"es2024.object",
|
|
177
|
+
"es2024.promise",
|
|
178
|
+
"es2024.regexp",
|
|
179
|
+
"es2024.sharedmemory",
|
|
180
|
+
"es2024.string",
|
|
181
|
+
"es2025.collection",
|
|
182
|
+
"es2025.float16",
|
|
183
|
+
"es2025.intl",
|
|
184
|
+
"es2025.iterator",
|
|
185
|
+
"es2025.promise",
|
|
186
|
+
"es2025.regexp",
|
|
187
|
+
"esnext.asynciterable",
|
|
188
|
+
"esnext.symbol",
|
|
189
|
+
"esnext.bigint",
|
|
190
|
+
"esnext.weakref",
|
|
191
|
+
"esnext.object",
|
|
192
|
+
"esnext.regexp",
|
|
193
|
+
"esnext.string",
|
|
194
|
+
"esnext.float16",
|
|
195
|
+
"esnext.iterator",
|
|
196
|
+
"esnext.promise",
|
|
197
|
+
"esnext.array",
|
|
198
|
+
"esnext.collection",
|
|
199
|
+
"esnext.date",
|
|
200
|
+
"esnext.decorators",
|
|
201
|
+
"esnext.disposable",
|
|
202
|
+
"esnext.error",
|
|
203
|
+
"esnext.intl",
|
|
204
|
+
"esnext.sharedmemory",
|
|
205
|
+
"esnext.temporal",
|
|
206
|
+
"esnext.typedarrays",
|
|
207
|
+
"decorators",
|
|
208
|
+
"decorators.legacy"
|
|
209
|
+
]);
|
|
210
|
+
const IgnoreDeprecations = Schema.Literals(["5.0", "6.0"]);
|
|
211
|
+
/**
|
|
212
|
+
* One `compilerOptions.plugins[]` entry: `name` is required and typed; every
|
|
213
|
+
* other key is preserved verbatim (ts-plugin authors attach arbitrary extra
|
|
214
|
+
* configuration).
|
|
215
|
+
*/
|
|
216
|
+
const PluginEntry = Schema.StructWithRest(Schema.Struct({ name: Schema.String }), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
217
|
+
/**
|
|
218
|
+
* `compilerOptions`, decoded as every R1.3-live boolean, R1.4 string/path/
|
|
219
|
+
* array/record/number, and R1.2 enum field — each `optionalKey` — intersected
|
|
220
|
+
* with a passthrough record so unknown and dead (R1.3 dead-list) keys survive
|
|
221
|
+
* decode and re-encode untouched, per the forward-tolerance constraint.
|
|
222
|
+
*
|
|
223
|
+
* @public
|
|
224
|
+
*/
|
|
225
|
+
const CompilerOptions = Schema.StructWithRest(Schema.Struct({
|
|
226
|
+
target: Schema.optionalKey(Target),
|
|
227
|
+
module: Schema.optionalKey(Module),
|
|
228
|
+
moduleResolution: Schema.optionalKey(ModuleResolution),
|
|
229
|
+
jsx: Schema.optionalKey(Jsx),
|
|
230
|
+
newLine: Schema.optionalKey(NewLine),
|
|
231
|
+
moduleDetection: Schema.optionalKey(ModuleDetection),
|
|
232
|
+
lib: Schema.optionalKey(Schema.Array(Lib)),
|
|
233
|
+
ignoreDeprecations: Schema.optionalKey(IgnoreDeprecations),
|
|
234
|
+
strict: Schema.optionalKey(Schema.Boolean),
|
|
235
|
+
noImplicitAny: Schema.optionalKey(Schema.Boolean),
|
|
236
|
+
strictNullChecks: Schema.optionalKey(Schema.Boolean),
|
|
237
|
+
strictFunctionTypes: Schema.optionalKey(Schema.Boolean),
|
|
238
|
+
strictBindCallApply: Schema.optionalKey(Schema.Boolean),
|
|
239
|
+
strictPropertyInitialization: Schema.optionalKey(Schema.Boolean),
|
|
240
|
+
strictBuiltinIteratorReturn: Schema.optionalKey(Schema.Boolean),
|
|
241
|
+
noImplicitThis: Schema.optionalKey(Schema.Boolean),
|
|
242
|
+
useUnknownInCatchVariables: Schema.optionalKey(Schema.Boolean),
|
|
243
|
+
/** @deprecated Deprecated in TypeScript 6.0 when set to `false`. */
|
|
244
|
+
alwaysStrict: Schema.optionalKey(Schema.Boolean),
|
|
245
|
+
noUnusedLocals: Schema.optionalKey(Schema.Boolean),
|
|
246
|
+
noUnusedParameters: Schema.optionalKey(Schema.Boolean),
|
|
247
|
+
exactOptionalPropertyTypes: Schema.optionalKey(Schema.Boolean),
|
|
248
|
+
noImplicitReturns: Schema.optionalKey(Schema.Boolean),
|
|
249
|
+
noFallthroughCasesInSwitch: Schema.optionalKey(Schema.Boolean),
|
|
250
|
+
noUncheckedIndexedAccess: Schema.optionalKey(Schema.Boolean),
|
|
251
|
+
noImplicitOverride: Schema.optionalKey(Schema.Boolean),
|
|
252
|
+
noPropertyAccessFromIndexSignature: Schema.optionalKey(Schema.Boolean),
|
|
253
|
+
allowUnusedLabels: Schema.optionalKey(Schema.Boolean),
|
|
254
|
+
allowUnreachableCode: Schema.optionalKey(Schema.Boolean),
|
|
255
|
+
noUncheckedSideEffectImports: Schema.optionalKey(Schema.Boolean),
|
|
256
|
+
allowJs: Schema.optionalKey(Schema.Boolean),
|
|
257
|
+
checkJs: Schema.optionalKey(Schema.Boolean),
|
|
258
|
+
resolveJsonModule: Schema.optionalKey(Schema.Boolean),
|
|
259
|
+
allowArbitraryExtensions: Schema.optionalKey(Schema.Boolean),
|
|
260
|
+
allowImportingTsExtensions: Schema.optionalKey(Schema.Boolean),
|
|
261
|
+
rewriteRelativeImportExtensions: Schema.optionalKey(Schema.Boolean),
|
|
262
|
+
resolvePackageJsonExports: Schema.optionalKey(Schema.Boolean),
|
|
263
|
+
resolvePackageJsonImports: Schema.optionalKey(Schema.Boolean),
|
|
264
|
+
/** @deprecated Deprecated in TypeScript 6.0 when set to `false`. */
|
|
265
|
+
allowSyntheticDefaultImports: Schema.optionalKey(Schema.Boolean),
|
|
266
|
+
/** @deprecated Deprecated in TypeScript 6.0 when set to `false`. */
|
|
267
|
+
esModuleInterop: Schema.optionalKey(Schema.Boolean),
|
|
268
|
+
preserveSymlinks: Schema.optionalKey(Schema.Boolean),
|
|
269
|
+
allowUmdGlobalAccess: Schema.optionalKey(Schema.Boolean),
|
|
270
|
+
verbatimModuleSyntax: Schema.optionalKey(Schema.Boolean),
|
|
271
|
+
isolatedModules: Schema.optionalKey(Schema.Boolean),
|
|
272
|
+
isolatedDeclarations: Schema.optionalKey(Schema.Boolean),
|
|
273
|
+
erasableSyntaxOnly: Schema.optionalKey(Schema.Boolean),
|
|
274
|
+
forceConsistentCasingInFileNames: Schema.optionalKey(Schema.Boolean),
|
|
275
|
+
declaration: Schema.optionalKey(Schema.Boolean),
|
|
276
|
+
declarationMap: Schema.optionalKey(Schema.Boolean),
|
|
277
|
+
emitDeclarationOnly: Schema.optionalKey(Schema.Boolean),
|
|
278
|
+
sourceMap: Schema.optionalKey(Schema.Boolean),
|
|
279
|
+
inlineSourceMap: Schema.optionalKey(Schema.Boolean),
|
|
280
|
+
inlineSources: Schema.optionalKey(Schema.Boolean),
|
|
281
|
+
removeComments: Schema.optionalKey(Schema.Boolean),
|
|
282
|
+
importHelpers: Schema.optionalKey(Schema.Boolean),
|
|
283
|
+
/** @deprecated Deprecated in TypeScript 6.0. */
|
|
284
|
+
downlevelIteration: Schema.optionalKey(Schema.Boolean),
|
|
285
|
+
emitBOM: Schema.optionalKey(Schema.Boolean),
|
|
286
|
+
noEmit: Schema.optionalKey(Schema.Boolean),
|
|
287
|
+
noEmitHelpers: Schema.optionalKey(Schema.Boolean),
|
|
288
|
+
noEmitOnError: Schema.optionalKey(Schema.Boolean),
|
|
289
|
+
preserveConstEnums: Schema.optionalKey(Schema.Boolean),
|
|
290
|
+
stripInternal: Schema.optionalKey(Schema.Boolean),
|
|
291
|
+
experimentalDecorators: Schema.optionalKey(Schema.Boolean),
|
|
292
|
+
emitDecoratorMetadata: Schema.optionalKey(Schema.Boolean),
|
|
293
|
+
useDefineForClassFields: Schema.optionalKey(Schema.Boolean),
|
|
294
|
+
noCheck: Schema.optionalKey(Schema.Boolean),
|
|
295
|
+
composite: Schema.optionalKey(Schema.Boolean),
|
|
296
|
+
incremental: Schema.optionalKey(Schema.Boolean),
|
|
297
|
+
disableSourceOfProjectReferenceRedirect: Schema.optionalKey(Schema.Boolean),
|
|
298
|
+
disableSolutionSearching: Schema.optionalKey(Schema.Boolean),
|
|
299
|
+
disableReferencedProjectLoad: Schema.optionalKey(Schema.Boolean),
|
|
300
|
+
assumeChangesOnlyAffectDirectDependencies: Schema.optionalKey(Schema.Boolean),
|
|
301
|
+
noErrorTruncation: Schema.optionalKey(Schema.Boolean),
|
|
302
|
+
noLib: Schema.optionalKey(Schema.Boolean),
|
|
303
|
+
noResolve: Schema.optionalKey(Schema.Boolean),
|
|
304
|
+
skipDefaultLibCheck: Schema.optionalKey(Schema.Boolean),
|
|
305
|
+
skipLibCheck: Schema.optionalKey(Schema.Boolean),
|
|
306
|
+
diagnostics: Schema.optionalKey(Schema.Boolean),
|
|
307
|
+
extendedDiagnostics: Schema.optionalKey(Schema.Boolean),
|
|
308
|
+
listFiles: Schema.optionalKey(Schema.Boolean),
|
|
309
|
+
listFilesOnly: Schema.optionalKey(Schema.Boolean),
|
|
310
|
+
listEmittedFiles: Schema.optionalKey(Schema.Boolean),
|
|
311
|
+
explainFiles: Schema.optionalKey(Schema.Boolean),
|
|
312
|
+
traceResolution: Schema.optionalKey(Schema.Boolean),
|
|
313
|
+
preserveWatchOutput: Schema.optionalKey(Schema.Boolean),
|
|
314
|
+
pretty: Schema.optionalKey(Schema.Boolean),
|
|
315
|
+
disableSizeLimit: Schema.optionalKey(Schema.Boolean),
|
|
316
|
+
libReplacement: Schema.optionalKey(Schema.Boolean),
|
|
317
|
+
stableTypeOrdering: Schema.optionalKey(Schema.Boolean),
|
|
318
|
+
/** @deprecated Deprecated in TypeScript 6.0. */
|
|
319
|
+
outFile: Schema.optionalKey(Schema.String),
|
|
320
|
+
outDir: Schema.optionalKey(Schema.String),
|
|
321
|
+
rootDir: Schema.optionalKey(Schema.String),
|
|
322
|
+
declarationDir: Schema.optionalKey(Schema.String),
|
|
323
|
+
sourceRoot: Schema.optionalKey(Schema.String),
|
|
324
|
+
mapRoot: Schema.optionalKey(Schema.String),
|
|
325
|
+
tsBuildInfoFile: Schema.optionalKey(Schema.String),
|
|
326
|
+
/** @deprecated Deprecated in TypeScript 6.0. */
|
|
327
|
+
baseUrl: Schema.optionalKey(Schema.String),
|
|
328
|
+
generateCpuProfile: Schema.optionalKey(Schema.String),
|
|
329
|
+
generateTrace: Schema.optionalKey(Schema.String),
|
|
330
|
+
typeRoots: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
331
|
+
rootDirs: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
332
|
+
jsxFactory: Schema.optionalKey(Schema.String),
|
|
333
|
+
jsxFragmentFactory: Schema.optionalKey(Schema.String),
|
|
334
|
+
jsxImportSource: Schema.optionalKey(Schema.String),
|
|
335
|
+
reactNamespace: Schema.optionalKey(Schema.String),
|
|
336
|
+
types: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
337
|
+
customConditions: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
338
|
+
moduleSuffixes: Schema.optionalKey(Schema.Array(Schema.String)),
|
|
339
|
+
paths: Schema.optionalKey(Schema.Record(Schema.String, Schema.Array(Schema.String))),
|
|
340
|
+
plugins: Schema.optionalKey(Schema.Array(PluginEntry)),
|
|
341
|
+
maxNodeModuleJsDepth: Schema.optionalKey(Schema.Number)
|
|
342
|
+
}), [Schema.Record(Schema.String, Schema.Unknown)]);
|
|
343
|
+
|
|
344
|
+
//#endregion
|
|
345
|
+
export { CompilerOptions, Jsx, Lib, Module, ModuleDetection, ModuleResolution, NewLine, Target };
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C. Spencer Beggs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
//#region src/PortableTsconfig.ts
|
|
2
|
+
const TSCONFIG_SCHEMA_URL = "https://json.schemastore.org/tsconfig";
|
|
3
|
+
/**
|
|
4
|
+
* Boolean `compilerOptions` preserved in a {@link (PortableTsconfig:interface)}: the
|
|
5
|
+
* strict family, module/interop semantics, checking-depth and lib controls,
|
|
6
|
+
* and language-feature switches — every one type-checking behavior, none of
|
|
7
|
+
* them naming a path or producing a build artifact.
|
|
8
|
+
*/
|
|
9
|
+
const PRESERVED_BOOLEAN_OPTIONS = [
|
|
10
|
+
"strict",
|
|
11
|
+
"noImplicitAny",
|
|
12
|
+
"strictNullChecks",
|
|
13
|
+
"strictFunctionTypes",
|
|
14
|
+
"strictBindCallApply",
|
|
15
|
+
"strictPropertyInitialization",
|
|
16
|
+
"strictBuiltinIteratorReturn",
|
|
17
|
+
"noImplicitThis",
|
|
18
|
+
"useUnknownInCatchVariables",
|
|
19
|
+
"alwaysStrict",
|
|
20
|
+
"noUnusedLocals",
|
|
21
|
+
"noUnusedParameters",
|
|
22
|
+
"exactOptionalPropertyTypes",
|
|
23
|
+
"noImplicitReturns",
|
|
24
|
+
"noFallthroughCasesInSwitch",
|
|
25
|
+
"noUncheckedIndexedAccess",
|
|
26
|
+
"noImplicitOverride",
|
|
27
|
+
"noPropertyAccessFromIndexSignature",
|
|
28
|
+
"allowUnusedLabels",
|
|
29
|
+
"allowUnreachableCode",
|
|
30
|
+
"noUncheckedSideEffectImports",
|
|
31
|
+
"allowJs",
|
|
32
|
+
"checkJs",
|
|
33
|
+
"resolveJsonModule",
|
|
34
|
+
"allowArbitraryExtensions",
|
|
35
|
+
"allowImportingTsExtensions",
|
|
36
|
+
"rewriteRelativeImportExtensions",
|
|
37
|
+
"resolvePackageJsonExports",
|
|
38
|
+
"resolvePackageJsonImports",
|
|
39
|
+
"allowSyntheticDefaultImports",
|
|
40
|
+
"esModuleInterop",
|
|
41
|
+
"preserveSymlinks",
|
|
42
|
+
"allowUmdGlobalAccess",
|
|
43
|
+
"verbatimModuleSyntax",
|
|
44
|
+
"isolatedModules",
|
|
45
|
+
"isolatedDeclarations",
|
|
46
|
+
"erasableSyntaxOnly",
|
|
47
|
+
"forceConsistentCasingInFileNames",
|
|
48
|
+
"useDefineForClassFields",
|
|
49
|
+
"noLib",
|
|
50
|
+
"skipDefaultLibCheck",
|
|
51
|
+
"skipLibCheck",
|
|
52
|
+
"noCheck",
|
|
53
|
+
"experimentalDecorators",
|
|
54
|
+
"emitDecoratorMetadata",
|
|
55
|
+
"importHelpers",
|
|
56
|
+
"downlevelIteration",
|
|
57
|
+
"preserveConstEnums"
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Enum-valued `compilerOptions` preserved in a {@link (PortableTsconfig:interface)}: the
|
|
61
|
+
* target/module/moduleResolution/jsx/lib family plus `moduleDetection` and
|
|
62
|
+
* `ignoreDeprecations`. `newLine` is deliberately excluded — pure emit
|
|
63
|
+
* formatting, no bearing on type-checking behavior.
|
|
64
|
+
*/
|
|
65
|
+
const PRESERVED_ENUM_OPTIONS = [
|
|
66
|
+
"target",
|
|
67
|
+
"module",
|
|
68
|
+
"moduleResolution",
|
|
69
|
+
"jsx",
|
|
70
|
+
"moduleDetection",
|
|
71
|
+
"lib",
|
|
72
|
+
"ignoreDeprecations"
|
|
73
|
+
];
|
|
74
|
+
/**
|
|
75
|
+
* Plain-string `compilerOptions` preserved in a {@link (PortableTsconfig:interface)}: the
|
|
76
|
+
* jsx factory/namespace family. None of these name a filesystem path.
|
|
77
|
+
*/
|
|
78
|
+
const PRESERVED_STRING_OPTIONS = [
|
|
79
|
+
"jsxFactory",
|
|
80
|
+
"jsxFragmentFactory",
|
|
81
|
+
"jsxImportSource",
|
|
82
|
+
"reactNamespace"
|
|
83
|
+
];
|
|
84
|
+
const PRESERVED_OPTIONS = [
|
|
85
|
+
...PRESERVED_BOOLEAN_OPTIONS,
|
|
86
|
+
...PRESERVED_ENUM_OPTIONS,
|
|
87
|
+
...PRESERVED_STRING_OPTIONS
|
|
88
|
+
];
|
|
89
|
+
/**
|
|
90
|
+
* A `ResolvedTsconfig` carries a string `configPath`, an array `extendedPaths`
|
|
91
|
+
* AND an object `compilerOptions`; a bare `CompilerOptions.Type` never carries
|
|
92
|
+
* all three. Every conjunct is required — `CompilerOptions.Type`'s passthrough
|
|
93
|
+
* index signature tolerates any unrecognized key, so a hostile config file can
|
|
94
|
+
* plant a string `configPath` and even an array `extendedPaths` as passthrough
|
|
95
|
+
* keys on a bare options bag (reachable from forward tolerance). The
|
|
96
|
+
* `compilerOptions` conjunct is what makes the discrimination TOTAL: `make`
|
|
97
|
+
* only branches into `input.compilerOptions` when that value is a non-null
|
|
98
|
+
* object it can safely index, so no crafted bag can steer the filter into a
|
|
99
|
+
* `TypeError` at the `source[key]` loop below. (A bag that fakes all three is
|
|
100
|
+
* then read exactly like a `ResolvedTsconfig` — its `compilerOptions` object
|
|
101
|
+
* is filtered, which is the safe outcome.)
|
|
102
|
+
*/
|
|
103
|
+
const isResolvedTsconfig = (input) => typeof input.configPath === "string" && Array.isArray(input.extendedPaths) && typeof input.compilerOptions === "object" && input.compilerOptions !== null;
|
|
104
|
+
/**
|
|
105
|
+
* Project a {@link (ResolvedTsconfig:interface)} or a bare
|
|
106
|
+
* `CompilerOptions.Type` down to a {@link (PortableTsconfig:interface)}: copy
|
|
107
|
+
* only the allow-listed type-semantics options, force `composite: false` and
|
|
108
|
+
* `noEmit: true` regardless of what the source declared, and stamp `$schema`.
|
|
109
|
+
* Every other key — including every unknown passthrough key the source
|
|
110
|
+
* preserved for forward tolerance — is dropped; this is an allow-list, not a
|
|
111
|
+
* deny-list, so an option this package does not yet classify never leaks onto
|
|
112
|
+
* the portable shape by accident.
|
|
113
|
+
*/
|
|
114
|
+
const make = (input) => {
|
|
115
|
+
const source = isResolvedTsconfig(input) ? input.compilerOptions : input;
|
|
116
|
+
const compilerOptions = {};
|
|
117
|
+
for (const key of PRESERVED_OPTIONS) {
|
|
118
|
+
const value = source[key];
|
|
119
|
+
if (value !== void 0) compilerOptions[key] = value;
|
|
120
|
+
}
|
|
121
|
+
compilerOptions.composite = false;
|
|
122
|
+
compilerOptions.noEmit = true;
|
|
123
|
+
return {
|
|
124
|
+
$schema: TSCONFIG_SCHEMA_URL,
|
|
125
|
+
compilerOptions
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* The portable-tsconfig filter: {@link (PortableTsconfig:variable).make}
|
|
130
|
+
* narrows a resolved or bare compiler-options object to the allow-listed,
|
|
131
|
+
* machine-independent subset described on {@link (PortableTsconfig:interface)}.
|
|
132
|
+
*
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
const PortableTsconfig = { make };
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
export { PortableTsconfig };
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# @effected/tsconfig-json
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@effected/tsconfig-json)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
Composable tsconfig.json handling for Effect: document and compiler-option schemas, `extends`-chain resolution with tsc's own merge semantics, nearest-config discovery and a portable-config filter for virtual TypeScript environments. Every parse is JSONC — comments and trailing commas are legal everywhere, exactly as tsc treats them — and options the schemas do not know pass through decode and encode untouched instead of being dropped.
|
|
9
|
+
|
|
10
|
+
> **Pre-release.** This package is part of the `@effected/*` kit, in pre-`1.0.0`
|
|
11
|
+
> development against a single pinned Effect v4 beta. Packages graduate to
|
|
12
|
+
> `1.0.0` once Effect `4.0.0` ships. To hold your own `effect` versions at
|
|
13
|
+
> exactly the ones the kit is built and tested against, install
|
|
14
|
+
> [`@effected/pnpm-plugin-effect`](https://www.npmjs.com/package/@effected/pnpm-plugin-effect).
|
|
15
|
+
>
|
|
16
|
+
> **Stability: unstable.** This package's API surface is not yet considered
|
|
17
|
+
> complete and may change across `0.x` releases. Pin an exact version — even a
|
|
18
|
+
> package marked *stable* before `1.0.0` can introduce a breaking change by
|
|
19
|
+
> accident, and an exact pin turns that into a type-check error rather than a
|
|
20
|
+
> runtime surprise. Full policy: [release strategy](https://github.com/spencerbeggs/effected#release-strategy).
|
|
21
|
+
|
|
22
|
+
## Why @effected/tsconfig-json
|
|
23
|
+
|
|
24
|
+
Reading a tsconfig.json correctly means reproducing what tsc does, and what tsc does is more than `JSON.parse` plus `Object.assign`. An `extends` target resolves like a module: a bare specifier walks ancestor `node_modules` directories, a package's `exports` map can redirect or block it, and a `tsconfig` field in its manifest can point somewhere else entirely. Merging the chain is per-field, path options absolutize against the config that declared them rather than the one you loaded, and `${configDir}` substitutes once at the end against the top config's directory. These rules were extracted from the TypeScript compiler's source and encoded here as data-driven tests, so the resolution you get is the resolution tsc computes.
|
|
25
|
+
|
|
26
|
+
The package does all of this without importing `typescript`, not even as a type. It works at the string level — `"target": "es2023"` stays a string through schema, merge and discovery — and the version-coupled numeric enum mappings live in `TsEnumCodec` as plain data tables, so converting to the numeric shape a real compiler expects is an explicit final step rather than a dependency you carry everywhere. Malformed input always fails through a typed error channel, and the recursive `extends` walk carries cycle and depth guards, because a config file is untrusted input.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @effected/tsconfig-json @effected/jsonc @effected/walker effect
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pnpm add @effected/tsconfig-json @effected/jsonc @effected/walker effect
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Requires Node.js >=24.11.0. `effect` v4, `@effected/jsonc` and `@effected/walker` are peer dependencies; there are no runtime dependencies of its own.
|
|
39
|
+
|
|
40
|
+
All IO goes through `FileSystem` and `Path` from `effect` core, not a platform package, so a consumer provides them once at the edge (`@effect/platform-node` on Node, `@effect/platform-bun` on Bun) and a test provides `Path.layer` and `FileSystem.layerNoop` straight from core with nothing else installed.
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
Resolve a config and its full `extends` chain:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { TsconfigLoader } from "@effected/tsconfig-json";
|
|
48
|
+
import { NodeFileSystem, NodePath } from "@effect/platform-node";
|
|
49
|
+
import { Effect, Layer } from "effect";
|
|
50
|
+
|
|
51
|
+
const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer);
|
|
52
|
+
|
|
53
|
+
const resolved = await Effect.runPromise(TsconfigLoader.resolve("./tsconfig.json").pipe(Effect.provide(PlatformLive)));
|
|
54
|
+
|
|
55
|
+
console.log(resolved.extendedPaths);
|
|
56
|
+
// every config on the extends chain as normalized absolute paths, base-most first and your own config last
|
|
57
|
+
console.log(resolved.compilerOptions);
|
|
58
|
+
// the merged options after folding the whole chain — later configs win per field, paths replaced wholesale
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Find the nearest config first when you only have a starting directory:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { TsconfigDiscovery } from "@effected/tsconfig-json";
|
|
65
|
+
import { Effect, Option } from "effect";
|
|
66
|
+
|
|
67
|
+
const nearest = TsconfigDiscovery.findNearest(process.cwd());
|
|
68
|
+
// Effect<Option<string>, never, FileSystem | Path> — absence is Option.none(), never an error
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Hand the result to a real compiler by encoding the enum families to their numeric form, or narrow it to the portable subset a virtual TypeScript environment can safely reuse:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { PortableTsconfig, TsEnumCodec } from "@effected/tsconfig-json";
|
|
75
|
+
|
|
76
|
+
console.log(TsEnumCodec.encodeCompilerOptions({ target: "es2023", strict: true, lib: ["esnext"] }));
|
|
77
|
+
// { target: 10, strict: true, lib: [ 'lib.esnext.d.ts' ] }
|
|
78
|
+
|
|
79
|
+
console.log(PortableTsconfig.make(resolved).compilerOptions.noEmit);
|
|
80
|
+
// true — always forced, whatever the source config declared
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Features
|
|
84
|
+
|
|
85
|
+
- `TsconfigJson` / `TsconfigJsonFromString` — the document schema and its JSONC string codec. Comments and trailing commas are legal in every parse; there is no JSON-strict path.
|
|
86
|
+
- `CompilerOptions` — string-level schemas for `compilerOptions`: enum values decode case-insensitively and encode to canonical lowercase, and unknown or removed options survive a round trip as passthrough.
|
|
87
|
+
- `TsconfigLoader.load` / `TsconfigLoader.resolve` — read and decode one config, or resolve its full `extends` chain depth-first with per-branch cycle stacks (diamond chains are legal), a depth guard and tsc's target resolution for relative, rooted and bare-specifier targets including `exports` maps.
|
|
88
|
+
- `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.
|
|
89
|
+
- `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.
|
|
90
|
+
- `TsEnumCodec` — the string↔numeric enum tables as plain data with zero `typescript` imports. `encodeCompilerOptions` produces the numeric shape `ts.CompilerOptions` expects, with `lib` entries in the file-name form the compiler resolves verbatim; `decodeCompilerOptions` reverses it.
|
|
91
|
+
- `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.
|
|
92
|
+
- 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.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
[MIT](LICENSE)
|