ttsc 0.24.0 → 0.25.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/LICENSE +21 -21
- package/README.md +66 -63
- package/cmd/ttscgraph/serve_answers_a_project_that_implements_its_own_ambient_declaration_test.go +69 -0
- package/go.sum +22 -22
- package/internal/graph/assigned_implementations_of_declaration_file_symbols_leave_no_edge_source_test.go +84 -0
- package/internal/graph/assigned_implementations_of_node_modules_symbols_leave_no_edge_source_test.go +85 -0
- package/internal/graph/edges.go +15 -1
- package/internal/lspserver/lsp_native_plugin_source.go +5 -6
- package/internal/lspserver/lsp_native_resident.go +17 -24
- package/lib/TtscService.d.ts +1 -6
- package/lib/TtscService.js +2 -2
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.d.ts +26 -0
- package/lib/compiler/internal/captureProcessOutput.js +92 -0
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -0
- package/lib/compiler/internal/compileProjectInMemory.js +4 -7
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/residentCheckProcess.d.ts +0 -2
- package/lib/compiler/internal/residentCheckProcess.js +1 -15
- package/lib/compiler/internal/residentCheckProcess.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +0 -6
- package/lib/compiler/internal/residentTransformProcess.js +0 -25
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +14 -6
- package/lib/compiler/internal/spawnNative.js +32 -11
- package/lib/compiler/internal/spawnNative.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +1 -5
- package/lib/compiler/internal/startResidentTransform.js +1 -3
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.d.ts +2 -1
- package/lib/launcher/internal/prepareExecution.js +269 -23
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/registerRuntimeHooks.js +6 -1
- package/lib/launcher/internal/registerRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +4 -9
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -55
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +25 -2
- package/lib/launcher/internal/runtimeHooks.js +344 -69
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.js +31 -3
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/descriptorProcessFailure.d.ts +23 -19
- package/lib/plugin/internal/descriptorProcessFailure.js +40 -51
- package/lib/plugin/internal/descriptorProcessFailure.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +12 -0
- package/lib/plugin/internal/loadProjectPlugins.js +77 -28
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/package.json +8 -8
- package/shim/ast/extra-shim.json +4 -4
- package/shim/ast/go.mod +14 -14
- package/shim/ast/go.sum +20 -20
- package/shim/astnav/go.mod +14 -14
- package/shim/astnav/go.sum +20 -20
- package/shim/bundled/extra-shim.json +1 -1
- package/shim/bundled/go.mod +5 -5
- package/shim/checker/extra-shim.json +31 -31
- package/shim/checker/go.mod +5 -5
- package/shim/compiler/extra-shim.json +4 -4
- package/shim/compiler/go.mod +14 -14
- package/shim/compiler/go.sum +22 -22
- package/shim/core/extra-shim.json +4 -4
- package/shim/core/go.mod +5 -5
- package/shim/diagnosticwriter/go.mod +5 -5
- package/shim/lsp/extra-shim.json +4 -4
- package/shim/lsp/go.mod +16 -16
- package/shim/lsp/go.sum +26 -26
- package/shim/parser/extra-shim.json +4 -4
- package/shim/parser/go.mod +5 -5
- package/shim/printer/go.mod +14 -14
- package/shim/printer/go.sum +22 -22
- package/shim/scanner/extra-shim.json +4 -4
- package/shim/scanner/go.mod +5 -5
- package/shim/transformers/go.mod +14 -14
- package/shim/transformers/go.sum +20 -20
- package/shim/tsoptions/extra-shim.json +4 -4
- package/shim/tsoptions/go.mod +5 -5
- package/shim/tspath/extra-shim.json +4 -4
- package/shim/tspath/go.mod +5 -5
- package/shim/vfs/cachedvfs/extra-shim.json +4 -4
- package/shim/vfs/cachedvfs/go.mod +5 -5
- package/shim/vfs/extra-shim.json +4 -4
- package/shim/vfs/go.mod +5 -5
- package/shim/vfs/osvfs/extra-shim.json +4 -4
- package/shim/vfs/osvfs/go.mod +5 -5
- package/src/TtscService.ts +8 -20
- package/src/compiler/internal/captureProcessOutput.ts +96 -0
- package/src/compiler/internal/compileProjectInMemory.ts +7 -7
- package/src/compiler/internal/residentCheckProcess.ts +1 -21
- package/src/compiler/internal/residentTransformProcess.ts +0 -42
- package/src/compiler/internal/spawnNative.ts +39 -18
- package/src/compiler/internal/startResidentTransform.ts +1 -12
- package/src/launcher/internal/prepareExecution.ts +297 -26
- package/src/launcher/internal/registerRuntimeHooks.ts +11 -2
- package/src/launcher/internal/runTtscserver.ts +4 -9
- package/src/launcher/internal/runTtsx.ts +2 -78
- package/src/launcher/internal/runtimeHooks.ts +385 -82
- package/src/plugin/internal/buildSourcePlugin.ts +36 -3
- package/src/plugin/internal/descriptorProcessFailure.ts +34 -65
- package/src/plugin/internal/loadProjectPlugins.ts +78 -39
- package/tools/gen_shims/go.mod +14 -14
- package/tools/gen_shims/go.sum +12 -12
- package/tools/shim_audit/go.mod +10 -10
- package/tools/shim_audit/go.sum +8 -8
|
@@ -8,12 +8,14 @@ exports.checkNodeRuntimeSupport = checkNodeRuntimeSupport;
|
|
|
8
8
|
exports.installRuntimeHooks = installRuntimeHooks;
|
|
9
9
|
exports.entryModuleFormat = entryModuleFormat;
|
|
10
10
|
exports.restoreStrippedNodeBuiltinScheme = restoreStrippedNodeBuiltinScheme;
|
|
11
|
+
exports.projectModuleOptions = projectModuleOptions;
|
|
11
12
|
exports.isWithin = isWithin;
|
|
12
13
|
exports.readDependencyCache = readDependencyCache;
|
|
13
14
|
exports.acquireDependencyBuildLock = acquireDependencyBuildLock;
|
|
14
15
|
exports.releaseDependencyBuildLock = releaseDependencyBuildLock;
|
|
15
16
|
exports.reclaimDependencyBuildLock = reclaimDependencyBuildLock;
|
|
16
17
|
exports.inspectDependencyBuildLock = inspectDependencyBuildLock;
|
|
18
|
+
exports.realPath = realPath;
|
|
17
19
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
18
20
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
19
21
|
const node_module_1 = require("node:module");
|
|
@@ -197,12 +199,13 @@ function installCommonJsHook() {
|
|
|
197
199
|
}
|
|
198
200
|
/**
|
|
199
201
|
* The module format of the entry source file, derived from the entry project's
|
|
200
|
-
*
|
|
202
|
+
* compiler options (via the runtime manifest) the same way the served files are
|
|
201
203
|
* classified. The bootstrap uses it to load the entry through a CommonJS
|
|
202
204
|
* `require` or an ESM `import`.
|
|
203
205
|
*/
|
|
204
206
|
function entryModuleFormat(entryFile) {
|
|
205
|
-
|
|
207
|
+
const m = manifest();
|
|
208
|
+
return moduleFormat(entryFile, m === null ? null : (m.moduleOptions ?? {})) === "module"
|
|
206
209
|
? "module"
|
|
207
210
|
: "commonjs";
|
|
208
211
|
}
|
|
@@ -261,7 +264,7 @@ function load(url, context, nextLoad) {
|
|
|
261
264
|
}
|
|
262
265
|
function resolveRuntimeSource(filename, url = (0, node_url_1.pathToFileURL)(filename).href) {
|
|
263
266
|
const served = resolveServedSource(filename, url);
|
|
264
|
-
const format = moduleFormat(filename, served.
|
|
267
|
+
const format = moduleFormat(filename, served.moduleOptions);
|
|
265
268
|
return {
|
|
266
269
|
format,
|
|
267
270
|
source: format === "commonjs" && commonJsNamedInteropUrls.has(url)
|
|
@@ -275,6 +278,17 @@ function rememberCommonJsNamedInterop(result, context) {
|
|
|
275
278
|
}
|
|
276
279
|
return result;
|
|
277
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Whether a CommonJS-classified TypeScript source reached from an ESM parent
|
|
283
|
+
* needs its nested `export *` names exposed.
|
|
284
|
+
*
|
|
285
|
+
* Only the parent side is decided here. The child's own format is re-checked
|
|
286
|
+
* authoritatively in `resolveRuntimeSource` against the format its served
|
|
287
|
+
* source actually carries, so this predicate deliberately does not repeat that
|
|
288
|
+
* check: doing so would need the child's owning project, which is not known
|
|
289
|
+
* until the source is served, and an answer guessed from the nearest tsconfig
|
|
290
|
+
* silently under-exposes a file that tsconfig does not compile.
|
|
291
|
+
*/
|
|
278
292
|
function shouldExposeCommonJsNamedExports(url, parentURL) {
|
|
279
293
|
if (parentURL === undefined ||
|
|
280
294
|
!url.startsWith("file:") ||
|
|
@@ -282,45 +296,67 @@ function shouldExposeCommonJsNamedExports(url, parentURL) {
|
|
|
282
296
|
return false;
|
|
283
297
|
}
|
|
284
298
|
const parentFile = (0, node_url_1.fileURLToPath)(parentURL);
|
|
285
|
-
if (moduleFormat(parentFile,
|
|
299
|
+
if (moduleFormat(parentFile, owningModuleOptions(parentFile)) !== "module") {
|
|
286
300
|
return false;
|
|
287
301
|
}
|
|
288
|
-
|
|
289
|
-
return (isTypeScriptSource(filename) &&
|
|
290
|
-
moduleFormat(filename, moduleOptionForSource(filename)) === "commonjs");
|
|
302
|
+
return isTypeScriptSource((0, node_url_1.fileURLToPath)(url));
|
|
291
303
|
}
|
|
292
|
-
|
|
304
|
+
/**
|
|
305
|
+
* The emit-deciding compiler options of the project that owns `filename`, or
|
|
306
|
+
* `null` when none does.
|
|
307
|
+
*
|
|
308
|
+
* The entry project owns a file only when it actually emitted it. Testing
|
|
309
|
+
* `isWithin(rootDir)` alone would claim every file under a wide `rootDir` —
|
|
310
|
+
* including the volume-root `rootDir` a config-loader project uses — and hand
|
|
311
|
+
* them the entry project's options even though the dependency or orphan lane is
|
|
312
|
+
* what serves them.
|
|
313
|
+
*/
|
|
314
|
+
function owningModuleOptions(filename) {
|
|
293
315
|
if (!isTypeScriptSource(filename)) {
|
|
294
|
-
return
|
|
316
|
+
return null;
|
|
295
317
|
}
|
|
296
318
|
const real = realPath(filename);
|
|
297
319
|
const m = manifest();
|
|
298
|
-
if (m !== null &&
|
|
299
|
-
return m.
|
|
320
|
+
if (m !== null && entryEmitPath(m, real) !== null) {
|
|
321
|
+
return m.moduleOptions ?? {};
|
|
300
322
|
}
|
|
301
323
|
const tsconfig = nearestTsconfig(real);
|
|
302
324
|
if (tsconfig === null) {
|
|
303
|
-
return
|
|
325
|
+
return null;
|
|
304
326
|
}
|
|
305
|
-
|
|
306
|
-
|
|
327
|
+
const cached = moduleOptionsCache.get(tsconfig);
|
|
328
|
+
if (cached !== undefined) {
|
|
329
|
+
return cached;
|
|
307
330
|
}
|
|
308
|
-
let
|
|
331
|
+
let options = {};
|
|
309
332
|
try {
|
|
310
333
|
const project = (0, readProjectConfig_1.readProjectConfig)({
|
|
311
334
|
cwd: node_path_1.default.dirname(tsconfig),
|
|
312
335
|
tsconfig,
|
|
313
336
|
});
|
|
314
|
-
|
|
315
|
-
typeof project.compilerOptions.module === "string"
|
|
316
|
-
? project.compilerOptions.module
|
|
317
|
-
: undefined;
|
|
337
|
+
options = projectModuleOptions(project.compilerOptions);
|
|
318
338
|
}
|
|
319
339
|
catch {
|
|
320
|
-
|
|
340
|
+
// The owning project cannot be read, so nothing is known about the format
|
|
341
|
+
// it would have emitted. That is the same state as having no project at
|
|
342
|
+
// all, and it is what the dependency lane will conclude too when its build
|
|
343
|
+
// fails and the file falls through to the orphan type-strip.
|
|
344
|
+
moduleOptionsCache.set(tsconfig, null);
|
|
345
|
+
return null;
|
|
321
346
|
}
|
|
322
|
-
|
|
323
|
-
return
|
|
347
|
+
moduleOptionsCache.set(tsconfig, options);
|
|
348
|
+
return options;
|
|
349
|
+
}
|
|
350
|
+
/** Narrow a resolved project's compiler options to the emit-format pair. */
|
|
351
|
+
function projectModuleOptions(compilerOptions) {
|
|
352
|
+
return {
|
|
353
|
+
...(typeof compilerOptions.module === "string"
|
|
354
|
+
? { module: compilerOptions.module }
|
|
355
|
+
: {}),
|
|
356
|
+
...(typeof compilerOptions.target === "string"
|
|
357
|
+
? { target: compilerOptions.target }
|
|
358
|
+
: {}),
|
|
359
|
+
};
|
|
324
360
|
}
|
|
325
361
|
/**
|
|
326
362
|
* Resolve the JavaScript to run for a TypeScript source file, in priority
|
|
@@ -333,17 +369,14 @@ function resolveServedSource(filename, url = (0, node_url_1.pathToFileURL)(filen
|
|
|
333
369
|
const real = realPath(filename);
|
|
334
370
|
const served = serveEntryEmit(real);
|
|
335
371
|
if (served !== null) {
|
|
336
|
-
return withInlineSourceMap(
|
|
337
|
-
moduleOption: manifest()?.moduleOption,
|
|
338
|
-
...served,
|
|
339
|
-
});
|
|
372
|
+
return withInlineSourceMap(served);
|
|
340
373
|
}
|
|
341
374
|
const built = serveDependencyEmit(real);
|
|
342
375
|
if (built !== null) {
|
|
343
376
|
return withInlineSourceMap(built);
|
|
344
377
|
}
|
|
345
378
|
return {
|
|
346
|
-
|
|
379
|
+
moduleOptions: null,
|
|
347
380
|
sourceFile: filename,
|
|
348
381
|
source: transformOrphanSource(filename, url),
|
|
349
382
|
};
|
|
@@ -376,7 +409,7 @@ function withInlineSourceMap(served) {
|
|
|
376
409
|
* owning project; an ESM-format orphan keeps the fast in-process strip.
|
|
377
410
|
*/
|
|
378
411
|
function transformOrphanSource(filename, url) {
|
|
379
|
-
if (moduleFormat(filename,
|
|
412
|
+
if (moduleFormat(filename, null) === "commonjs") {
|
|
380
413
|
const lowered = emitOrphanAsCommonJs(filename);
|
|
381
414
|
if (lowered !== null) {
|
|
382
415
|
return lowered;
|
|
@@ -908,23 +941,49 @@ function serveEntryEmit(real) {
|
|
|
908
941
|
if (m === null) {
|
|
909
942
|
return null;
|
|
910
943
|
}
|
|
911
|
-
|
|
912
|
-
return null;
|
|
913
|
-
}
|
|
914
|
-
const emitted = (0, resolveEmittedJavaScript_1.resolveEmittedJavaScript)({
|
|
915
|
-
emittedFiles: m.emittedFiles,
|
|
916
|
-
outDir: m.emitDir,
|
|
917
|
-
projectRoot: m.rootDir,
|
|
918
|
-
sourceFile: real,
|
|
919
|
-
});
|
|
944
|
+
const emitted = entryEmitPath(m, real);
|
|
920
945
|
if (emitted === null) {
|
|
921
946
|
return null;
|
|
922
947
|
}
|
|
923
948
|
const source = readFileOrNull(emitted);
|
|
924
949
|
return source === null
|
|
925
950
|
? null
|
|
926
|
-
: {
|
|
951
|
+
: {
|
|
952
|
+
emittedFile: emitted,
|
|
953
|
+
moduleOptions: m.moduleOptions ?? {},
|
|
954
|
+
source,
|
|
955
|
+
sourceFile: real,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* The entry project's emitted JavaScript for `real`, or `null` when that
|
|
960
|
+
* project did not emit it. Shared with `owningModuleOptions` so "the entry
|
|
961
|
+
* project owns this file" means exactly one thing in both places.
|
|
962
|
+
*/
|
|
963
|
+
function entryEmitPath(m, real) {
|
|
964
|
+
const cached = entryEmitPathCache.get(real);
|
|
965
|
+
if (cached !== undefined) {
|
|
966
|
+
return cached;
|
|
967
|
+
}
|
|
968
|
+
const resolved = isWithin(real, m.rootDir)
|
|
969
|
+
? (0, resolveEmittedJavaScript_1.resolveEmittedJavaScript)({
|
|
970
|
+
emittedFiles: m.emittedFiles,
|
|
971
|
+
outDir: m.emitDir,
|
|
972
|
+
projectRoot: m.rootDir,
|
|
973
|
+
sourceFile: real,
|
|
974
|
+
})
|
|
975
|
+
: null;
|
|
976
|
+
entryEmitPathCache.set(real, resolved);
|
|
977
|
+
return resolved;
|
|
927
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Memo for `entryEmitPath`, because it is now on the `resolve` hook's path
|
|
981
|
+
* through `owningModuleOptions` — once per import specifier — and a miss inside
|
|
982
|
+
* `resolveEmittedJavaScript` walks the whole emit tree. The entry emit is
|
|
983
|
+
* written once before the run starts and never changes under it, so one answer
|
|
984
|
+
* per path is the only one there is.
|
|
985
|
+
*/
|
|
986
|
+
const entryEmitPathCache = new Map();
|
|
928
987
|
/**
|
|
929
988
|
* True when `real` is `directory` itself or sits beneath it. Handles a root
|
|
930
989
|
* `directory` (`/`, `C:\`): naively appending a separator would yield `//`,
|
|
@@ -982,7 +1041,7 @@ function serveBuiltDependency(built, real) {
|
|
|
982
1041
|
? null
|
|
983
1042
|
: {
|
|
984
1043
|
emittedFile: emitted,
|
|
985
|
-
|
|
1044
|
+
moduleOptions: built.moduleOptions,
|
|
986
1045
|
source,
|
|
987
1046
|
sourceFile: real,
|
|
988
1047
|
};
|
|
@@ -1063,7 +1122,15 @@ function readDependencyCache(cacheDir, metaPath) {
|
|
|
1063
1122
|
return null;
|
|
1064
1123
|
}
|
|
1065
1124
|
if (!isDependencyGeneration(meta.generation) ||
|
|
1066
|
-
typeof meta.rootDir !== "string"
|
|
1125
|
+
typeof meta.rootDir !== "string" ||
|
|
1126
|
+
// A marker with no `moduleOptions` object predates this field and cannot
|
|
1127
|
+
// say which format its emit carries. Treating the absence as "no options"
|
|
1128
|
+
// would classify a CommonJS emit as an ES module, so the generation is
|
|
1129
|
+
// rejected and rebuilt instead. Every marker this version writes carries
|
|
1130
|
+
// the object, empty or not.
|
|
1131
|
+
typeof meta.moduleOptions !== "object" ||
|
|
1132
|
+
meta.moduleOptions === null ||
|
|
1133
|
+
Array.isArray(meta.moduleOptions)) {
|
|
1067
1134
|
return null;
|
|
1068
1135
|
}
|
|
1069
1136
|
const emitDir = dependencyGenerationDir(cacheDir, meta.generation);
|
|
@@ -1073,8 +1140,16 @@ function readDependencyCache(cacheDir, metaPath) {
|
|
|
1073
1140
|
return {
|
|
1074
1141
|
emitDir,
|
|
1075
1142
|
emittedFiles: undefined,
|
|
1076
|
-
|
|
1077
|
-
|
|
1143
|
+
moduleOptions: projectModuleOptions(meta.moduleOptions),
|
|
1144
|
+
// Resolved on the way out, not trusted as written. `rootDir` never gated
|
|
1145
|
+
// reuse — the marker's generation, module options, and a non-empty emit do
|
|
1146
|
+
// — so a marker carrying an unresolved spelling was already being reused,
|
|
1147
|
+
// and then served every file of that dependency through the whole-tree stem
|
|
1148
|
+
// rescan. The pass is idempotent and runs only on a hit, which
|
|
1149
|
+
// `ensureProjectBuilt` memoizes per tsconfig. A marker from an earlier ttsc
|
|
1150
|
+
// survives only under the shared `os.tmpdir()/ttsx-dep` fallback root; the
|
|
1151
|
+
// manifest's own `depCacheDir` is per-process and removed with the run.
|
|
1152
|
+
rootDir: resolvePhysicalPath(meta.rootDir),
|
|
1078
1153
|
};
|
|
1079
1154
|
}
|
|
1080
1155
|
/**
|
|
@@ -1194,14 +1269,10 @@ function buildDependency(tsconfig, cacheDir, metaPath) {
|
|
|
1194
1269
|
.filter((line) => line.trim().length !== 0)
|
|
1195
1270
|
.join("\n"));
|
|
1196
1271
|
}
|
|
1197
|
-
const rootDir =
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
? project.compilerOptions.module
|
|
1202
|
-
: undefined;
|
|
1203
|
-
publishDependencyMeta(metaPath, { generation, moduleOption, rootDir });
|
|
1204
|
-
return { emitDir, emittedFiles: undefined, moduleOption, rootDir };
|
|
1272
|
+
const rootDir = resolveDependencySourceRoot(project);
|
|
1273
|
+
const moduleOptions = projectModuleOptions(project.compilerOptions);
|
|
1274
|
+
publishDependencyMeta(metaPath, { generation, moduleOptions, rootDir });
|
|
1275
|
+
return { emitDir, emittedFiles: undefined, moduleOptions, rootDir };
|
|
1205
1276
|
}
|
|
1206
1277
|
/**
|
|
1207
1278
|
* Publish the completion marker atomically: write it to a private temp name in
|
|
@@ -1512,7 +1583,7 @@ function formatDuration(ms) {
|
|
|
1512
1583
|
}
|
|
1513
1584
|
/** Owning-tsconfig cache keyed by directory, mirroring `packageTypeCache`. */
|
|
1514
1585
|
const tsconfigCache = new Map();
|
|
1515
|
-
const
|
|
1586
|
+
const moduleOptionsCache = new Map();
|
|
1516
1587
|
/**
|
|
1517
1588
|
* The nearest `tsconfig.json` at or above `file`'s directory, or `null`. The
|
|
1518
1589
|
* walk stops at a `node_modules` boundary: a tsconfig above `node_modules`
|
|
@@ -1565,14 +1636,81 @@ function readFileOrNull(file) {
|
|
|
1565
1636
|
return null;
|
|
1566
1637
|
}
|
|
1567
1638
|
}
|
|
1639
|
+
/**
|
|
1640
|
+
* The physical path, in the same spelling the launcher decided on.
|
|
1641
|
+
*
|
|
1642
|
+
* `realpathSync.native` first, because the plain implementation resolves
|
|
1643
|
+
* reparse points but leaves a Windows 8.3 component alone — and `TEMP` is
|
|
1644
|
+
* `C:\Users\RUNNER~1\...` on a GitHub Windows runner. The launcher resolves the
|
|
1645
|
+
* entry through `fs.realpathSync.native`, so answering here with the short name
|
|
1646
|
+
* would put a `..` in `path.relative(rootDir, real)`, drop the exact-mirror
|
|
1647
|
+
* lane, and leave the entry to whatever the trailing-stem matcher picks.
|
|
1648
|
+
*/
|
|
1568
1649
|
function realPath(target) {
|
|
1569
1650
|
try {
|
|
1570
|
-
return node_fs_1.default.realpathSync(target);
|
|
1651
|
+
return node_fs_1.default.realpathSync.native?.(target) ?? node_fs_1.default.realpathSync(target);
|
|
1571
1652
|
}
|
|
1572
1653
|
catch {
|
|
1573
1654
|
return target;
|
|
1574
1655
|
}
|
|
1575
1656
|
}
|
|
1657
|
+
/**
|
|
1658
|
+
* The physical spelling of a path, produced the way the served sources it will
|
|
1659
|
+
* be compared against are produced.
|
|
1660
|
+
*
|
|
1661
|
+
* `createFilesystemPathIdentityContext` is the same resolver the entry lane
|
|
1662
|
+
* uses (`prepareExecution.ts::resolveRuntimeSourceRoot`). For a path that
|
|
1663
|
+
* exists it is one `realpathSync.native`. For one that does not it resolves as
|
|
1664
|
+
* far as the filesystem goes and folds the missing tail by the case semantics
|
|
1665
|
+
* of the surviving ancestor, which costs a directory read and, on Windows, can
|
|
1666
|
+
* cost an `fsutil` query — worth knowing, but off the path a real dependency
|
|
1667
|
+
* root takes.
|
|
1668
|
+
*
|
|
1669
|
+
* Total on purpose. `throwOnRealpathError: false` silences a failed realpath,
|
|
1670
|
+
* but the case-sensitivity probe can still fail on its own (an unreadable
|
|
1671
|
+
* ancestor, a denied alternate-case `lstat`), and neither caller has anywhere
|
|
1672
|
+
* to put that: `readDependencyCache` is contracted to answer `null` rather than
|
|
1673
|
+
* throw, and `buildDependency` has already produced its emit. The unresolved
|
|
1674
|
+
* spelling is exactly what both had before this pass, so it is the fallback.
|
|
1675
|
+
*/
|
|
1676
|
+
function resolvePhysicalPath(location) {
|
|
1677
|
+
try {
|
|
1678
|
+
return (0, projectInputPathIdentity_1.createFilesystemPathIdentityContext)({
|
|
1679
|
+
throwOnRealpathError: false,
|
|
1680
|
+
}).resolve(location).path;
|
|
1681
|
+
}
|
|
1682
|
+
catch {
|
|
1683
|
+
return location;
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* The source-tree root a dependency's emit mirrors, in the spelling
|
|
1688
|
+
* `resolveEmittedJavaScript` compares a served source against.
|
|
1689
|
+
*
|
|
1690
|
+
* Both branches need the pass, for different reasons. A declared `rootDir`
|
|
1691
|
+
* arrives from `readProjectConfig` joined against the config that declared it
|
|
1692
|
+
* but never resolved, so a `rootDir` that is itself a symlinked directory stays
|
|
1693
|
+
* unresolved. `project.root` arrives through plain `fs.realpathSync`, which
|
|
1694
|
+
* follows reparse points but leaves a Windows 8.3 component alone — and
|
|
1695
|
+
* {@link realPath} uses `fs.realpathSync.native`, which expands it. Either way
|
|
1696
|
+
* `path.relative` puts a `..` in front of an in-project source,
|
|
1697
|
+
* `resolveExactEmittedFiles` returns nothing, and every served file of that
|
|
1698
|
+
* dependency falls to the trailing-stem matcher, which rescans the whole emit
|
|
1699
|
+
* tree per file because a dependency build publishes no emitted-file list.
|
|
1700
|
+
*
|
|
1701
|
+
* This is the pass the entry lane settled on for the same mixed pair; the two
|
|
1702
|
+
* lanes now read alike, `path.isAbsolute` guard included. `readProjectConfig`
|
|
1703
|
+
* absolutizes every path option against the config that declared it, so that
|
|
1704
|
+
* guard is a mirror of the entry lane rather than a live branch.
|
|
1705
|
+
*/
|
|
1706
|
+
function resolveDependencySourceRoot(project) {
|
|
1707
|
+
const rootDir = project.compilerOptions.rootDir;
|
|
1708
|
+
return resolvePhysicalPath(typeof rootDir !== "string"
|
|
1709
|
+
? project.root
|
|
1710
|
+
: node_path_1.default.isAbsolute(rootDir)
|
|
1711
|
+
? rootDir
|
|
1712
|
+
: node_path_1.default.resolve(project.root, rootDir));
|
|
1713
|
+
}
|
|
1576
1714
|
/**
|
|
1577
1715
|
* Map the JavaScript extension a relative `specifier` carries to the TypeScript
|
|
1578
1716
|
* source extensions tsgo would have emitted it from. Running from source, a
|
|
@@ -1654,35 +1792,138 @@ function probeRescuableSpecifier(specifier, parentURL) {
|
|
|
1654
1792
|
}
|
|
1655
1793
|
/**
|
|
1656
1794
|
* Decide the module format the way Node and tsgo do — from configuration, never
|
|
1657
|
-
* by sniffing the emitted text.
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1660
|
-
*
|
|
1661
|
-
*
|
|
1662
|
-
*
|
|
1795
|
+
* by sniffing the emitted text.
|
|
1796
|
+
*
|
|
1797
|
+
* The file extension is authoritative first (`.mts`/`.mjs` → module,
|
|
1798
|
+
* `.cts`/`.cjs` → commonjs), exactly as tsgo's
|
|
1799
|
+
* `getImpliedNodeFormatForEmitWorker` checks it ahead of everything else.
|
|
1800
|
+
*
|
|
1801
|
+
* After that the decision belongs to the project that emitted the file, so
|
|
1802
|
+
* `options` is the whole compiler-option pair tsgo consults, not just `module`:
|
|
1803
|
+
* an absent `module` is NOT "ask Node", it is "derive the kind from `target`"
|
|
1804
|
+
* (tsgo's `getEmitModuleKind`), and TypeScript 7 defaults `target` to the
|
|
1805
|
+
* latest standard, which means ES modules. Only the `node*` family defers to
|
|
1806
|
+
* the nearest `package.json` `type`, because only that family makes tsgo
|
|
1807
|
+
* consult it.
|
|
1808
|
+
*
|
|
1809
|
+
* `options` is `null` for a file no tsconfig owns at all — a raw `.ts` shipped
|
|
1810
|
+
* under `node_modules`. Nothing emitted it, so Node's own rule is the only rule
|
|
1811
|
+
* there is, and the package `type` decides.
|
|
1663
1812
|
*/
|
|
1664
|
-
function moduleFormat(filename,
|
|
1813
|
+
function moduleFormat(filename, options) {
|
|
1665
1814
|
if (filename.endsWith(".mts") || filename.endsWith(".mjs")) {
|
|
1666
1815
|
return "module";
|
|
1667
1816
|
}
|
|
1668
1817
|
if (filename.endsWith(".cts") || filename.endsWith(".cjs")) {
|
|
1669
1818
|
return "commonjs";
|
|
1670
1819
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
return "commonjs";
|
|
1820
|
+
if (options === null) {
|
|
1821
|
+
return nearestPackageType(filename);
|
|
1674
1822
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1823
|
+
// A package that states its `type` outright decides for the files inside it
|
|
1824
|
+
// whatever the module kind is. This is where a source-shipping dependency's
|
|
1825
|
+
// own declaration overrides the compiling project's `module`; see
|
|
1826
|
+
// `declaredNodeModulesPackageType` for why `node_modules` bounds it.
|
|
1827
|
+
const declared = declaredNodeModulesPackageType(filename);
|
|
1828
|
+
if (declared !== null) {
|
|
1829
|
+
return declared;
|
|
1830
|
+
}
|
|
1831
|
+
const kind = effectiveModuleKind(options);
|
|
1832
|
+
if (kind === "node16" ||
|
|
1833
|
+
kind === "node18" ||
|
|
1834
|
+
kind === "node20" ||
|
|
1835
|
+
kind === "nodenext") {
|
|
1680
1836
|
return nearestPackageType(filename);
|
|
1681
1837
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1838
|
+
if (kind === "commonjs") {
|
|
1839
|
+
return "commonjs";
|
|
1840
|
+
}
|
|
1841
|
+
// Everything that remains (es2015 … esnext, and `preserve`, which keeps the
|
|
1842
|
+
// authored ESM syntax verbatim whatever the package `type` says) is emitted
|
|
1843
|
+
// as ECMAScript modules. `amd`, `umd`, and `system` are not among them:
|
|
1844
|
+
// TypeScript 7 removed all three, so a project declaring one cannot build,
|
|
1845
|
+
// and a file it would have owned reaches the run through the orphan lane
|
|
1846
|
+
// instead.
|
|
1684
1847
|
return "module";
|
|
1685
1848
|
}
|
|
1849
|
+
/**
|
|
1850
|
+
* The `"type"` a `node_modules` package states for `filename`, or `null`.
|
|
1851
|
+
*
|
|
1852
|
+
* Tsgo's `GetImpliedNodeFormatForEmitWorker` consults a file's
|
|
1853
|
+
* `packageJsonType` for **every** module kind, not just the `node*` family. So
|
|
1854
|
+
* a source-shipping dependency that declares `"type": "commonjs"` is emitted as
|
|
1855
|
+
* CommonJS even while the compiling project asks for `esnext`, and the mirror
|
|
1856
|
+
* has to honour the same override or Node is handed the wrong format.
|
|
1857
|
+
*
|
|
1858
|
+
* Outside `node_modules` that field is empty for every module kind this
|
|
1859
|
+
* override can change. `loadSourceFileMetaData` does fill it elsewhere — for a
|
|
1860
|
+
* file whose extension does not itself state the format (anything but `.mts`,
|
|
1861
|
+
* `.cts`, `.mjs`, `.cjs`) whose project sets `moduleResolution` to the
|
|
1862
|
+
* `node16`…`nodenext` family — but `program.go` rejects that resolution unless
|
|
1863
|
+
* `module` is in the same family, and for a `node*` `module` the package `type`
|
|
1864
|
+
* is already the whole answer, which the caller's own `node*` branch produces.
|
|
1865
|
+
* So answering from a manifest outside `node_modules` could only override the
|
|
1866
|
+
* project's own `module` option, the very confusion this classifier exists to
|
|
1867
|
+
* end.
|
|
1868
|
+
*/
|
|
1869
|
+
function declaredNodeModulesPackageType(filename) {
|
|
1870
|
+
if (!pathHasNodeModulesSegment(filename)) {
|
|
1871
|
+
return null;
|
|
1872
|
+
}
|
|
1873
|
+
return declaredPackageType(filename);
|
|
1874
|
+
}
|
|
1875
|
+
/**
|
|
1876
|
+
* Whether any path segment of `filename` is literally `node_modules`.
|
|
1877
|
+
*
|
|
1878
|
+
* Case-sensitive on every platform, because the upstream test this mirrors is a
|
|
1879
|
+
* case-sensitive substring search and Node's own resolver recognises only the
|
|
1880
|
+
* exact spelling. A directory named `Node_Modules` is a package store to
|
|
1881
|
+
* neither of them, and must not become one here.
|
|
1882
|
+
*/
|
|
1883
|
+
function pathHasNodeModulesSegment(filename) {
|
|
1884
|
+
return filename.split(/[\\/]/).includes("node_modules");
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* The `module` kind tsgo actually emits with, mirroring `getEmitModuleKind`:
|
|
1888
|
+
* the declared `module` when there is one, and otherwise the kind implied by
|
|
1889
|
+
* `target`. `"none"` is the spelling of an unset `module`, so it derives the
|
|
1890
|
+
* same way.
|
|
1891
|
+
*/
|
|
1892
|
+
function effectiveModuleKind(options) {
|
|
1893
|
+
const declared = (options.module ?? "").toLowerCase();
|
|
1894
|
+
if (declared !== "" && declared !== "none") {
|
|
1895
|
+
return declared;
|
|
1896
|
+
}
|
|
1897
|
+
const target = (options.target ?? "").toLowerCase();
|
|
1898
|
+
if (target === "esnext") {
|
|
1899
|
+
return "esnext";
|
|
1900
|
+
}
|
|
1901
|
+
const year = scriptTargetYear(target);
|
|
1902
|
+
if (year === null) {
|
|
1903
|
+
// An unset `target` is `ScriptTargetLatestStandard` upstream, well above the
|
|
1904
|
+
// ES2022 boundary. Every other spelling lands here too: TypeScript 7 removed
|
|
1905
|
+
// `ES5` and dropped `ES3` entirely, and its only remaining targets are
|
|
1906
|
+
// year-numbered ones, so nothing that reaches emit derives CommonJS.
|
|
1907
|
+
return "es2022";
|
|
1908
|
+
}
|
|
1909
|
+
if (year >= 2022)
|
|
1910
|
+
return "es2022";
|
|
1911
|
+
if (year >= 2020)
|
|
1912
|
+
return "es2020";
|
|
1913
|
+
return "es2015";
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Year of an `ES<year>` script target, with `es6` normalized to its `es2015`
|
|
1917
|
+
* synonym. Returns `null` when the spelling is not a year-numbered target,
|
|
1918
|
+
* which the caller resolves to the modern default.
|
|
1919
|
+
*/
|
|
1920
|
+
function scriptTargetYear(target) {
|
|
1921
|
+
if (target === "es6") {
|
|
1922
|
+
return 2015;
|
|
1923
|
+
}
|
|
1924
|
+
const match = /^es(\d{4})$/.exec(target);
|
|
1925
|
+
return match === null ? null : Number(match[1]);
|
|
1926
|
+
}
|
|
1686
1927
|
/** Package-type cache keyed by directory, mirroring Node's own lookup walk. */
|
|
1687
1928
|
const packageTypeCache = new Map();
|
|
1688
1929
|
function nearestPackageType(filename) {
|
|
@@ -1725,6 +1966,40 @@ function readPackageType(directory) {
|
|
|
1725
1966
|
return "commonjs";
|
|
1726
1967
|
}
|
|
1727
1968
|
}
|
|
1969
|
+
/**
|
|
1970
|
+
* The `"type"` the nearest `package.json` states outright, or `null` when the
|
|
1971
|
+
* nearest manifest omits it (or there is none).
|
|
1972
|
+
*
|
|
1973
|
+
* `nearestPackageType` answers "what format would Node use", which defaults a
|
|
1974
|
+
* silent manifest to CommonJS. This answers the narrower question "did a
|
|
1975
|
+
* package actually say", which is what an override has to be built on: a
|
|
1976
|
+
* manifest that says nothing must not out-vote the compiling project's own
|
|
1977
|
+
* `module` option.
|
|
1978
|
+
*/
|
|
1979
|
+
function declaredPackageType(filename) {
|
|
1980
|
+
let directory = node_path_1.default.dirname(filename);
|
|
1981
|
+
while (true) {
|
|
1982
|
+
const manifestPath = node_path_1.default.join(directory, "package.json");
|
|
1983
|
+
if (isFile(manifestPath)) {
|
|
1984
|
+
try {
|
|
1985
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(manifestPath, "utf8"));
|
|
1986
|
+
// The walk stops at the first manifest either way, exactly as Node's
|
|
1987
|
+
// package-scope lookup does; only the answer differs.
|
|
1988
|
+
return parsed.type === "module" || parsed.type === "commonjs"
|
|
1989
|
+
? parsed.type
|
|
1990
|
+
: null;
|
|
1991
|
+
}
|
|
1992
|
+
catch {
|
|
1993
|
+
return null;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
const parent = node_path_1.default.dirname(directory);
|
|
1997
|
+
if (parent === directory) {
|
|
1998
|
+
return null;
|
|
1999
|
+
}
|
|
2000
|
+
directory = parent;
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
1728
2003
|
function isRelativeSpecifier(specifier) {
|
|
1729
2004
|
return (specifier === "." ||
|
|
1730
2005
|
specifier === ".." ||
|