ttsc 0.26.2 → 0.28.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 +9 -0
- package/cmd/graphbench/main.go +26 -16
- package/cmd/graphdump/main.go +37 -12
- package/cmd/graphdump/run_declares_the_capability_its_output_carries_test.go +137 -0
- package/cmd/graphdump/run_reports_an_unknown_flag_without_exiting_the_process_test.go +56 -0
- package/cmd/graphdump/run_reports_an_unloadable_project_without_printing_a_dump_test.go +46 -0
- package/cmd/ttsc/api_transform.go +28 -3
- package/cmd/ttscgraph/auxiliary_identity_state_tracks_retargets_not_source_contents_test.go +85 -0
- package/cmd/ttscgraph/disk_realpath_other.go +16 -0
- package/cmd/ttscgraph/disk_realpath_windows.go +56 -0
- package/cmd/ttscgraph/dump.go +43 -11
- package/cmd/ttscgraph/run_dump_claims_artifacts_only_when_it_asked_test.go +135 -0
- package/cmd/ttscgraph/run_dump_declares_the_capability_its_output_carries_test.go +96 -0
- package/cmd/ttscgraph/serve.go +357 -60
- package/cmd/ttscgraph/serve_adopts_a_republished_artifact_set_test.go +348 -0
- package/cmd/ttscgraph/serve_projects_an_artifact_no_source_owns_test.go +90 -0
- package/cmd/ttscgraph/serve_shards.go +31 -9
- package/driver/config_discovery.go +121 -0
- package/driver/emit_plugin.go +77 -11
- package/driver/graph.go +64 -11
- package/driver/input_observation.go +273 -0
- package/driver/input_observation_test.go +149 -0
- package/driver/plugins.go +506 -15
- package/driver/program.go +54 -1
- package/driver/resolution_candidates.go +13 -5
- package/driver/rewrite.go +45 -2
- package/driver/transform_dependencies.go +283 -0
- package/driver/transform_dependencies_declares_every_file_without_a_contributor_test.go +28 -0
- package/driver/transform_dependencies_drops_a_self_reported_dependency_test.go +21 -0
- package/driver/transform_dependencies_keys_a_reported_path_like_the_envelope_test.go +46 -0
- package/driver/transform_dependencies_requires_every_contributor_to_declare_test.go +38 -0
- package/driver/transform_dependencies_stays_silent_for_an_undeclared_contributor_test.go +23 -0
- package/driver/transform_dependencies_withdraws_completeness_after_an_unusable_dependency_test.go +37 -0
- package/internal/graph/artifacts.go +230 -0
- package/internal/graph/artifacts_index_what_a_plugin_published_test.go +154 -0
- package/internal/graph/artifacts_survive_a_partial_rebuild_test.go +89 -0
- package/internal/graph/build.go +35 -0
- package/internal/graph/build_releases_the_scratch_it_no_longer_reads_test.go +118 -0
- package/internal/graph/doc_refs_are_the_use_the_checker_already_counts_test.go +93 -0
- package/internal/graph/doc_refs_resolve_documentation_links_test.go +335 -0
- package/internal/graph/doc_tags.go +416 -0
- package/internal/graph/doc_tags_are_claimed_before_they_are_read_test.go +131 -0
- package/internal/graph/doc_tags_capture_every_unrecognized_tag_test.go +165 -0
- package/internal/graph/doc_tags_read_only_what_the_parser_attached_test.go +97 -0
- package/internal/graph/doc_tags_stop_at_the_external_boundary_test.go +59 -0
- package/internal/graph/doc_tags_survive_line_endings_and_repeated_blocks_test.go +146 -0
- package/internal/graph/dump.go +60 -13
- package/internal/graph/dump_checkout_paths_are_stable_test.go +2 -2
- package/internal/graph/dump_paths.go +2 -2
- package/internal/graph/dump_paths_are_portable_and_injective_test.go +1 -1
- package/internal/graph/edge_kinds_names_every_declared_kind_test.go +49 -0
- package/internal/graph/edges.go +1 -0
- package/internal/graph/graph.go +79 -4
- package/internal/graph/provenance.go +29 -2
- package/lib/TtscCompiler.d.ts +12 -11
- package/lib/TtscCompiler.js +44 -29
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.js +2 -2
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +4 -3
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/createNativeProjectContextArgs.d.ts +9 -0
- package/lib/compiler/internal/project/createNativeProjectContextArgs.js +16 -4
- package/lib/compiler/internal/project/createNativeProjectContextArgs.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +2 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js +3 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +1 -1
- package/lib/compiler/internal/runBuild.js +80 -34
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +2 -2
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +28 -0
- package/lib/compiler/internal/sharedHostHelpers.js +39 -0
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +12 -4
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.d.ts +3 -0
- package/lib/compiler/internal/transformProjectInMemory.js +239 -35
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/schema.js +12 -5
- package/lib/flags/schema.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/internal/createCanonicalTempDirectory.d.ts +17 -0
- package/lib/internal/createCanonicalTempDirectory.js +42 -0
- package/lib/internal/createCanonicalTempDirectory.js.map +1 -0
- package/lib/internal/projectInputPathIdentity.d.ts +3 -0
- package/lib/internal/projectInputPathIdentity.js +11 -5
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/internal/resolveNodeBinary.d.ts +14 -0
- package/lib/internal/resolveNodeBinary.js +150 -0
- package/lib/internal/resolveNodeBinary.js.map +1 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.d.ts +15 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js +61 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js.map +1 -0
- package/lib/launcher/internal/prepareExecution.d.ts +3 -0
- package/lib/launcher/internal/prepareExecution.js +42 -14
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +25 -13
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +18 -3
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -0
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +47 -3
- package/lib/launcher/internal/runtimeHooks.js +746 -64
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +62 -8
- package/lib/plugin/internal/buildSourcePlugin.js +754 -72
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/capabilityResolutionCache.d.ts +76 -0
- package/lib/plugin/internal/capabilityResolutionCache.js +224 -0
- package/lib/plugin/internal/capabilityResolutionCache.js.map +1 -0
- package/lib/plugin/internal/loadProjectPlugins.d.ts +17 -5
- package/lib/plugin/internal/loadProjectPlugins.js +1339 -77
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/resolveCapabilityPlugins.d.ts +55 -0
- package/lib/plugin/resolveCapabilityPlugins.js +118 -0
- package/lib/plugin/resolveCapabilityPlugins.js.map +1 -0
- package/lib/register.d.ts +1 -0
- package/lib/register.js +46 -0
- package/lib/register.js.map +1 -0
- package/lib/structures/ITtscCompilerContext.d.ts +6 -3
- package/lib/structures/ITtscCompilerTransformation.d.ts +38 -0
- package/lib/structures/ITtscPlugin.d.ts +33 -0
- package/package.json +16 -9
- package/shim/ast/parent.go +16 -3
- package/shim/checker/enums_gen.go +11 -0
- package/shim/checker/go.mod +9 -0
- package/shim/checker/go.sum +22 -0
- package/shim/checker/shim.go +78 -0
- package/shim/compiler/emithook.go +10 -0
- package/shim/compiler/incremental.go +45 -8
- package/src/TtscCompiler.ts +55 -33
- package/src/compiler/internal/captureProcessOutput.ts +3 -2
- package/src/compiler/internal/compileProjectInMemory.ts +4 -3
- package/src/compiler/internal/project/createNativeProjectContextArgs.ts +19 -4
- package/src/compiler/internal/resolveEmittedJavaScript.ts +6 -0
- package/src/compiler/internal/runBuild.ts +102 -39
- package/src/compiler/internal/runSingleFileEmit.ts +2 -2
- package/src/compiler/internal/sharedHostHelpers.ts +44 -0
- package/src/compiler/internal/startResidentTransform.ts +12 -3
- package/src/compiler/internal/transformProjectInMemory.ts +410 -39
- package/src/flags/schema.ts +12 -5
- package/src/index.ts +1 -0
- package/src/internal/createCanonicalTempDirectory.ts +53 -0
- package/src/internal/projectInputPathIdentity.ts +17 -4
- package/src/internal/resolveNodeBinary.ts +173 -0
- package/src/internal/resolveSafeCacheCleanupTargets.ts +85 -0
- package/src/launcher/internal/prepareExecution.ts +50 -14
- package/src/launcher/internal/runTtsc.ts +24 -13
- package/src/launcher/internal/runTtscserver.ts +18 -3
- package/src/launcher/internal/runTtsx.ts +2 -0
- package/src/launcher/internal/runtimeHooks.ts +934 -68
- package/src/plugin/internal/buildSourcePlugin.ts +984 -79
- package/src/plugin/internal/capabilityResolutionCache.ts +305 -0
- package/src/plugin/internal/loadProjectPlugins.ts +1718 -104
- package/src/plugin/resolveCapabilityPlugins.ts +171 -0
- package/src/register.ts +47 -0
- package/src/structures/ITtscCompilerContext.ts +6 -3
- package/src/structures/ITtscCompilerTransformation.ts +46 -0
- package/src/structures/ITtscPlugin.ts +36 -0
- package/tools/shim_audit/baseline.json +0 -2
- package/utility/host.go +18 -3
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +0 -5
- package/lib/internal/assertSafeExplicitCacheDirectory.js +0 -26
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +0 -1
- package/src/internal/assertSafeExplicitCacheDirectory.ts +0 -29
|
@@ -10,7 +10,9 @@ exports.entryModuleFormat = entryModuleFormat;
|
|
|
10
10
|
exports.restoreStrippedNodeBuiltinScheme = restoreStrippedNodeBuiltinScheme;
|
|
11
11
|
exports.projectModuleOptions = projectModuleOptions;
|
|
12
12
|
exports.isWithin = isWithin;
|
|
13
|
+
exports.dependencyCacheKey = dependencyCacheKey;
|
|
13
14
|
exports.readDependencyCache = readDependencyCache;
|
|
15
|
+
exports.dependencyCacheRoot = dependencyCacheRoot;
|
|
14
16
|
exports.acquireDependencyBuildLock = acquireDependencyBuildLock;
|
|
15
17
|
exports.releaseDependencyBuildLock = releaseDependencyBuildLock;
|
|
16
18
|
exports.reclaimDependencyBuildLock = reclaimDependencyBuildLock;
|
|
@@ -27,6 +29,7 @@ const resolveEmittedJavaScript_1 = require("../../compiler/internal/resolveEmitt
|
|
|
27
29
|
const resolveTsgo_1 = require("../../compiler/internal/resolveTsgo");
|
|
28
30
|
const runBuild_1 = require("../../compiler/internal/runBuild");
|
|
29
31
|
const spawnNative_1 = require("../../compiler/internal/spawnNative");
|
|
32
|
+
const createCanonicalTempDirectory_1 = require("../../internal/createCanonicalTempDirectory");
|
|
30
33
|
const projectInputPathIdentity_1 = require("../../internal/projectInputPathIdentity");
|
|
31
34
|
const servedSourceMap_1 = require("./servedSourceMap");
|
|
32
35
|
/**
|
|
@@ -67,25 +70,39 @@ const RESOLVABLE_EXTENSIONS = [
|
|
|
67
70
|
".mjs",
|
|
68
71
|
".cjs",
|
|
69
72
|
];
|
|
73
|
+
/** Union of the ttsx rescue probes and Node's built-in CommonJS probes. */
|
|
74
|
+
const DESCRIPTOR_PROBE_EXTENSIONS = [
|
|
75
|
+
...RESOLVABLE_EXTENSIONS,
|
|
76
|
+
".json",
|
|
77
|
+
".node",
|
|
78
|
+
];
|
|
70
79
|
/** TypeScript source extensions these hooks compile. */
|
|
71
80
|
const TYPESCRIPT_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts"];
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
let environmentManifestCache;
|
|
82
|
+
const registeredManifests = [];
|
|
83
|
+
function environmentManifest() {
|
|
84
|
+
if (environmentManifestCache !== undefined) {
|
|
85
|
+
return environmentManifestCache;
|
|
76
86
|
}
|
|
77
87
|
const file = process.env.TTSX_RUNTIME_MANIFEST;
|
|
78
88
|
if (file === undefined || file.length === 0) {
|
|
79
|
-
|
|
80
|
-
return
|
|
89
|
+
environmentManifestCache = null;
|
|
90
|
+
return environmentManifestCache;
|
|
81
91
|
}
|
|
82
92
|
try {
|
|
83
|
-
|
|
93
|
+
environmentManifestCache = JSON.parse(node_fs_1.default.readFileSync(file, "utf8"));
|
|
84
94
|
}
|
|
85
95
|
catch {
|
|
86
|
-
|
|
96
|
+
environmentManifestCache = null;
|
|
87
97
|
}
|
|
88
|
-
return
|
|
98
|
+
return environmentManifestCache;
|
|
99
|
+
}
|
|
100
|
+
/** Every checked entry emit available to this runtime, in ownership order. */
|
|
101
|
+
function runtimeManifests() {
|
|
102
|
+
const inherited = environmentManifest();
|
|
103
|
+
return inherited === null
|
|
104
|
+
? registeredManifests
|
|
105
|
+
: [inherited, ...registeredManifests];
|
|
89
106
|
}
|
|
90
107
|
/**
|
|
91
108
|
* Lowest Node.js the ttsx source runtime supports. The synchronous
|
|
@@ -154,6 +171,7 @@ function assertNodeRuntimeSupport() {
|
|
|
154
171
|
}
|
|
155
172
|
}
|
|
156
173
|
let installed = false;
|
|
174
|
+
let prepareRuntimeEntry;
|
|
157
175
|
/**
|
|
158
176
|
* Install the source-loading hooks on the current (main) thread. Idempotent:
|
|
159
177
|
* the bootstrap installs them for the entry process, and `NODE_OPTIONS`
|
|
@@ -164,10 +182,21 @@ let installed = false;
|
|
|
164
182
|
* `require()` made from inside a CommonJS module that was itself reached
|
|
165
183
|
* through an ESM `import` (the interop translator loads it on the raw CJS
|
|
166
184
|
* path). The ESM graph goes through `registerHooks`; the CommonJS `require`
|
|
167
|
-
* graph goes through `Module._extensions` — the
|
|
168
|
-
*
|
|
185
|
+
* graph goes through `Module._extensions` and `Module._resolveFilename` — the
|
|
186
|
+
* canonical loader extension points `ts-node`/`tsx` use for the same reason.
|
|
187
|
+
*
|
|
188
|
+
* Both halves of that second graph are required, and they answer different
|
|
189
|
+
* questions. `_extensions` decides how a file that was _found_ is compiled, and
|
|
190
|
+
* Node reuses its keys when it probes an extensionless request, so
|
|
191
|
+
* `require("./x")` reaches `x.ts` for free. Nothing there resolves a request
|
|
192
|
+
* that already carries an extension: `require("./x.js")` backed only by `x.ts`
|
|
193
|
+
* is a resolution failure before any compiler is consulted, which is why the
|
|
194
|
+
* rescue is installed on `_resolveFilename` as well (samchon/ttsc#1280).
|
|
169
195
|
*/
|
|
170
|
-
function installRuntimeHooks() {
|
|
196
|
+
function installRuntimeHooks(options = {}) {
|
|
197
|
+
if (options.prepareEntry !== undefined) {
|
|
198
|
+
prepareRuntimeEntry = options.prepareEntry;
|
|
199
|
+
}
|
|
171
200
|
if (installed) {
|
|
172
201
|
return;
|
|
173
202
|
}
|
|
@@ -182,6 +211,7 @@ function installRuntimeHooks() {
|
|
|
182
211
|
}
|
|
183
212
|
(0, node_module_1.registerHooks)({ load, resolve });
|
|
184
213
|
installCommonJsHook();
|
|
214
|
+
installCommonJsResolveHook();
|
|
185
215
|
}
|
|
186
216
|
/**
|
|
187
217
|
* Register a CommonJS `require` handler for each TypeScript source extension so
|
|
@@ -191,12 +221,95 @@ function installRuntimeHooks() {
|
|
|
191
221
|
function installCommonJsHook() {
|
|
192
222
|
const extensions = node_module_1.Module._extensions;
|
|
193
223
|
const compile = (module, filename) => {
|
|
194
|
-
module.
|
|
224
|
+
const parent = module.parent?.filename;
|
|
225
|
+
module._compile(resolveServedSource(filename, (0, node_url_1.pathToFileURL)(filename).href, typeof parent !== "string" || !isTypeScriptSource(parent)).source, filename);
|
|
195
226
|
};
|
|
196
227
|
for (const extension of [".ts", ".tsx", ".cts"]) {
|
|
197
228
|
extensions[extension] = compile;
|
|
198
229
|
}
|
|
199
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Map a JavaScript spelling back to its TypeScript source on the CommonJS
|
|
233
|
+
* resolution path, so `require("./x.js")` finds `x.ts` when nothing emitted
|
|
234
|
+
* `x.js`.
|
|
235
|
+
*
|
|
236
|
+
* TypeScript asks authors to write the emitted `.js` extension in a relative
|
|
237
|
+
* specifier, so this spelling is the documented one rather than a mistake, and
|
|
238
|
+
* the ESM `resolve` hook has always rescued it. The CommonJS graph could not,
|
|
239
|
+
* and on Node 22 that graph is reached by every `require()` inside a CommonJS
|
|
240
|
+
* module the ESM loader evaluated — `module.registerHooks` sees the `import()`
|
|
241
|
+
* of that module and nothing within it. Node 24 routes the same `require`
|
|
242
|
+
* through the hooks, which is why the gap was only ever visible on the oldest
|
|
243
|
+
* runtime ttsx supports (`engines.node` is `>=22.15.0`).
|
|
244
|
+
*
|
|
245
|
+
* The wrapper only ever answers a request Node's own resolver already refused,
|
|
246
|
+
* so a resolution that succeeds is never perturbed, and a request no candidate
|
|
247
|
+
* satisfies rethrows Node's original error with its `MODULE_NOT_FOUND` code and
|
|
248
|
+
* `requireStack` intact. `require.resolve` shares this entry point and is
|
|
249
|
+
* rescued with it, except in its `{ paths }` form, which resolves against the
|
|
250
|
+
* caller's list rather than the parent this reads.
|
|
251
|
+
*/
|
|
252
|
+
function installCommonJsResolveHook() {
|
|
253
|
+
const internals = node_module_1.Module;
|
|
254
|
+
const original = internals._resolveFilename;
|
|
255
|
+
internals._resolveFilename = function resolveFilename(request, parent, isMain, options) {
|
|
256
|
+
try {
|
|
257
|
+
return original.call(this, request, parent, isMain, options);
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
const rescued = rescueCommonJsRequest(request, parent, options);
|
|
261
|
+
if (rescued === null) {
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
return rescued;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The TypeScript source a refused CommonJS request names, or `null`.
|
|
270
|
+
*
|
|
271
|
+
* Only a relative or absolute request can be rescued: a bare specifier is a
|
|
272
|
+
* package lookup, whose own resolver already probed every extension the
|
|
273
|
+
* installed `_extensions` keys declare.
|
|
274
|
+
*
|
|
275
|
+
* Two shapes decline before that. `_resolveFilename` is an internal entry point
|
|
276
|
+
* anything may call, so a non-string request reaches here as readily as a
|
|
277
|
+
* specifier does, and reading it would replace Node's own argument error with a
|
|
278
|
+
* `TypeError` from this file. And `require.resolve(request, { paths })`
|
|
279
|
+
* resolves against the caller's list, which this rescue does not consult, so
|
|
280
|
+
* answering it from the parent's directory would be a different question's
|
|
281
|
+
* answer.
|
|
282
|
+
*/
|
|
283
|
+
function rescueCommonJsRequest(request, parent, options) {
|
|
284
|
+
if (typeof request !== "string") {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
if (typeof options === "object" &&
|
|
288
|
+
options !== null &&
|
|
289
|
+
Array.isArray(options.paths)) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
let base;
|
|
293
|
+
if (node_path_1.default.isAbsolute(request)) {
|
|
294
|
+
base = request;
|
|
295
|
+
}
|
|
296
|
+
else if (isRelativeSpecifier(request)) {
|
|
297
|
+
const from = parent?.filename;
|
|
298
|
+
if (typeof from !== "string") {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
base = node_path_1.default.resolve(node_path_1.default.dirname(from), request);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
for (const candidate of typescriptSourcesForJavaScriptSpecifier(base)) {
|
|
307
|
+
if (isFile(candidate)) {
|
|
308
|
+
return candidate;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
200
313
|
/**
|
|
201
314
|
* The module format of the entry source file, derived from the entry project's
|
|
202
315
|
* compiler options (via the runtime manifest) the same way the served files are
|
|
@@ -204,11 +317,14 @@ function installCommonJsHook() {
|
|
|
204
317
|
* `require` or an ESM `import`.
|
|
205
318
|
*/
|
|
206
319
|
function entryModuleFormat(entryFile) {
|
|
207
|
-
const
|
|
208
|
-
|
|
320
|
+
const real = realPath(entryFile);
|
|
321
|
+
const owner = findEntryEmit(real)?.manifest;
|
|
322
|
+
return moduleFormat(entryFile, owner === undefined ? null : (owner.moduleOptions ?? {})) === "module"
|
|
209
323
|
? "module"
|
|
210
324
|
: "commonjs";
|
|
211
325
|
}
|
|
326
|
+
/** TypeScript URLs resolved at a JavaScript-to-TypeScript entry boundary. */
|
|
327
|
+
const runtimeEntryUrls = new Set();
|
|
212
328
|
/**
|
|
213
329
|
* Rescue an extensionless or directory relative specifier that Node's resolver
|
|
214
330
|
* rejected. Only runs after `nextResolve` throws, so a successful resolution is
|
|
@@ -216,17 +332,344 @@ function entryModuleFormat(entryFile) {
|
|
|
216
332
|
* original error is rethrown, preserving `ERR_MODULE_NOT_FOUND`.
|
|
217
333
|
*/
|
|
218
334
|
function resolve(specifier, context, nextResolve) {
|
|
335
|
+
recordPluginDescriptorResolutionCandidates(specifier, context.parentURL);
|
|
219
336
|
try {
|
|
220
|
-
|
|
337
|
+
const result = rememberRuntimeEntry(rememberCommonJsNamedInterop(restoreStrippedNodeBuiltinScheme(specifier, nextResolve(specifier, context)), context), context);
|
|
338
|
+
recordPluginDescriptorResolution(specifier, context.parentURL, result.url);
|
|
339
|
+
return result;
|
|
221
340
|
}
|
|
222
341
|
catch (error) {
|
|
223
342
|
const rescued = probeRescuableSpecifier(specifier, context.parentURL);
|
|
224
343
|
if (rescued === null) {
|
|
225
344
|
throw error;
|
|
226
345
|
}
|
|
227
|
-
|
|
346
|
+
const result = rememberRuntimeEntry(rememberCommonJsNamedInterop({ shortCircuit: true, url: rescued }, context), context);
|
|
347
|
+
recordPluginDescriptorResolution(specifier, context.parentURL, result.url);
|
|
348
|
+
return result;
|
|
228
349
|
}
|
|
229
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Fingerprint every path whose state can redirect one descriptor import.
|
|
353
|
+
*
|
|
354
|
+
* This runs before the real resolver. Reporting candidates only after the
|
|
355
|
+
* descriptor finished would pair an earlier descriptor result with later file
|
|
356
|
+
* state when a higher-priority candidate appeared during evaluation.
|
|
357
|
+
*/
|
|
358
|
+
function recordPluginDescriptorResolutionCandidates(specifier, parentURL) {
|
|
359
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
360
|
+
return;
|
|
361
|
+
if ((0, node_module_1.isBuiltin)(specifier) || specifier.startsWith("node:"))
|
|
362
|
+
return;
|
|
363
|
+
const parent = runtimeFilePath(parentURL);
|
|
364
|
+
if (parent === undefined)
|
|
365
|
+
return;
|
|
366
|
+
const recorded = new Set();
|
|
367
|
+
const record = (candidate) => {
|
|
368
|
+
const resolved = node_path_1.default.resolve(candidate);
|
|
369
|
+
if (recorded.has(resolved))
|
|
370
|
+
return;
|
|
371
|
+
recorded.add(resolved);
|
|
372
|
+
recordPluginDescriptorInput({
|
|
373
|
+
parent,
|
|
374
|
+
resolved,
|
|
375
|
+
});
|
|
376
|
+
};
|
|
377
|
+
const recordManifestTargets = (value, directory, allowBare = false) => {
|
|
378
|
+
if (typeof value === "string") {
|
|
379
|
+
if (value !== "" &&
|
|
380
|
+
(allowBare || value.startsWith("./") || value.startsWith("../"))) {
|
|
381
|
+
recordBase(node_path_1.default.resolve(directory, value));
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (Array.isArray(value)) {
|
|
386
|
+
for (const item of value) {
|
|
387
|
+
recordManifestTargets(item, directory, allowBare);
|
|
388
|
+
}
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (value !== null && typeof value === "object") {
|
|
392
|
+
for (const item of Object.values(value)) {
|
|
393
|
+
recordManifestTargets(item, directory, allowBare);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
const recordPackageManifests = (file) => {
|
|
398
|
+
for (let directory = node_path_1.default.dirname(file);; directory = node_path_1.default.dirname(directory)) {
|
|
399
|
+
const manifest = node_path_1.default.join(directory, "package.json");
|
|
400
|
+
record(manifest);
|
|
401
|
+
if (isFile(manifest))
|
|
402
|
+
return;
|
|
403
|
+
const parentDirectory = node_path_1.default.dirname(directory);
|
|
404
|
+
if (parentDirectory === directory)
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const localBases = (value) => {
|
|
409
|
+
if (value.startsWith("file:"))
|
|
410
|
+
return [(0, node_url_1.fileURLToPath)(value)];
|
|
411
|
+
const raw = node_path_1.default.resolve(node_path_1.default.dirname(parent), value);
|
|
412
|
+
const suffixStart = value.search(/[?#]/);
|
|
413
|
+
if (suffixStart === -1)
|
|
414
|
+
return [raw];
|
|
415
|
+
const pathname = value.slice(0, suffixStart);
|
|
416
|
+
return pathname === ""
|
|
417
|
+
? [raw]
|
|
418
|
+
: [...new Set([raw, node_path_1.default.resolve(node_path_1.default.dirname(parent), pathname)])];
|
|
419
|
+
};
|
|
420
|
+
const bases = new Set();
|
|
421
|
+
const recordBase = (base) => {
|
|
422
|
+
const resolvedBase = node_path_1.default.resolve(base);
|
|
423
|
+
if (bases.has(resolvedBase))
|
|
424
|
+
return;
|
|
425
|
+
bases.add(resolvedBase);
|
|
426
|
+
record(resolvedBase);
|
|
427
|
+
for (const candidate of typescriptSourcesForJavaScriptSpecifier(resolvedBase)) {
|
|
428
|
+
record(candidate);
|
|
429
|
+
}
|
|
430
|
+
for (const extension of DESCRIPTOR_PROBE_EXTENSIONS) {
|
|
431
|
+
record(resolvedBase + extension);
|
|
432
|
+
}
|
|
433
|
+
const manifestFile = node_path_1.default.join(resolvedBase, "package.json");
|
|
434
|
+
record(manifestFile);
|
|
435
|
+
for (const extension of DESCRIPTOR_PROBE_EXTENSIONS) {
|
|
436
|
+
record(node_path_1.default.join(resolvedBase, `index${extension}`));
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const manifest = JSON.parse(node_fs_1.default.readFileSync(manifestFile, "utf8").replace(/^\uFEFF/, ""));
|
|
440
|
+
recordManifestTargets(manifest.exports, resolvedBase);
|
|
441
|
+
recordManifestTargets(manifest.module, resolvedBase, true);
|
|
442
|
+
recordManifestTargets(manifest.main, resolvedBase, true);
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// The real resolver owns malformed package diagnostics.
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
if (specifier.startsWith(".") ||
|
|
449
|
+
node_path_1.default.isAbsolute(specifier) ||
|
|
450
|
+
specifier.startsWith("file:")) {
|
|
451
|
+
try {
|
|
452
|
+
for (const base of localBases(specifier)) {
|
|
453
|
+
recordPackageManifests(base);
|
|
454
|
+
if (isFile(base))
|
|
455
|
+
record(base);
|
|
456
|
+
else
|
|
457
|
+
recordBase(base);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
// The real resolver owns invalid URL spellings.
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const parts = specifier.split("/");
|
|
466
|
+
const packageParts = parts[0]?.startsWith("@")
|
|
467
|
+
? parts.slice(0, 2)
|
|
468
|
+
: parts.slice(0, 1);
|
|
469
|
+
if (packageParts.some((part) => part === undefined || part === ""))
|
|
470
|
+
return;
|
|
471
|
+
const packageName = packageParts.join("/");
|
|
472
|
+
const subpath = parts.slice(packageParts.length);
|
|
473
|
+
for (const searchPath of (0, node_module_1.createRequire)(parent).resolve.paths(specifier) ??
|
|
474
|
+
[]) {
|
|
475
|
+
const packageDirectory = node_path_1.default.join(searchPath, packageName);
|
|
476
|
+
recordBase(packageDirectory);
|
|
477
|
+
if (subpath.length !== 0) {
|
|
478
|
+
recordBase(node_path_1.default.join(packageDirectory, ...subpath));
|
|
479
|
+
}
|
|
480
|
+
// CommonJS resolution continues past an existing but unusable package
|
|
481
|
+
// directory. Record every search root before the resolver runs so a
|
|
482
|
+
// farther selected package retains evaluation-time hashes for its
|
|
483
|
+
// superseding candidates.
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Report one resolved descriptor edge to the parent loader. The channel is
|
|
488
|
+
* armed by the generated descriptor shim only after ttsx's own bootstrap and
|
|
489
|
+
* imports have loaded, keeping compiler implementation files out of the
|
|
490
|
+
* project's persistent-cache inputs.
|
|
491
|
+
*/
|
|
492
|
+
function recordPluginDescriptorResolution(specifier, parentURL, resolvedURL) {
|
|
493
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
494
|
+
return;
|
|
495
|
+
const out = process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_OUT;
|
|
496
|
+
if (out === undefined || out.length === 0)
|
|
497
|
+
return;
|
|
498
|
+
const resolved = runtimeFilePath(resolvedURL);
|
|
499
|
+
if (resolved === undefined)
|
|
500
|
+
return;
|
|
501
|
+
const parent = runtimeFilePath(parentURL);
|
|
502
|
+
recordPluginDescriptorInput({
|
|
503
|
+
...(parent === undefined ? {} : { parent }),
|
|
504
|
+
resolved,
|
|
505
|
+
specifier,
|
|
506
|
+
});
|
|
507
|
+
for (let directory = node_path_1.default.dirname(resolved);; directory = node_path_1.default.dirname(directory)) {
|
|
508
|
+
const manifest = node_path_1.default.join(directory, "package.json");
|
|
509
|
+
recordPluginDescriptorInput({
|
|
510
|
+
...(parent === undefined ? {} : { parent }),
|
|
511
|
+
resolved: manifest,
|
|
512
|
+
});
|
|
513
|
+
if (isFile(manifest))
|
|
514
|
+
break;
|
|
515
|
+
const parentDirectory = node_path_1.default.dirname(directory);
|
|
516
|
+
if (parentDirectory === directory)
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function recordPluginDescriptorInput(record) {
|
|
521
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
522
|
+
return;
|
|
523
|
+
const out = process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_OUT;
|
|
524
|
+
if (out === undefined || out.length === 0)
|
|
525
|
+
return;
|
|
526
|
+
recordPluginDescriptorInputOnce(record);
|
|
527
|
+
const resolved = node_path_1.default.resolve(record.resolved);
|
|
528
|
+
const parsed = node_path_1.default.parse(resolved);
|
|
529
|
+
let current = parsed.root;
|
|
530
|
+
const relative = node_path_1.default.relative(parsed.root, resolved);
|
|
531
|
+
for (const segment of relative.split(node_path_1.default.sep).slice(0, -1)) {
|
|
532
|
+
if (segment === "")
|
|
533
|
+
continue;
|
|
534
|
+
current = node_path_1.default.join(current, segment);
|
|
535
|
+
try {
|
|
536
|
+
if (node_fs_1.default.lstatSync(current).isSymbolicLink()) {
|
|
537
|
+
recordPluginDescriptorInputOnce({ resolved: current });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch {
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/** Record one path without recursively revisiting its lexical ancestors. */
|
|
546
|
+
function recordPluginDescriptorInputOnce(record) {
|
|
547
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
548
|
+
return;
|
|
549
|
+
const out = process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_OUT;
|
|
550
|
+
if (out === undefined || out.length === 0)
|
|
551
|
+
return;
|
|
552
|
+
try {
|
|
553
|
+
const beforeSignature = pluginDescriptorInputMetadataSignature(record.resolved);
|
|
554
|
+
const observedHash = pluginDescriptorInputHash(record.resolved);
|
|
555
|
+
const observedRealpath = pluginDescriptorInputRealpath(record.resolved);
|
|
556
|
+
const afterSignature = pluginDescriptorInputMetadataSignature(record.resolved);
|
|
557
|
+
const unstable = record.unstable === true ||
|
|
558
|
+
beforeSignature === undefined ||
|
|
559
|
+
afterSignature === undefined ||
|
|
560
|
+
beforeSignature !== afterSignature ||
|
|
561
|
+
(record.hash !== undefined && record.hash !== observedHash) ||
|
|
562
|
+
(record.realpath !== undefined && record.realpath !== observedRealpath) ||
|
|
563
|
+
(record.signature !== undefined && record.signature !== afterSignature);
|
|
564
|
+
node_fs_1.default.appendFileSync(out, `${JSON.stringify({
|
|
565
|
+
...record,
|
|
566
|
+
hash: observedHash,
|
|
567
|
+
realpath: observedRealpath,
|
|
568
|
+
...(unstable ? { unstable: true } : { signature: afterSignature }),
|
|
569
|
+
})}\n`, "utf8");
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
// Dependency reporting is advisory to cache reuse; the selected entry is
|
|
573
|
+
// still retained by the parent if this best-effort side channel fails.
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function pluginDescriptorInputRealpath(file) {
|
|
577
|
+
try {
|
|
578
|
+
return node_fs_1.default.realpathSync.native(file);
|
|
579
|
+
}
|
|
580
|
+
catch {
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/** Metadata identity that exposes content-preserving A-B-A replacement. */
|
|
585
|
+
function pluginDescriptorInputMetadataSignature(file) {
|
|
586
|
+
const requested = node_path_1.default.resolve(file);
|
|
587
|
+
let current = requested;
|
|
588
|
+
for (;;) {
|
|
589
|
+
try {
|
|
590
|
+
const link = node_fs_1.default.lstatSync(current, { bigint: true });
|
|
591
|
+
let target = link;
|
|
592
|
+
if (link.isSymbolicLink()) {
|
|
593
|
+
try {
|
|
594
|
+
target = node_fs_1.default.statSync(current, { bigint: true });
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
// A broken link's own metadata cannot expose its target appearing
|
|
598
|
+
// and disappearing during evaluation. Decline cache proof instead.
|
|
599
|
+
return undefined;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return [
|
|
603
|
+
node_path_1.default.relative(current, requested),
|
|
604
|
+
link.dev,
|
|
605
|
+
link.ino,
|
|
606
|
+
link.mode,
|
|
607
|
+
link.size,
|
|
608
|
+
link.mtimeNs,
|
|
609
|
+
link.ctimeNs,
|
|
610
|
+
target.dev,
|
|
611
|
+
target.ino,
|
|
612
|
+
target.mode,
|
|
613
|
+
target.size,
|
|
614
|
+
target.mtimeNs,
|
|
615
|
+
target.ctimeNs,
|
|
616
|
+
].join(":");
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
if (!isMissingPathError(error))
|
|
620
|
+
return undefined;
|
|
621
|
+
const parent = node_path_1.default.dirname(current);
|
|
622
|
+
if (parent === current)
|
|
623
|
+
return undefined;
|
|
624
|
+
current = parent;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
function pluginDescriptorInputHash(file) {
|
|
629
|
+
try {
|
|
630
|
+
if (node_fs_1.default.statSync(file).isDirectory()) {
|
|
631
|
+
return pluginDescriptorDirectoryHash();
|
|
632
|
+
}
|
|
633
|
+
return node_crypto_1.default
|
|
634
|
+
.createHash("sha256")
|
|
635
|
+
.update(node_fs_1.default.readFileSync(file))
|
|
636
|
+
.digest("hex");
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
/** Stable public fingerprint for an existing directory candidate's kind. */
|
|
643
|
+
function pluginDescriptorDirectoryHash() {
|
|
644
|
+
return node_crypto_1.default
|
|
645
|
+
.createHash("sha256")
|
|
646
|
+
.update("ttsc:host-input:directory\0")
|
|
647
|
+
.digest("hex");
|
|
648
|
+
}
|
|
649
|
+
function runtimeFilePath(value) {
|
|
650
|
+
if (value === undefined)
|
|
651
|
+
return undefined;
|
|
652
|
+
if (value.startsWith("file:")) {
|
|
653
|
+
try {
|
|
654
|
+
return node_path_1.default.resolve((0, node_url_1.fileURLToPath)(value));
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
return undefined;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return node_path_1.default.isAbsolute(value) ? node_path_1.default.resolve(value) : undefined;
|
|
661
|
+
}
|
|
662
|
+
/** Remember an ESM root until its synchronous load hook prepares the project. */
|
|
663
|
+
function rememberRuntimeEntry(result, context) {
|
|
664
|
+
if (result.url.startsWith("file:") &&
|
|
665
|
+
isTypeScriptSource((0, node_url_1.fileURLToPath)(result.url)) &&
|
|
666
|
+
(context.parentURL === undefined ||
|
|
667
|
+
!context.parentURL.startsWith("file:") ||
|
|
668
|
+
!isTypeScriptSource((0, node_url_1.fileURLToPath)(context.parentURL)))) {
|
|
669
|
+
runtimeEntryUrls.add(result.url);
|
|
670
|
+
}
|
|
671
|
+
return result;
|
|
672
|
+
}
|
|
230
673
|
/**
|
|
231
674
|
* Restore a `node:` builtin URL when affected Node releases return the exact
|
|
232
675
|
* prefix-stripped spelling from their synchronous CommonJS resolver.
|
|
@@ -244,6 +687,12 @@ function restoreStrippedNodeBuiltinScheme(specifier, result) {
|
|
|
244
687
|
: result;
|
|
245
688
|
}
|
|
246
689
|
const builtProjects = new Map();
|
|
690
|
+
// A descriptor evaluator's dependency emit must never be reused by another
|
|
691
|
+
// process. PIDs are eventually recycled while the disk cache persists, so PID
|
|
692
|
+
// alone cannot provide that isolation. One cryptographically random process
|
|
693
|
+
// nonce keeps every evaluator generation distinct while `builtProjects` still
|
|
694
|
+
// shares repeated imports inside this process.
|
|
695
|
+
const descriptorProcessCacheNonce = node_crypto_1.default.randomBytes(16).toString("hex");
|
|
247
696
|
/** File URLs whose CommonJS source was reached from an ESM parent import. */
|
|
248
697
|
const commonJsNamedInteropUrls = new Set();
|
|
249
698
|
const commonJsNameScanSources = new Map();
|
|
@@ -255,15 +704,15 @@ function load(url, context, nextLoad) {
|
|
|
255
704
|
if (!isTypeScriptSource(filename)) {
|
|
256
705
|
return nextLoad(url, context);
|
|
257
706
|
}
|
|
258
|
-
const { format, source } = resolveRuntimeSource(filename, url);
|
|
707
|
+
const { format, source } = resolveRuntimeSource(filename, url, runtimeEntryUrls.delete(url) || isProcessEntry(filename));
|
|
259
708
|
return {
|
|
260
709
|
format,
|
|
261
710
|
shortCircuit: true,
|
|
262
711
|
source,
|
|
263
712
|
};
|
|
264
713
|
}
|
|
265
|
-
function resolveRuntimeSource(filename, url = (0, node_url_1.pathToFileURL)(filename).href) {
|
|
266
|
-
const served = resolveServedSource(filename, url);
|
|
714
|
+
function resolveRuntimeSource(filename, url = (0, node_url_1.pathToFileURL)(filename).href, prepareAsEntry = false) {
|
|
715
|
+
const served = resolveServedSource(filename, url, prepareAsEntry);
|
|
267
716
|
const format = moduleFormat(filename, served.moduleOptions);
|
|
268
717
|
return {
|
|
269
718
|
format,
|
|
@@ -272,6 +721,13 @@ function resolveRuntimeSource(filename, url = (0, node_url_1.pathToFileURL)(file
|
|
|
272
721
|
: served.source,
|
|
273
722
|
};
|
|
274
723
|
}
|
|
724
|
+
/** Whether `filename` is the TypeScript main module named on Node's argv. */
|
|
725
|
+
function isProcessEntry(filename) {
|
|
726
|
+
const entry = process.argv[1];
|
|
727
|
+
return (entry !== undefined &&
|
|
728
|
+
isTypeScriptSource(entry) &&
|
|
729
|
+
realPath(node_path_1.default.resolve(entry)) === realPath(filename));
|
|
730
|
+
}
|
|
275
731
|
function rememberCommonJsNamedInterop(result, context) {
|
|
276
732
|
if (shouldExposeCommonJsNamedExports(result.url, context.parentURL)) {
|
|
277
733
|
commonJsNamedInteropUrls.add(result.url);
|
|
@@ -316,9 +772,9 @@ function owningModuleOptions(filename) {
|
|
|
316
772
|
return null;
|
|
317
773
|
}
|
|
318
774
|
const real = realPath(filename);
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
321
|
-
return
|
|
775
|
+
const owner = findEntryEmit(real)?.manifest;
|
|
776
|
+
if (owner !== undefined) {
|
|
777
|
+
return owner.moduleOptions ?? {};
|
|
322
778
|
}
|
|
323
779
|
const tsconfig = nearestTsconfig(real);
|
|
324
780
|
if (tsconfig === null) {
|
|
@@ -330,10 +786,7 @@ function owningModuleOptions(filename) {
|
|
|
330
786
|
}
|
|
331
787
|
let options = {};
|
|
332
788
|
try {
|
|
333
|
-
const project = (
|
|
334
|
-
cwd: node_path_1.default.dirname(tsconfig),
|
|
335
|
-
tsconfig,
|
|
336
|
-
});
|
|
789
|
+
const project = readPluginDescriptorProjectConfig(tsconfig);
|
|
337
790
|
options = projectModuleOptions(project.compilerOptions);
|
|
338
791
|
}
|
|
339
792
|
catch {
|
|
@@ -347,6 +800,108 @@ function owningModuleOptions(filename) {
|
|
|
347
800
|
moduleOptionsCache.set(tsconfig, options);
|
|
348
801
|
return options;
|
|
349
802
|
}
|
|
803
|
+
/**
|
|
804
|
+
* Read one owning config while its discovered chain stays unchanged.
|
|
805
|
+
*
|
|
806
|
+
* A preliminary read discovers `extends`; an accepted read is bracketed by
|
|
807
|
+
* equal fingerprints over that exact chain. If churn never settles, ttsx can
|
|
808
|
+
* still execute the last project but reports every observed config as unstable,
|
|
809
|
+
* preventing a later snapshot from certifying the torn result.
|
|
810
|
+
*/
|
|
811
|
+
function readPluginDescriptorProjectConfig(tsconfig) {
|
|
812
|
+
const read = () => (0, readProjectConfig_1.readProjectConfig)({ cwd: node_path_1.default.dirname(tsconfig), tsconfig });
|
|
813
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
814
|
+
return read();
|
|
815
|
+
const observed = new Set([node_path_1.default.resolve(tsconfig)]);
|
|
816
|
+
let project;
|
|
817
|
+
try {
|
|
818
|
+
project = read();
|
|
819
|
+
}
|
|
820
|
+
catch (error) {
|
|
821
|
+
recordPluginDescriptorProjectInputs(observed, true);
|
|
822
|
+
throw error;
|
|
823
|
+
}
|
|
824
|
+
let inputs = normalizedProjectConfigPaths(project, tsconfig);
|
|
825
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
826
|
+
for (const input of inputs)
|
|
827
|
+
observed.add(input);
|
|
828
|
+
const before = pluginDescriptorInputHashes(inputs);
|
|
829
|
+
const beforeRealpaths = pluginDescriptorInputRealpaths(inputs);
|
|
830
|
+
const beforeSignatures = pluginDescriptorInputMetadataSignatures(inputs);
|
|
831
|
+
let candidate;
|
|
832
|
+
try {
|
|
833
|
+
candidate = read();
|
|
834
|
+
}
|
|
835
|
+
catch (error) {
|
|
836
|
+
recordPluginDescriptorProjectInputs(observed, true);
|
|
837
|
+
throw error;
|
|
838
|
+
}
|
|
839
|
+
const candidateInputs = normalizedProjectConfigPaths(candidate, tsconfig);
|
|
840
|
+
for (const input of candidateInputs)
|
|
841
|
+
observed.add(input);
|
|
842
|
+
const after = pluginDescriptorInputHashes(candidateInputs);
|
|
843
|
+
const afterRealpaths = pluginDescriptorInputRealpaths(candidateInputs);
|
|
844
|
+
const afterSignatures = pluginDescriptorInputMetadataSignatures(candidateInputs);
|
|
845
|
+
if (equalPluginDescriptorInputLists(inputs, candidateInputs) &&
|
|
846
|
+
equalPluginDescriptorInputHashes(before, after) &&
|
|
847
|
+
equalPluginDescriptorInputHashes(beforeRealpaths, afterRealpaths) &&
|
|
848
|
+
beforeSignatures !== undefined &&
|
|
849
|
+
afterSignatures !== undefined &&
|
|
850
|
+
equalPluginDescriptorInputHashes(beforeSignatures, afterSignatures)) {
|
|
851
|
+
for (const input of candidateInputs) {
|
|
852
|
+
recordPluginDescriptorInput({
|
|
853
|
+
hash: after[input],
|
|
854
|
+
realpath: afterRealpaths[input],
|
|
855
|
+
resolved: input,
|
|
856
|
+
signature: afterSignatures[input],
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return candidate;
|
|
860
|
+
}
|
|
861
|
+
project = candidate;
|
|
862
|
+
inputs = candidateInputs;
|
|
863
|
+
}
|
|
864
|
+
recordPluginDescriptorProjectInputs(observed, true);
|
|
865
|
+
return project;
|
|
866
|
+
}
|
|
867
|
+
function normalizedProjectConfigPaths(project, requestedConfig) {
|
|
868
|
+
return [
|
|
869
|
+
...new Set([requestedConfig, ...project.configPaths].map((file) => node_path_1.default.resolve(file))),
|
|
870
|
+
].sort();
|
|
871
|
+
}
|
|
872
|
+
function pluginDescriptorInputHashes(inputs) {
|
|
873
|
+
return Object.fromEntries(inputs.map((input) => [input, pluginDescriptorInputHash(input)]));
|
|
874
|
+
}
|
|
875
|
+
function pluginDescriptorInputRealpaths(inputs) {
|
|
876
|
+
return Object.fromEntries(inputs.map((input) => [input, pluginDescriptorInputRealpath(input)]));
|
|
877
|
+
}
|
|
878
|
+
function pluginDescriptorInputMetadataSignatures(inputs) {
|
|
879
|
+
const output = {};
|
|
880
|
+
for (const input of inputs) {
|
|
881
|
+
const signature = pluginDescriptorInputMetadataSignature(input);
|
|
882
|
+
if (signature === undefined)
|
|
883
|
+
return undefined;
|
|
884
|
+
output[node_path_1.default.resolve(input)] = signature;
|
|
885
|
+
}
|
|
886
|
+
return output;
|
|
887
|
+
}
|
|
888
|
+
function equalPluginDescriptorInputLists(first, second) {
|
|
889
|
+
return (first.length === second.length &&
|
|
890
|
+
first.every((input, index) => input === second[index]));
|
|
891
|
+
}
|
|
892
|
+
function equalPluginDescriptorInputHashes(first, second) {
|
|
893
|
+
return (Object.keys(first).length === Object.keys(second).length &&
|
|
894
|
+
Object.entries(first).every(([input, hash]) => second[input] === hash));
|
|
895
|
+
}
|
|
896
|
+
function recordPluginDescriptorProjectInputs(inputs, unstable) {
|
|
897
|
+
for (const input of inputs) {
|
|
898
|
+
const resolved = node_path_1.default.resolve(input);
|
|
899
|
+
recordPluginDescriptorInput({
|
|
900
|
+
resolved,
|
|
901
|
+
...(unstable ? { unstable: true } : {}),
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
350
905
|
/** Narrow a resolved project's compiler options to the emit-format pair. */
|
|
351
906
|
function projectModuleOptions(compilerOptions) {
|
|
352
907
|
return {
|
|
@@ -365,12 +920,31 @@ function projectModuleOptions(compilerOptions) {
|
|
|
365
920
|
* "transform"` type-strip. Shared by the ESM `load` hook and the CommonJS
|
|
366
921
|
* `require` handler.
|
|
367
922
|
*/
|
|
368
|
-
function resolveServedSource(filename, url = (0, node_url_1.pathToFileURL)(filename).href) {
|
|
923
|
+
function resolveServedSource(filename, url = (0, node_url_1.pathToFileURL)(filename).href, prepareAsEntry = false) {
|
|
369
924
|
const real = realPath(filename);
|
|
370
|
-
|
|
925
|
+
// Only the public preload can prepare a newly discovered root. Direct ttsx
|
|
926
|
+
// has one pre-built manifest and historically relies on trailing-stem
|
|
927
|
+
// recovery when Windows presents the same source through its short and long
|
|
928
|
+
// temp-path spellings (the lint TypeScript-config loader is one such case).
|
|
929
|
+
// Treating that boundary as prepare-only would discard the existing emit and
|
|
930
|
+
// feed ESM source into CommonJS interop, producing ERR_REQUIRE_CYCLE_MODULE.
|
|
931
|
+
const prepareEntry = prepareAsEntry ? prepareRuntimeEntry : undefined;
|
|
932
|
+
// A JavaScript-to-TypeScript boundary is a new checked root unless an
|
|
933
|
+
// existing manifest proves exact ownership. Trailing-stem recovery is not
|
|
934
|
+
// ownership evidence: two out-of-include `index.ts` roots can otherwise map
|
|
935
|
+
// to the first manifest's `index.js`, skipping the second root's diagnostics.
|
|
936
|
+
let served = serveEntryEmit(real, prepareEntry === undefined);
|
|
371
937
|
if (served !== null) {
|
|
372
938
|
return withInlineSourceMap(served);
|
|
373
939
|
}
|
|
940
|
+
if (prepareEntry !== undefined) {
|
|
941
|
+
registeredManifests.push(prepareEntry(real));
|
|
942
|
+
served = serveEntryEmit(real);
|
|
943
|
+
if (served === null) {
|
|
944
|
+
throw new Error(`ttsx: prepared entry emit not found for ${filename}`);
|
|
945
|
+
}
|
|
946
|
+
return withInlineSourceMap(served);
|
|
947
|
+
}
|
|
374
948
|
const built = serveDependencyEmit(real);
|
|
375
949
|
if (built !== null) {
|
|
376
950
|
return withInlineSourceMap(built);
|
|
@@ -447,7 +1021,7 @@ function emitOrphanAsCommonJs(filename) {
|
|
|
447
1021
|
return hit;
|
|
448
1022
|
}
|
|
449
1023
|
}
|
|
450
|
-
const outDir =
|
|
1024
|
+
const outDir = (0, createCanonicalTempDirectory_1.createCanonicalTempDirectory)("ttsx-orphan-");
|
|
451
1025
|
try {
|
|
452
1026
|
const res = (0, spawnNative_1.spawnNative)(tsgo, [
|
|
453
1027
|
filename,
|
|
@@ -508,7 +1082,7 @@ function emitCommonJsForNameScan(filename) {
|
|
|
508
1082
|
commonJsNameScanSources.set(real, null);
|
|
509
1083
|
return null;
|
|
510
1084
|
}
|
|
511
|
-
const outDir =
|
|
1085
|
+
const outDir = (0, createCanonicalTempDirectory_1.createCanonicalTempDirectory)("ttsx-export-scan-");
|
|
512
1086
|
try {
|
|
513
1087
|
const res = (0, spawnNative_1.spawnNative)(tsgo, [
|
|
514
1088
|
real,
|
|
@@ -936,54 +1510,93 @@ function isIdentifierName(name) {
|
|
|
936
1510
|
* `rootDir` cannot have a mirrored emit, so it falls through to the dependency
|
|
937
1511
|
* paths.
|
|
938
1512
|
*/
|
|
939
|
-
function serveEntryEmit(real) {
|
|
940
|
-
const
|
|
941
|
-
if (
|
|
942
|
-
return null;
|
|
943
|
-
}
|
|
944
|
-
const emitted = entryEmitPath(m, real);
|
|
945
|
-
if (emitted === null) {
|
|
1513
|
+
function serveEntryEmit(real, allowStemFallback = true) {
|
|
1514
|
+
const owner = findEntryEmit(real, allowStemFallback);
|
|
1515
|
+
if (owner === null) {
|
|
946
1516
|
return null;
|
|
947
1517
|
}
|
|
948
|
-
const source = readFileOrNull(
|
|
1518
|
+
const source = readFileOrNull(owner.emittedFile);
|
|
949
1519
|
return source === null
|
|
950
1520
|
? null
|
|
951
1521
|
: {
|
|
952
|
-
emittedFile:
|
|
953
|
-
moduleOptions:
|
|
1522
|
+
emittedFile: owner.emittedFile,
|
|
1523
|
+
moduleOptions: owner.manifest.moduleOptions ?? {},
|
|
954
1524
|
source,
|
|
955
1525
|
sourceFile: real,
|
|
956
1526
|
};
|
|
957
1527
|
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Find the manifest that owns `real`. Explicit prepared roots win, followed by
|
|
1530
|
+
* exact mirrored paths across every manifest. Only then may legacy stem
|
|
1531
|
+
* recovery run, so one manifest's approximate match cannot shadow another's
|
|
1532
|
+
* exact emit.
|
|
1533
|
+
*/
|
|
1534
|
+
function findEntryEmit(real, allowStemFallback = true) {
|
|
1535
|
+
const manifests = runtimeManifests();
|
|
1536
|
+
for (const candidate of manifests) {
|
|
1537
|
+
if (candidate.entrySource !== undefined &&
|
|
1538
|
+
candidate.entryFile !== undefined &&
|
|
1539
|
+
realPath(candidate.entrySource) === real &&
|
|
1540
|
+
node_fs_1.default.existsSync(candidate.entryFile)) {
|
|
1541
|
+
return { emittedFile: candidate.entryFile, manifest: candidate };
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
for (const candidate of manifests) {
|
|
1545
|
+
const emitted = entryEmitPath(candidate, real, false);
|
|
1546
|
+
if (emitted !== null) {
|
|
1547
|
+
return { emittedFile: emitted, manifest: candidate };
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (!allowStemFallback) {
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
for (const candidate of manifests) {
|
|
1554
|
+
const emitted = entryEmitPath(candidate, real, true);
|
|
1555
|
+
if (emitted !== null) {
|
|
1556
|
+
return { emittedFile: emitted, manifest: candidate };
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
958
1561
|
/**
|
|
959
1562
|
* The entry project's emitted JavaScript for `real`, or `null` when that
|
|
960
1563
|
* project did not emit it. Shared with `owningModuleOptions` so "the entry
|
|
961
1564
|
* project owns this file" means exactly one thing in both places.
|
|
962
1565
|
*/
|
|
963
|
-
function entryEmitPath(m, real) {
|
|
964
|
-
const
|
|
965
|
-
|
|
966
|
-
|
|
1566
|
+
function entryEmitPath(m, real, allowStemFallback = true) {
|
|
1567
|
+
const cache = allowStemFallback
|
|
1568
|
+
? entryEmitPathCache
|
|
1569
|
+
: exactEntryEmitPathCache;
|
|
1570
|
+
let manifestCache = cache.get(m);
|
|
1571
|
+
if (manifestCache === undefined) {
|
|
1572
|
+
manifestCache = new Map();
|
|
1573
|
+
cache.set(m, manifestCache);
|
|
1574
|
+
}
|
|
1575
|
+
if (manifestCache.has(real)) {
|
|
1576
|
+
return manifestCache.get(real) ?? null;
|
|
967
1577
|
}
|
|
968
1578
|
const resolved = isWithin(real, m.rootDir)
|
|
969
1579
|
? (0, resolveEmittedJavaScript_1.resolveEmittedJavaScript)({
|
|
1580
|
+
allowStemFallback,
|
|
970
1581
|
emittedFiles: m.emittedFiles,
|
|
971
1582
|
outDir: m.emitDir,
|
|
972
1583
|
projectRoot: m.rootDir,
|
|
973
1584
|
sourceFile: real,
|
|
974
1585
|
})
|
|
975
1586
|
: null;
|
|
976
|
-
|
|
1587
|
+
manifestCache.set(real, resolved);
|
|
977
1588
|
return resolved;
|
|
978
1589
|
}
|
|
979
1590
|
/**
|
|
980
1591
|
* Memo for `entryEmitPath`, because it is now on the `resolve` hook's path
|
|
981
1592
|
* through `owningModuleOptions` — once per import specifier — and a miss inside
|
|
982
1593
|
* `resolveEmittedJavaScript` walks the whole emit tree. The entry emit is
|
|
983
|
-
* written once before the run starts and never changes under it
|
|
984
|
-
*
|
|
1594
|
+
* written once before the run starts and never changes under it. Registration
|
|
1595
|
+
* can add several independent entry emits, so the manifest is part of the cache
|
|
1596
|
+
* identity as well as the source path.
|
|
985
1597
|
*/
|
|
986
|
-
const entryEmitPathCache = new
|
|
1598
|
+
const entryEmitPathCache = new WeakMap();
|
|
1599
|
+
const exactEntryEmitPathCache = new WeakMap();
|
|
987
1600
|
/**
|
|
988
1601
|
* True when `real` is `directory` itself or sits beneath it. Handles a root
|
|
989
1602
|
* `directory` (`/`, `C:\`): naively appending a separator would yield `//`,
|
|
@@ -1075,11 +1688,7 @@ function ensureProjectBuilt(tsconfig) {
|
|
|
1075
1688
|
return built;
|
|
1076
1689
|
}
|
|
1077
1690
|
function dependencyCachePaths(tsconfig) {
|
|
1078
|
-
const key =
|
|
1079
|
-
.createHash("sha256")
|
|
1080
|
-
.update(tsconfig)
|
|
1081
|
-
.digest("hex")
|
|
1082
|
-
.slice(0, 16);
|
|
1691
|
+
const key = dependencyCacheKey(tsconfig);
|
|
1083
1692
|
const root = dependencyCacheRoot();
|
|
1084
1693
|
return {
|
|
1085
1694
|
cacheDir: node_path_1.default.join(root, key),
|
|
@@ -1088,6 +1697,24 @@ function dependencyCachePaths(tsconfig) {
|
|
|
1088
1697
|
root,
|
|
1089
1698
|
};
|
|
1090
1699
|
}
|
|
1700
|
+
/** Derive one dependency cache key; exported for isolation regressions. */
|
|
1701
|
+
function dependencyCacheKey(tsconfig, options = {}) {
|
|
1702
|
+
const descriptorLoad = options.descriptorLoad ?? process.env.TTSC_PLUGIN_DESCRIPTOR_LOAD === "1";
|
|
1703
|
+
return (node_crypto_1.default
|
|
1704
|
+
.createHash("sha256")
|
|
1705
|
+
.update(tsconfig)
|
|
1706
|
+
// Descriptor evaluation promises a result bound to this process's exact
|
|
1707
|
+
// input observations. Reusing an emit another evaluator built can pair
|
|
1708
|
+
// that process's old source/config bytes with this process's later hashes.
|
|
1709
|
+
// Keep ordinary ttsx worker sharing, but isolate descriptor builds with a
|
|
1710
|
+
// non-reusable process nonce; the in-process `builtProjects` map still
|
|
1711
|
+
// compiles each owning project once.
|
|
1712
|
+
.update(descriptorLoad
|
|
1713
|
+
? `\0descriptor-process:${options.descriptorNonce ?? descriptorProcessCacheNonce}`
|
|
1714
|
+
: "")
|
|
1715
|
+
.digest("hex")
|
|
1716
|
+
.slice(0, 16));
|
|
1717
|
+
}
|
|
1091
1718
|
/** The immutable emit directory of one build generation under `cacheDir`. */
|
|
1092
1719
|
function dependencyGenerationDir(cacheDir, generation) {
|
|
1093
1720
|
return node_path_1.default.join(cacheDir, `gen-${generation}`);
|
|
@@ -1216,7 +1843,7 @@ function sleepSync(ms) {
|
|
|
1216
1843
|
* never be reused.
|
|
1217
1844
|
*/
|
|
1218
1845
|
function buildDependency(tsconfig, cacheDir, metaPath) {
|
|
1219
|
-
const project = (
|
|
1846
|
+
const project = readPluginDescriptorProjectConfig(tsconfig);
|
|
1220
1847
|
const generation = newDependencyGeneration();
|
|
1221
1848
|
const emitDir = dependencyGenerationDir(cacheDir, generation);
|
|
1222
1849
|
node_fs_1.default.rmSync(emitDir, { force: true, recursive: true });
|
|
@@ -1303,10 +1930,19 @@ function publishDependencyMeta(metaPath, meta) {
|
|
|
1303
1930
|
throw error;
|
|
1304
1931
|
}
|
|
1305
1932
|
}
|
|
1306
|
-
function dependencyCacheRoot() {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1933
|
+
function dependencyCacheRoot(env = process.env) {
|
|
1934
|
+
// Descriptor evaluators are disposable and must not leave one isolated emit
|
|
1935
|
+
// generation in the shared temp cache per load. Their result file already
|
|
1936
|
+
// lives in the evaluator-owned directory that the parent removes in
|
|
1937
|
+
// `finally`; put dependency emits beside it so that cleanup owns both.
|
|
1938
|
+
if (env.TTSC_PLUGIN_DESCRIPTOR_LOAD === "1" &&
|
|
1939
|
+
typeof env.TTSC_PLUGIN_DESCRIPTOR_OUT === "string" &&
|
|
1940
|
+
node_path_1.default.isAbsolute(env.TTSC_PLUGIN_DESCRIPTOR_OUT)) {
|
|
1941
|
+
return node_path_1.default.join(node_path_1.default.dirname(env.TTSC_PLUGIN_DESCRIPTOR_OUT), "dependency-cache");
|
|
1942
|
+
}
|
|
1943
|
+
const owner = runtimeManifests().find((candidate) => candidate.depCacheDir.length !== 0);
|
|
1944
|
+
return owner !== undefined
|
|
1945
|
+
? owner.depCacheDir
|
|
1310
1946
|
: node_path_1.default.join(node_os_1.default.tmpdir(), "ttsx-dep");
|
|
1311
1947
|
}
|
|
1312
1948
|
// -----------------------------------------------------------------------------
|
|
@@ -1588,7 +2224,6 @@ function formatDuration(ms) {
|
|
|
1588
2224
|
const remainder = seconds % 60;
|
|
1589
2225
|
return minutes === 0 ? `${seconds}s` : `${minutes}m ${remainder}s`;
|
|
1590
2226
|
}
|
|
1591
|
-
/** Owning-tsconfig cache keyed by directory, mirroring `packageTypeCache`. */
|
|
1592
2227
|
const tsconfigCache = new Map();
|
|
1593
2228
|
const moduleOptionsCache = new Map();
|
|
1594
2229
|
/**
|
|
@@ -1609,13 +2244,30 @@ function nearestTsconfig(file) {
|
|
|
1609
2244
|
for (;;) {
|
|
1610
2245
|
const cached = tsconfigCache.get(directory);
|
|
1611
2246
|
if (cached !== undefined) {
|
|
1612
|
-
|
|
2247
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1") {
|
|
2248
|
+
return rememberTsconfig(chain, cached.result, cached.candidates);
|
|
2249
|
+
}
|
|
2250
|
+
// Bracket the cached selection with the same candidate observations the
|
|
2251
|
+
// parent later reconciles. A nearer config created between a liveness
|
|
2252
|
+
// check and reporting must conflict, not be paired with the cached
|
|
2253
|
+
// farther result and certified as current.
|
|
2254
|
+
recordPluginDescriptorTsconfigCandidates(cached.candidates);
|
|
2255
|
+
const current = nearestExistingTsconfig(cached.candidates);
|
|
2256
|
+
recordPluginDescriptorTsconfigCandidates(cached.candidates);
|
|
2257
|
+
if (current === cached.result) {
|
|
2258
|
+
return rememberTsconfig(chain, cached.result, cached.candidates);
|
|
2259
|
+
}
|
|
2260
|
+
// Descriptor factories can deliberately create a config before a lazy
|
|
2261
|
+
// import. A lookup cached while an earlier import ran must not keep
|
|
2262
|
+
// serving the orphan lane after the nearest candidate changed.
|
|
2263
|
+
tsconfigCache.delete(directory);
|
|
1613
2264
|
}
|
|
1614
2265
|
if (node_path_1.default.basename(directory) === "node_modules") {
|
|
1615
2266
|
return rememberTsconfig(chain, null);
|
|
1616
2267
|
}
|
|
1617
2268
|
chain.push(directory);
|
|
1618
2269
|
const candidate = node_path_1.default.join(directory, "tsconfig.json");
|
|
2270
|
+
recordPluginDescriptorTsconfigCandidates([candidate]);
|
|
1619
2271
|
if (isFile(candidate)) {
|
|
1620
2272
|
return rememberTsconfig(chain, candidate);
|
|
1621
2273
|
}
|
|
@@ -1626,12 +2278,33 @@ function nearestTsconfig(file) {
|
|
|
1626
2278
|
directory = parent;
|
|
1627
2279
|
}
|
|
1628
2280
|
}
|
|
1629
|
-
function
|
|
1630
|
-
for (const
|
|
1631
|
-
|
|
2281
|
+
function nearestExistingTsconfig(candidates) {
|
|
2282
|
+
for (const candidate of candidates) {
|
|
2283
|
+
if (isFile(candidate))
|
|
2284
|
+
return node_path_1.default.resolve(candidate);
|
|
2285
|
+
}
|
|
2286
|
+
return null;
|
|
2287
|
+
}
|
|
2288
|
+
function rememberTsconfig(directories, result, tailCandidates = []) {
|
|
2289
|
+
let candidates = [...tailCandidates];
|
|
2290
|
+
for (let index = directories.length - 1; index >= 0; index -= 1) {
|
|
2291
|
+
const directory = directories[index];
|
|
2292
|
+
candidates = [node_path_1.default.join(directory, "tsconfig.json"), ...candidates];
|
|
2293
|
+
tsconfigCache.set(directory, { candidates, result });
|
|
1632
2294
|
}
|
|
1633
2295
|
return result;
|
|
1634
2296
|
}
|
|
2297
|
+
/** Report every owning-config candidate observed by the nearest-config walk. */
|
|
2298
|
+
function recordPluginDescriptorTsconfigCandidates(candidates) {
|
|
2299
|
+
if (process.env.TTSC_PLUGIN_DESCRIPTOR_INPUTS_ACTIVE !== "1")
|
|
2300
|
+
return;
|
|
2301
|
+
for (const candidate of candidates) {
|
|
2302
|
+
const resolved = node_path_1.default.resolve(candidate);
|
|
2303
|
+
recordPluginDescriptorInput({
|
|
2304
|
+
resolved,
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
1635
2308
|
function readFileOrNull(file) {
|
|
1636
2309
|
if (file === null) {
|
|
1637
2310
|
return null;
|
|
@@ -1730,6 +2403,15 @@ const JS_TO_TS_EXTENSIONS = new Map([
|
|
|
1730
2403
|
[".mjs", [".mts"]],
|
|
1731
2404
|
[".cjs", [".cts"]],
|
|
1732
2405
|
]);
|
|
2406
|
+
/** Candidate sources that can satisfy one missing JavaScript spelling. */
|
|
2407
|
+
function typescriptSourcesForJavaScriptSpecifier(file) {
|
|
2408
|
+
const extension = node_path_1.default.extname(file).toLowerCase();
|
|
2409
|
+
const substitutions = JS_TO_TS_EXTENSIONS.get(extension);
|
|
2410
|
+
if (substitutions === undefined)
|
|
2411
|
+
return [];
|
|
2412
|
+
const stem = file.slice(0, file.length - extension.length);
|
|
2413
|
+
return substitutions.map((candidate) => stem + candidate);
|
|
2414
|
+
}
|
|
1733
2415
|
/**
|
|
1734
2416
|
* Rescue a `specifier` that Node's resolver rejected: map a JavaScript
|
|
1735
2417
|
* extension back to its TypeScript source, or probe candidate extensions /
|