ttsc 0.31.6 → 0.32.1
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/cmd/ttsc/compiler_argument_cwd_test.go +38 -38
- package/cmd/ttscserver/main.go +2 -12
- package/cmd/ttscserver/resolve_launch_uses_native_flag_authority_test.go +7 -7
- package/driver/lsp.go +4 -43
- package/driver/rewrite.go +1 -1
- package/internal/e2etrace/completion_hints.go +3 -3
- package/internal/e2etrace/completion_hints_test.go +26 -12
- package/internal/graph/build.go +5 -6
- package/internal/graph/graph.go +3 -3
- package/internal/graph/line_model.go +2 -35
- package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +13 -36
- package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +9 -3
- package/internal/lspserver/lsp_plugin.go +19 -0
- package/internal/lspserver/lsp_proxy.go +9 -129
- package/internal/lspserver/lsp_server.go +0 -8
- package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +6 -36
- package/internal/sourceprocess/windows_target_owns_suspended_admission_test.go +5 -5
- package/lib/TtscCompiler.js +1 -1
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/ResidentCheckProcess.d.ts +74 -96
- package/lib/compiler/internal/ResidentCheckProcess.js +150 -335
- package/lib/compiler/internal/ResidentCheckProcess.js.map +1 -1
- package/lib/compiler/internal/ResidentCheckProcessHost.d.ts +127 -0
- package/lib/compiler/internal/ResidentCheckProcessHost.js +447 -0
- package/lib/compiler/internal/ResidentCheckProcessHost.js.map +1 -0
- package/lib/compiler/internal/ResidentCheckProcessProtocol.d.ts +64 -0
- package/lib/compiler/internal/ResidentCheckProcessProtocol.js +3 -0
- package/lib/compiler/internal/ResidentCheckProcessProtocol.js.map +1 -0
- package/lib/compiler/internal/build/BuildExecution.d.ts +103 -1
- package/lib/compiler/internal/build/BuildExecution.js +92 -12
- package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
- package/lib/compiler/internal/build/ResidentCheckWatchSession.d.ts +16 -11
- package/lib/compiler/internal/build/ResidentCheckWatchSession.js +37 -21
- package/lib/compiler/internal/build/ResidentCheckWatchSession.js.map +1 -1
- package/lib/compiler/internal/build/RunBuildOptions.d.ts +6 -5
- package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
- package/lib/compiler/internal/build/WatchInputPublication.d.ts +59 -0
- package/lib/compiler/internal/build/WatchInputPublication.js +93 -0
- package/lib/compiler/internal/build/WatchInputPublication.js.map +1 -0
- package/lib/compiler/internal/build/runBuild.d.ts +29 -2
- package/lib/compiler/internal/build/runBuild.js +45 -19
- package/lib/compiler/internal/build/runBuild.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +2 -0
- package/lib/compiler/internal/buildNativeCompiler.js +1 -0
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +4 -2
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/CompilerProjectSelection.d.ts +9 -8
- package/lib/compiler/internal/project/CompilerProjectSelection.js +11 -9
- package/lib/compiler/internal/project/CompilerProjectSelection.js.map +1 -1
- package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +25 -8
- package/lib/compiler/internal/project/resolveOwningProjectConfig.js +26 -14
- package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
- package/lib/compiler/internal/readEffectiveCompilerOptions.js +1 -1
- package/lib/compiler/internal/readEffectiveCompilerOptions.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +1 -0
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +6 -0
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +32 -4
- package/lib/compiler/internal/runSingleFileEmit.js +124 -89
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +8 -2
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +11 -3
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/transformProjectInWorker.d.ts +1 -1
- package/lib/compiler/internal/transformProjectInWorker.js +3 -2
- package/lib/compiler/internal/transformProjectInWorker.js.map +1 -1
- package/lib/flags/ParseOptions.d.ts +2 -2
- package/lib/internal/E2ETrace.js.map +1 -1
- package/lib/internal/OwnedNativeProcess.d.ts +2 -2
- package/lib/internal/OwnedNativeProcess.js +2 -2
- package/lib/internal/SourceNativeRetirement.d.ts +8 -7
- package/lib/internal/SourceNativeRetirement.js +19 -10
- package/lib/internal/SourceNativeRetirement.js.map +1 -1
- package/lib/internal/TtsxNodeOptions.d.ts +79 -0
- package/lib/internal/TtsxNodeOptions.js +121 -0
- package/lib/internal/TtsxNodeOptions.js.map +1 -0
- package/lib/internal/javascriptRuntimeCapabilities.js +4 -1
- package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
- package/lib/internal/parseWindowsDirectoryCaseSensitivity.d.ts +5 -5
- package/lib/internal/parseWindowsDirectoryCaseSensitivity.js +5 -5
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.d.ts +35 -0
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.js +209 -0
- package/lib/internal/pathIdentity/createCaseSensitivityObservations.js.map +1 -0
- package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js +2 -143
- package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js.map +1 -1
- package/lib/internal/projectInputPathIdentity.d.ts +1 -0
- package/lib/internal/projectInputPathIdentity.js +1 -0
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/internal/workerModuleUrl.d.ts +28 -0
- package/lib/internal/workerModuleUrl.js +40 -0
- package/lib/internal/workerModuleUrl.js.map +1 -0
- package/lib/launcher/internal/buildSingleRootProject.js +2 -1
- package/lib/launcher/internal/buildSingleRootProject.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +3 -2
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/parseTtsxCLI.d.ts +5 -4
- package/lib/launcher/internal/parseTtsxCLI.js +5 -4
- package/lib/launcher/internal/parseTtsxCLI.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +2 -1
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveSingleFileOutput.d.ts +8 -9
- package/lib/launcher/internal/resolveSingleFileOutput.js +8 -9
- package/lib/launcher/internal/resolveSingleFileOutput.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +91 -41
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +5 -2
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtime/CommonJsRuntimeSource.d.ts +10 -6
- package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js +11 -9
- package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js.map +1 -1
- package/lib/launcher/internal/runtime/RuntimeEntrySources.d.ts +40 -0
- package/lib/launcher/internal/runtime/RuntimeEntrySources.js +57 -0
- package/lib/launcher/internal/runtime/RuntimeEntrySources.js.map +1 -0
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.d.ts +5 -4
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js +8 -6
- package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js.map +1 -1
- package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.d.ts +20 -0
- package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js +27 -0
- package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js.map +1 -0
- package/lib/launcher/internal/runtime/commonJsExportNames.d.ts +2 -1
- package/lib/launcher/internal/runtime/commonJsExportNames.js +2 -1
- package/lib/launcher/internal/runtime/commonJsExportNames.js.map +1 -1
- package/lib/launcher/internal/runtime/installRuntimeHooks.d.ts +5 -3
- package/lib/launcher/internal/runtime/installRuntimeHooks.js +42 -12
- package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
- package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js +3 -1
- package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js.map +1 -1
- package/lib/launcher/internal/watch/AsyncWatchTopology.d.ts +113 -0
- package/lib/launcher/internal/watch/AsyncWatchTopology.js +232 -0
- package/lib/launcher/internal/watch/AsyncWatchTopology.js.map +1 -0
- package/lib/launcher/internal/watch/WatchSettlement.d.ts +49 -0
- package/lib/launcher/internal/watch/WatchSettlement.js +70 -0
- package/lib/launcher/internal/watch/WatchSettlement.js.map +1 -0
- package/lib/launcher/internal/watch/WatchTopology.d.ts +103 -13
- package/lib/launcher/internal/watch/WatchTopology.js +380 -95
- package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
- package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.d.ts +121 -0
- package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js +3 -0
- package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js.map +1 -0
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.d.ts +4 -1
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js +3 -2
- package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js.map +1 -1
- package/lib/launcher/internal/watch/serveWatchTopologyWorker.d.ts +19 -0
- package/lib/launcher/internal/watch/serveWatchTopologyWorker.js +62 -0
- package/lib/launcher/internal/watch/serveWatchTopologyWorker.js.map +1 -0
- package/lib/launcher/internal/watch/watchTopologyWorker.d.ts +1 -0
- package/lib/launcher/internal/watch/watchTopologyWorker.js +22 -0
- package/lib/launcher/internal/watch/watchTopologyWorker.js.map +1 -0
- package/lib/plugin/CapabilityPluginResolver.d.ts +1 -1
- package/lib/plugin/CapabilityPluginResolver.js +3 -2
- package/lib/plugin/CapabilityPluginResolver.js.map +1 -1
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +15 -12
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +13 -11
- package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
- package/lib/plugin/internal/load/PluginPackageResolution.d.ts +2 -2
- package/lib/plugin/internal/load/PluginPackageResolution.js +13 -7
- package/lib/plugin/internal/load/PluginPackageResolution.js.map +1 -1
- package/lib/plugin/internal/load/ProjectPluginEntries.d.ts +23 -7
- package/lib/plugin/internal/load/ProjectPluginEntries.js +36 -14
- package/lib/plugin/internal/load/ProjectPluginEntries.js.map +1 -1
- package/lib/plugin/internal/load/collectProjectHostInputs.d.ts +3 -1
- package/lib/plugin/internal/load/collectProjectHostInputs.js +5 -2
- package/lib/plugin/internal/load/collectProjectHostInputs.js.map +1 -1
- package/lib/plugin/internal/load/hasProjectPluginEntries.d.ts +3 -1
- package/lib/plugin/internal/load/hasProjectPluginEntries.js +4 -2
- package/lib/plugin/internal/load/hasProjectPluginEntries.js.map +1 -1
- package/lib/plugin/internal/load/loadProjectPlugins.d.ts +36 -5
- package/lib/plugin/internal/load/loadProjectPlugins.js +477 -421
- package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/internal/load/pluginResolutionFailure.d.ts +18 -0
- package/lib/plugin/internal/load/pluginResolutionFailure.js +32 -0
- package/lib/plugin/internal/load/pluginResolutionFailure.js.map +1 -0
- package/lib/plugin/internal/load/resolveNativeSource.js +2 -3
- package/lib/plugin/internal/load/resolveNativeSource.js.map +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +1 -1
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +3 -2
- package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +4 -1
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +9 -2
- package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +12 -12
- package/lib/structures/ITtscPlugin.d.ts +7 -5
- package/lib/structures/ITtscPluginFactoryContext.d.ts +9 -8
- package/lib/structures/internal/TtscCommonOptions.d.ts +2 -2
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +12 -6
- package/package.json +9 -9
- package/src/TtscCompiler.ts +1 -1
- package/src/compiler/internal/ResidentCheckProcess.ts +159 -374
- package/src/compiler/internal/ResidentCheckProcessHost.ts +502 -0
- package/src/compiler/internal/ResidentCheckProcessProtocol.ts +53 -0
- package/src/compiler/internal/build/BuildExecution.ts +123 -11
- package/src/compiler/internal/build/ResidentCheckWatchSession.ts +41 -24
- package/src/compiler/internal/build/RunBuildOptions.ts +9 -5
- package/src/compiler/internal/build/TsgoArguments.ts +4 -1
- package/src/compiler/internal/build/WatchInputPublication.ts +106 -0
- package/src/compiler/internal/build/runBuild.ts +58 -34
- package/src/compiler/internal/buildNativeCompiler.ts +4 -0
- package/src/compiler/internal/compileProjectInMemory.ts +8 -2
- package/src/compiler/internal/project/CompilerProjectSelection.ts +14 -14
- package/src/compiler/internal/project/resolveOwningProjectConfig.ts +42 -16
- package/src/compiler/internal/readEffectiveCompilerOptions.ts +1 -1
- package/src/compiler/internal/resolveBinary.ts +4 -1
- package/src/compiler/internal/resolveTsgo.ts +7 -0
- package/src/compiler/internal/runSingleFileEmit.ts +145 -101
- package/src/compiler/internal/startResidentTransform.ts +9 -2
- package/src/compiler/internal/transformProjectInMemory.ts +12 -3
- package/src/compiler/internal/transformProjectInWorker.ts +10 -2
- package/src/flags/ParseOptions.ts +2 -2
- package/src/internal/E2ETrace.ts +3 -1
- package/src/internal/OwnedNativeProcess.ts +2 -2
- package/src/internal/SourceNativeRetirement.ts +42 -19
- package/src/internal/TtsxNodeOptions.ts +124 -0
- package/src/internal/javascriptRuntimeCapabilities.ts +6 -1
- package/src/internal/parseWindowsDirectoryCaseSensitivity.ts +5 -5
- package/src/internal/pathIdentity/createCaseSensitivityObservations.ts +242 -0
- package/src/internal/pathIdentity/createFilesystemPathIdentityContext.ts +2 -169
- package/src/internal/projectInputPathIdentity.ts +1 -0
- package/src/internal/workerModuleUrl.ts +34 -0
- package/src/launcher/internal/buildSingleRootProject.ts +2 -0
- package/src/launcher/internal/getCompilerVersionText.ts +3 -2
- package/src/launcher/internal/parseTtsxCLI.ts +5 -4
- package/src/launcher/internal/prepareExecution.ts +6 -1
- package/src/launcher/internal/resolveSingleFileOutput.ts +8 -9
- package/src/launcher/internal/runTtsc.ts +127 -49
- package/src/launcher/internal/runTtsx.ts +17 -8
- package/src/launcher/internal/runtime/CommonJsRuntimeSource.ts +11 -9
- package/src/launcher/internal/runtime/RuntimeEntrySources.ts +57 -0
- package/src/launcher/internal/runtime/RuntimeProjectOwnership.ts +23 -10
- package/src/launcher/internal/runtime/advertiseTypeScriptExtensions.ts +25 -0
- package/src/launcher/internal/runtime/commonJsExportNames.ts +11 -4
- package/src/launcher/internal/runtime/installRuntimeHooks.ts +62 -16
- package/src/launcher/internal/runtime/resolveRuntimeCleanTargets.ts +17 -5
- package/src/launcher/internal/watch/AsyncWatchTopology.ts +267 -0
- package/src/launcher/internal/watch/WatchSettlement.ts +67 -0
- package/src/launcher/internal/watch/WatchTopology.ts +461 -102
- package/src/launcher/internal/watch/WatchTopologyWorkerProtocol.ts +106 -0
- package/src/launcher/internal/watch/projectInputReloadEventShouldNotify.ts +14 -9
- package/src/launcher/internal/watch/serveWatchTopologyWorker.ts +75 -0
- package/src/launcher/internal/watch/watchTopologyWorker.ts +29 -0
- package/src/plugin/CapabilityPluginResolver.ts +9 -2
- package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +17 -12
- package/src/plugin/internal/load/PluginPackageResolution.ts +14 -9
- package/src/plugin/internal/load/ProjectPluginEntries.ts +40 -15
- package/src/plugin/internal/load/collectProjectHostInputs.ts +7 -1
- package/src/plugin/internal/load/hasProjectPluginEntries.ts +8 -3
- package/src/plugin/internal/load/loadProjectPlugins.ts +683 -593
- package/src/plugin/internal/load/pluginResolutionFailure.ts +37 -0
- package/src/plugin/internal/load/resolveNativeSource.ts +10 -4
- package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +8 -2
- package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +10 -1
- package/src/structures/ITtscCompilerContext.ts +12 -12
- package/src/structures/ITtscPlugin.ts +7 -5
- package/src/structures/ITtscPluginFactoryContext.ts +9 -8
- package/src/structures/internal/TtscCommonOptions.ts +2 -2
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +22 -11
- package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +0 -78
- package/internal/graphsymbols/provider.go +0 -650
- package/internal/lspserver/lsp_symbols.go +0 -212
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import childProcess from "node:child_process";
|
|
2
1
|
import fs from "node:fs";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
|
|
5
|
-
import { E2ETrace } from "../E2ETrace";
|
|
6
|
-
import { OwnedSynchronousProcess } from "../OwnedSynchronousProcess";
|
|
7
|
-
import { parseWindowsDirectoryCaseSensitivity } from "../parseWindowsDirectoryCaseSensitivity";
|
|
8
4
|
import type { FilesystemPathIdentity } from "./FilesystemPathIdentity";
|
|
9
5
|
import type { FilesystemPathIdentityContext } from "./FilesystemPathIdentityContext";
|
|
10
6
|
import type { FilesystemPathIdentityOperations } from "./FilesystemPathIdentityOperations";
|
|
7
|
+
import { createCaseSensitivityObservations } from "./createCaseSensitivityObservations";
|
|
11
8
|
import { isFilesystemPathIdentityWithin } from "./isFilesystemPathIdentityWithin";
|
|
12
9
|
import { resolveFilesystemPath } from "./resolveFilesystemPath";
|
|
13
10
|
|
|
@@ -47,19 +44,7 @@ export function createFilesystemPathIdentityContext(
|
|
|
47
44
|
const readdir = operations.readdir ?? fs.readdirSync;
|
|
48
45
|
const caseSensitive =
|
|
49
46
|
operations.caseSensitive ??
|
|
50
|
-
(
|
|
51
|
-
try {
|
|
52
|
-
return filesystemDirectoryIsCaseSensitive(directory, platform, {
|
|
53
|
-
lstat,
|
|
54
|
-
readdir,
|
|
55
|
-
realpath,
|
|
56
|
-
});
|
|
57
|
-
} catch {
|
|
58
|
-
// A failed capability observation is unknown. This does not suppress
|
|
59
|
-
// strict physical identity errors in the separate realpath resolver.
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
});
|
|
47
|
+
createCaseSensitivityObservations({ lstat, platform, readdir, realpath });
|
|
63
48
|
|
|
64
49
|
const resolve = (location: string): FilesystemPathIdentity => {
|
|
65
50
|
const normalized = resolveFilesystemPath(location, platform);
|
|
@@ -262,150 +247,6 @@ function physicalRealpath(location: string): string {
|
|
|
262
247
|
return fs.realpathSync.native?.(location) ?? fs.realpathSync(location);
|
|
263
248
|
}
|
|
264
249
|
|
|
265
|
-
function filesystemDirectoryIsCaseSensitive(
|
|
266
|
-
directory: string,
|
|
267
|
-
platform: NodeJS.Platform,
|
|
268
|
-
operations: {
|
|
269
|
-
lstat(location: string): fs.Stats | fs.BigIntStats;
|
|
270
|
-
readdir(directory: string): string[];
|
|
271
|
-
realpath(location: string): string;
|
|
272
|
-
},
|
|
273
|
-
): boolean | undefined {
|
|
274
|
-
const pathApi = platform === "win32" ? path.win32 : path.posix;
|
|
275
|
-
let entries: string[];
|
|
276
|
-
try {
|
|
277
|
-
entries = operations.readdir(directory);
|
|
278
|
-
} catch {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
const foldedNames = new Map<string, string>();
|
|
282
|
-
for (const name of entries) {
|
|
283
|
-
const folded = foldAsciiCase(name);
|
|
284
|
-
const previous = foldedNames.get(folded);
|
|
285
|
-
if (previous !== undefined && previous !== name) return true;
|
|
286
|
-
foldedNames.set(folded, name);
|
|
287
|
-
}
|
|
288
|
-
for (const name of entries) {
|
|
289
|
-
const alternate = alternateCase(name);
|
|
290
|
-
if (alternate === name) continue;
|
|
291
|
-
try {
|
|
292
|
-
operations.lstat(pathApi.join(directory, alternate));
|
|
293
|
-
} catch (error) {
|
|
294
|
-
if (isMissingFilesystemEntry(error)) {
|
|
295
|
-
// Enumeration is not an atomic snapshot. An absent alternate proves
|
|
296
|
-
// sensitivity only if the original spelling still names an entry.
|
|
297
|
-
try {
|
|
298
|
-
operations.lstat(pathApi.join(directory, name));
|
|
299
|
-
return true;
|
|
300
|
-
} catch (originalError) {
|
|
301
|
-
if (isMissingFilesystemEntry(originalError) === false)
|
|
302
|
-
throw originalError;
|
|
303
|
-
}
|
|
304
|
-
continue;
|
|
305
|
-
}
|
|
306
|
-
throw error;
|
|
307
|
-
}
|
|
308
|
-
try {
|
|
309
|
-
const actual = resolveFilesystemPath(
|
|
310
|
-
operations.realpath(pathApi.join(directory, name)),
|
|
311
|
-
platform,
|
|
312
|
-
);
|
|
313
|
-
const alias = resolveFilesystemPath(
|
|
314
|
-
operations.realpath(pathApi.join(directory, alternate)),
|
|
315
|
-
platform,
|
|
316
|
-
);
|
|
317
|
-
return actual !== alias;
|
|
318
|
-
} catch (error) {
|
|
319
|
-
if (isMissingFilesystemEntry(error) === false) throw error;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
if (platform === "darwin") {
|
|
323
|
-
// APFS/HFS case semantics are volume-wide. An empty directory has no child
|
|
324
|
-
// name to probe, so ask the same read-only question of its existing name in
|
|
325
|
-
// the parent and walk upward until a name with ASCII case is available.
|
|
326
|
-
// This avoids a write probe while still distinguishing default APFS from a
|
|
327
|
-
// case-sensitive volume.
|
|
328
|
-
let current = resolveFilesystemPath(directory, platform);
|
|
329
|
-
while (true) {
|
|
330
|
-
const parent = pathApi.dirname(current);
|
|
331
|
-
if (parent === current) break;
|
|
332
|
-
// Names are interpreted by their parent; another mount is not evidence
|
|
333
|
-
// of the directory's own volume case policy.
|
|
334
|
-
if (operations.lstat(current).dev !== operations.lstat(parent).dev)
|
|
335
|
-
return undefined;
|
|
336
|
-
const name = pathApi.basename(current);
|
|
337
|
-
const alternate = alternateCase(name);
|
|
338
|
-
if (alternate !== name) {
|
|
339
|
-
try {
|
|
340
|
-
const actual = resolveFilesystemPath(
|
|
341
|
-
operations.realpath(current),
|
|
342
|
-
platform,
|
|
343
|
-
);
|
|
344
|
-
const alias = resolveFilesystemPath(
|
|
345
|
-
operations.realpath(pathApi.join(parent, alternate)),
|
|
346
|
-
platform,
|
|
347
|
-
);
|
|
348
|
-
return actual !== alias;
|
|
349
|
-
} catch (error) {
|
|
350
|
-
if (isMissingFilesystemEntry(error) === false) throw error;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
current = parent;
|
|
354
|
-
}
|
|
355
|
-
return undefined;
|
|
356
|
-
}
|
|
357
|
-
if (platform !== "win32") return undefined;
|
|
358
|
-
// Node does not expose the Windows per-directory flag. Prefer fsutil's
|
|
359
|
-
// read-only answer; unavailable capability remains unknown.
|
|
360
|
-
const queried = queryWindowsDirectoryCaseSensitivity(directory);
|
|
361
|
-
if (queried !== undefined) return queried;
|
|
362
|
-
return undefined;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Read the per-directory flag without depending on fsutil's display language.
|
|
367
|
-
*
|
|
368
|
-
* English output is cheap to recognize directly. Other Windows locales write
|
|
369
|
-
* console-code-page bytes that Node cannot reliably decode, so their raw
|
|
370
|
-
* message is interpreted against the volume-root query.
|
|
371
|
-
*/
|
|
372
|
-
function queryWindowsDirectoryCaseSensitivity(
|
|
373
|
-
directory: string,
|
|
374
|
-
): boolean | undefined {
|
|
375
|
-
const result = queryWindowsDirectoryCaseSensitivityBytes(directory);
|
|
376
|
-
if (result === undefined) return undefined;
|
|
377
|
-
const volumeRoot = path.win32.parse(directory).root;
|
|
378
|
-
const direct = parseWindowsDirectoryCaseSensitivity(
|
|
379
|
-
result,
|
|
380
|
-
undefined,
|
|
381
|
-
volumeRoot,
|
|
382
|
-
);
|
|
383
|
-
if (direct !== undefined) return direct;
|
|
384
|
-
const volume = queryWindowsDirectoryCaseSensitivityBytes(volumeRoot);
|
|
385
|
-
return parseWindowsDirectoryCaseSensitivity(result, volume, volumeRoot);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function queryWindowsDirectoryCaseSensitivityBytes(
|
|
389
|
-
directory: string,
|
|
390
|
-
): Buffer | undefined {
|
|
391
|
-
const args = ["file", "queryCaseSensitiveInfo", directory];
|
|
392
|
-
const result = E2ETrace.synchronous(
|
|
393
|
-
"fsutil.exe",
|
|
394
|
-
args,
|
|
395
|
-
{},
|
|
396
|
-
"path-case-probe",
|
|
397
|
-
() =>
|
|
398
|
-
OwnedSynchronousProcess.launch("fsutil.exe", args, {
|
|
399
|
-
windowsHide: true,
|
|
400
|
-
}) ?? childProcess.spawnSync("fsutil.exe", args, { windowsHide: true }),
|
|
401
|
-
);
|
|
402
|
-
return result.error === undefined &&
|
|
403
|
-
result.status === 0 &&
|
|
404
|
-
Buffer.isBuffer(result.stdout)
|
|
405
|
-
? result.stdout
|
|
406
|
-
: undefined;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
250
|
function filesystemPathIdentityKey(
|
|
410
251
|
location: string,
|
|
411
252
|
platform: NodeJS.Platform,
|
|
@@ -415,14 +256,6 @@ function filesystemPathIdentityKey(
|
|
|
415
256
|
return `${root.toLowerCase()}${location.slice(root.length)}`;
|
|
416
257
|
}
|
|
417
258
|
|
|
418
|
-
function alternateCase(value: string): string {
|
|
419
|
-
return value.replace(/[A-Za-z]/g, (character) =>
|
|
420
|
-
character === character.toLowerCase()
|
|
421
|
-
? character.toUpperCase()
|
|
422
|
-
: character.toLowerCase(),
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
259
|
/** ASCII folding is the case equivalence observed by the read-only probes. */
|
|
427
260
|
function foldAsciiCase(value: string): string {
|
|
428
261
|
return value.replace(/[A-Z]/g, (character) => character.toLowerCase());
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
export * from "./pathIdentity/FilesystemPathIdentity";
|
|
17
17
|
export * from "./pathIdentity/FilesystemPathIdentityOperations";
|
|
18
18
|
export * from "./pathIdentity/FilesystemPathIdentityContext";
|
|
19
|
+
export * from "./pathIdentity/createCaseSensitivityObservations";
|
|
19
20
|
export * from "./pathIdentity/createFilesystemPathIdentityContext";
|
|
20
21
|
export * from "./pathIdentity/isFilesystemPathIdentityWithin";
|
|
21
22
|
export * from "./pathIdentity/resolveFilesystemPath";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Load an owned CommonJS worker through a module URL while retaining Node's
|
|
5
|
+
* inherited execution options and preload hooks.
|
|
6
|
+
*
|
|
7
|
+
* A parent launched with --input-type interprets stdin or evaluated source.
|
|
8
|
+
* With a preload, module resolution rejects that inherited option at a file
|
|
9
|
+
* worker entry before the worker can start. A supported data URL supplies
|
|
10
|
+
* module source that requires the real entry. Setting execArgv instead would
|
|
11
|
+
* replace Node's inherited-option handling and can reject otherwise valid
|
|
12
|
+
* process options such as --max-old-space-size. createRequire preserves the
|
|
13
|
+
* entry's CommonJS resolution, including literal POSIX backslashes that file
|
|
14
|
+
* URL imports reject as encoded separators. The worker entry must use the
|
|
15
|
+
* package's CommonJS source or emitted format.
|
|
16
|
+
*
|
|
17
|
+
* @param filename Native entry path, resolved from cwd when relative.
|
|
18
|
+
* @returns A data URL accepted by the Worker constructor; it owns no thread.
|
|
19
|
+
* @evidence contracts/common.md#principled-implementation Node documents data URL worker entries and inherited options; a module-source entry requires the selected CommonJS owner without applying the parent's stdin-only input type to that file's initial module resolution.
|
|
20
|
+
* @evidence contracts/common.md#clear-and-simple-design One URL constructor owns the bootstrap representation while callers retain worker options, thread lifetime and actual entry selection.
|
|
21
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts Supported createRequire loads the actual CommonJS owner; no flags are guessed, stripped or replaced and no foreign process state is changed.
|
|
22
|
+
* @evidence contracts/common.md#meaningful-documentation Prose explains the stdin inheritance failure, the explicit-execArgv hazard, CommonJS entry requirements and the native path input.
|
|
23
|
+
* @evidence contracts/portability.md#os-neutral-implementation Native path resolution and createRequire retain filesystem path spelling, including POSIX literal backslashes; JSON string encoding and data URL encoding separate that path from JavaScript and URL syntax without routing it through ESM file-URL separator rules.
|
|
24
|
+
* @evidence contracts/performance.md#efficient-algorithms Native path resolution, string encoding and data URL construction each visit entry-path text; the returned URL and its module source occupy linear space in that text.
|
|
25
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work Each call represents its selected entry and retains no computed answer.
|
|
26
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources This operation returns a URL without acquiring a worker, file handle or persistent state; the caller owns the URL and thread.
|
|
27
|
+
* @see https://nodejs.org/api/worker_threads.html#new-workerfilename-options
|
|
28
|
+
* @see https://nodejs.org/api/cli.html#--input-typetype
|
|
29
|
+
*/
|
|
30
|
+
export function workerModuleUrl(filename: string): URL {
|
|
31
|
+
const entry = JSON.stringify(path.resolve(filename));
|
|
32
|
+
const source = `import { createRequire } from "node:module"; createRequire(${entry})(${entry});`;
|
|
33
|
+
return new URL(`data:text/javascript,${encodeURIComponent(source)}`);
|
|
34
|
+
}
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { runBuild } from "../../compiler/internal/build/runBuild";
|
|
5
5
|
import { readProjectConfig } from "../../compiler/internal/project/readProjectConfig";
|
|
6
6
|
import { readEffectiveCompilerOptions } from "../../compiler/internal/readEffectiveCompilerOptions";
|
|
7
|
+
import { SidecarEnvironment } from "../../compiler/internal/sharedHost/SidecarEnvironment";
|
|
7
8
|
import { createFilesystemPathIdentityContext } from "../../internal/pathIdentity/createFilesystemPathIdentityContext";
|
|
8
9
|
import type { TtscBuildResult } from "../../structures/internal/TtscBuildResult";
|
|
9
10
|
import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOptions";
|
|
@@ -186,6 +187,7 @@ export function buildSingleRootProject(props: {
|
|
|
186
187
|
project,
|
|
187
188
|
options.passthrough,
|
|
188
189
|
options.binary,
|
|
190
|
+
SidecarEnvironment.merge(process.env, options.env),
|
|
189
191
|
);
|
|
190
192
|
const result = runBuild({
|
|
191
193
|
binary: options.binary,
|
|
@@ -24,10 +24,11 @@ import type { TtscCommonOptions } from "../../structures/internal/TtscCommonOpti
|
|
|
24
24
|
export function getCompilerVersionText(
|
|
25
25
|
options: TtscCommonOptions = {},
|
|
26
26
|
): string {
|
|
27
|
-
const
|
|
27
|
+
const env = SidecarEnvironment.merge(process.env, options.env);
|
|
28
|
+
const tsgo = resolveTsgo({ ...options, env });
|
|
28
29
|
const res = spawnNative(tsgo.binary, ["--version"], {
|
|
29
30
|
cwd: options.cwd,
|
|
30
|
-
env
|
|
31
|
+
env,
|
|
31
32
|
});
|
|
32
33
|
if (res.error || res.status !== 0) {
|
|
33
34
|
throw new Error(
|
|
@@ -14,10 +14,11 @@ import { assertTtsxNoWatch } from "./assertTtsxNoWatch";
|
|
|
14
14
|
* Compiler flags before the first entry, repeated preloads and launcher-owned
|
|
15
15
|
* settings are separated once through the shared flag schema. Terminal requests
|
|
16
16
|
* are recognized only before the entry; unsupported watch/build requests fail
|
|
17
|
-
* before a check, build or program is started. These decisions inspect the
|
|
18
|
-
* argv frame; this parser does not read response-file contents or
|
|
19
|
-
* eventual native effects. Preparation inspects response frames
|
|
20
|
-
* project's actual compiler directory before requesting its
|
|
17
|
+
* before a check, build or program is started. These decisions inspect the
|
|
18
|
+
* supplied argv frame; this parser does not read response-file contents or
|
|
19
|
+
* certify their eventual native effects. Preparation inspects response frames
|
|
20
|
+
* at the selected project's actual compiler directory before requesting its
|
|
21
|
+
* build.
|
|
21
22
|
*
|
|
22
23
|
* @param argv Command tokens as received from the launcher.
|
|
23
24
|
* @returns A terminal request or the launcher's typed option record.
|
|
@@ -324,8 +324,12 @@ function createProjectContext(
|
|
|
324
324
|
project,
|
|
325
325
|
options.passthrough,
|
|
326
326
|
options.binary,
|
|
327
|
+
env,
|
|
328
|
+
);
|
|
329
|
+
const privateEmitRootDir = resolveRuntimeSourceRoot(
|
|
330
|
+
project,
|
|
331
|
+
effectiveOptions,
|
|
327
332
|
);
|
|
328
|
-
const privateEmitRootDir = resolveRuntimeSourceRoot(project, effectiveOptions);
|
|
329
333
|
// Resolved once: resolution costs a realpath (and, for a missing directory on
|
|
330
334
|
// Windows, a case-sensitivity probe).
|
|
331
335
|
const runtimeRootDir = createFilesystemPathIdentityContext({
|
|
@@ -437,6 +441,7 @@ function discoverOwningProject(
|
|
|
437
441
|
});
|
|
438
442
|
const owning = resolveOwningProjectConfig({
|
|
439
443
|
binary: options.binary,
|
|
444
|
+
env: SidecarEnvironment.merge(process.env, options.env),
|
|
440
445
|
file,
|
|
441
446
|
tsconfig: nearest.path,
|
|
442
447
|
});
|
|
@@ -14,15 +14,14 @@ import type { ITtscParsedProjectConfig } from "../../structures/internal/ITtscPa
|
|
|
14
14
|
* then copies one transformed JavaScript file to this path, so project-mode
|
|
15
15
|
* declaration, map, build-info, outFile, and broad outDir products are not
|
|
16
16
|
* positional outputs. Explicit output mirrors cwd for contained sources and
|
|
17
|
-
* uses the source basename for outside-layout or different-volume sources.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* pinned filesystem object.
|
|
17
|
+
* uses the source basename for outside-layout or different-volume sources. A
|
|
18
|
+
* supplied invocation project owns placement without resolving the original
|
|
19
|
+
* locator again. Forwarded JSX uses the supplied native argv frames and does
|
|
20
|
+
* not expand response-file requests here. CLI enum normalization; an explicit
|
|
21
|
+
* reset selects the default suffix instead of reviving the configured JSX.
|
|
22
|
+
* Placement-setting read failures use the no-project fallback; that fallback
|
|
23
|
+
* does not certify a successful build or valid configuration. Physical spelling
|
|
24
|
+
* is a current native observation, not a pinned filesystem object.
|
|
26
25
|
*
|
|
27
26
|
* @evidence contracts/common.md#principled-implementation CLI output wins over the selected project output; a contained cwd-relative source mirrors its layout while an outside-layout or cross-volume source uses its basename inside that explicit root. Supported extensions choose the emitted suffix, and physical root/file relation preserves configured project layout through links.
|
|
28
27
|
* @evidence contracts/common.md#clear-and-simple-design Output placement delegates project settings and isolates containment, extension and forwarded-option readers; it does not materialize compiler side products.
|
|
@@ -35,8 +35,9 @@ import { resolveCacheDir } from "./resolveCacheDir";
|
|
|
35
35
|
import { resolveSingleFileOutput } from "./resolveSingleFileOutput";
|
|
36
36
|
import { resolveRuntimeCleanTargets } from "./runtime/resolveRuntimeCleanTargets";
|
|
37
37
|
import { withRuntimeDirectoryLock } from "./runtime/withRuntimeDirectoryLock";
|
|
38
|
+
import { AsyncWatchTopology } from "./watch/AsyncWatchTopology";
|
|
38
39
|
import { type WatchInputChange } from "./watch/WatchInputChange";
|
|
39
|
-
import {
|
|
40
|
+
import { WatchSettlement } from "./watch/WatchSettlement";
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* CLI entry point for `ttsc`. Dispatches argv to the appropriate lane (build,
|
|
@@ -578,16 +579,30 @@ function printCacheHelp(): void {
|
|
|
578
579
|
|
|
579
580
|
/**
|
|
580
581
|
* Adapt one normalized CLI request to its selected private producer and public
|
|
581
|
-
* copy. The existing selection owns config and original argument anchoring;
|
|
582
|
-
*
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
582
|
+
* copy. The existing selection owns config and original argument anchoring; the
|
|
583
|
+
* full request retains cache, reporting and watch callbacks. Only the private
|
|
584
|
+
* helper writes the selected public file. Success presentation follows that
|
|
585
|
+
* completed copy and describes public emission, without private products or a
|
|
586
|
+
* fabricated transform-site count. Watch supplies quiet true itself. The async
|
|
587
|
+
* watch executor awaits input publication before building; ordinary CLI calls
|
|
588
|
+
* keep their synchronous return contract and share output planning.
|
|
586
589
|
*/
|
|
587
590
|
function runSingleFile(
|
|
588
591
|
options: ReturnType<typeof parseTtscBuildArgs> &
|
|
589
592
|
Pick<TtscSingleFileEmitOptions, "onProjectInputs" | "onWatchInputs">,
|
|
590
|
-
): number
|
|
593
|
+
): number;
|
|
594
|
+
function runSingleFile(
|
|
595
|
+
options: ReturnType<typeof parseTtscBuildArgs> &
|
|
596
|
+
Pick<TtscSingleFileEmitOptions, "onProjectInputs" | "onWatchInputs">,
|
|
597
|
+
execute: typeof runSingleFileEmit.async,
|
|
598
|
+
): Promise<number>;
|
|
599
|
+
function runSingleFile(
|
|
600
|
+
options: ReturnType<typeof parseTtscBuildArgs> &
|
|
601
|
+
Pick<TtscSingleFileEmitOptions, "onProjectInputs" | "onWatchInputs">,
|
|
602
|
+
execute:
|
|
603
|
+
| typeof runSingleFileEmit
|
|
604
|
+
| typeof runSingleFileEmit.async = runSingleFileEmit,
|
|
605
|
+
): number | Promise<number> {
|
|
591
606
|
if (options.files.length !== 1) {
|
|
592
607
|
throw new Error(
|
|
593
608
|
"ttsc: single-file mode currently accepts exactly one input file",
|
|
@@ -610,20 +625,25 @@ function runSingleFile(
|
|
|
610
625
|
project,
|
|
611
626
|
)
|
|
612
627
|
: undefined;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
628
|
+
const result = execute({ ...options, cwd, file, out }, selection);
|
|
629
|
+
const report = (): number => {
|
|
630
|
+
if (options.quiet === false) {
|
|
631
|
+
process.stdout.write(
|
|
632
|
+
`// ttsc: tsconfig=${project.path} cwd=${cwd} emit=${emit}\n`,
|
|
633
|
+
);
|
|
634
|
+
if (out !== undefined) {
|
|
635
|
+
process.stdout.write(`// ttsc: emitted=1 files\n`);
|
|
636
|
+
process.stdout.write(` + ${relativeToCwd(cwd, out) || out}\n`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
618
639
|
if (out !== undefined) {
|
|
619
|
-
process.stdout.write(
|
|
620
|
-
|
|
640
|
+
process.stdout.write(
|
|
641
|
+
`${relativeToCwd(cwd, out) || path.basename(out)}\n`,
|
|
642
|
+
);
|
|
621
643
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
return 0;
|
|
644
|
+
return 0;
|
|
645
|
+
};
|
|
646
|
+
return typeof result === "string" ? report() : result.then(report);
|
|
627
647
|
}
|
|
628
648
|
|
|
629
649
|
function runWatch(
|
|
@@ -631,7 +651,7 @@ function runWatch(
|
|
|
631
651
|
checkOnly: boolean,
|
|
632
652
|
): number {
|
|
633
653
|
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
634
|
-
let topology:
|
|
654
|
+
let topology: AsyncWatchTopology | undefined;
|
|
635
655
|
const invocation = {
|
|
636
656
|
...(checkOnly ? { ...options, emit: false } : options),
|
|
637
657
|
cwd,
|
|
@@ -639,10 +659,10 @@ function runWatch(
|
|
|
639
659
|
inputs: readonly string[],
|
|
640
660
|
identities?: PluginContentIdentities.Store,
|
|
641
661
|
) => {
|
|
642
|
-
topology?.setExtraInputs(inputs, identities);
|
|
662
|
+
return topology?.setExtraInputs(inputs, identities);
|
|
643
663
|
},
|
|
644
664
|
onProjectInputs: (inputs: ITtscProjectInputSnapshot) => {
|
|
645
|
-
topology?.setProjectInputs(inputs);
|
|
665
|
+
return topology?.setProjectInputs(inputs);
|
|
646
666
|
},
|
|
647
667
|
quiet: true,
|
|
648
668
|
};
|
|
@@ -653,6 +673,8 @@ function runWatch(
|
|
|
653
673
|
let closing: Promise<void> | undefined;
|
|
654
674
|
let rerun = false;
|
|
655
675
|
let timer: NodeJS.Timeout | null = null;
|
|
676
|
+
const settlement = new WatchSettlement();
|
|
677
|
+
let completedBuilds = 0;
|
|
656
678
|
const resident =
|
|
657
679
|
invocation.files.length === 0 ? new ResidentCheckWatchSession() : undefined;
|
|
658
680
|
const pendingChanges = new PendingResidentCheckWatchChanges();
|
|
@@ -664,6 +686,7 @@ function runWatch(
|
|
|
664
686
|
const runOnce = async () => {
|
|
665
687
|
if (closed) return;
|
|
666
688
|
running = true;
|
|
689
|
+
settlement.changed();
|
|
667
690
|
const change = pendingChanges.take();
|
|
668
691
|
if (process.env.TTSC_WATCH_DEBUG_INPUTS)
|
|
669
692
|
debugWatchInputs(`cycle ${JSON.stringify(change)}`);
|
|
@@ -682,14 +705,14 @@ function runWatch(
|
|
|
682
705
|
// rebuild reports a clear failure and the watcher keeps running instead
|
|
683
706
|
// of crashing the process with an uncaught exception.
|
|
684
707
|
const status = await (invocation.files.length !== 0
|
|
685
|
-
?
|
|
708
|
+
? runSingleFile(invocation, runSingleFileEmit.async)
|
|
686
709
|
: (async () => {
|
|
687
710
|
const buildOptions = checkOnly
|
|
688
711
|
? { ...invocation, emit: false }
|
|
689
712
|
: invocation;
|
|
690
713
|
const result =
|
|
691
714
|
resident === undefined
|
|
692
|
-
? runBuild(buildOptions)
|
|
715
|
+
? await runBuild.async(buildOptions)
|
|
693
716
|
: await resident.run(buildOptions, change);
|
|
694
717
|
// Watch output is one ordered stream. The diagnostics a build
|
|
695
718
|
// produced and the "[ttsc] watch build …" marker that concludes
|
|
@@ -710,6 +733,7 @@ function runWatch(
|
|
|
710
733
|
process.stdout.write(
|
|
711
734
|
`[ttsc] ${status === 0 ? "watch build complete" : "watch build failed"}\n`,
|
|
712
735
|
);
|
|
736
|
+
++completedBuilds;
|
|
713
737
|
} catch (error) {
|
|
714
738
|
if (closed) return;
|
|
715
739
|
// Same ordered-stream rule as the build path: the failure text goes on
|
|
@@ -719,30 +743,33 @@ function runWatch(
|
|
|
719
743
|
lastStatus = lastStatus === 0 ? 2 : lastStatus;
|
|
720
744
|
completedBuild = true;
|
|
721
745
|
process.stdout.write(`[ttsc] watch build failed\n`);
|
|
746
|
+
++completedBuilds;
|
|
722
747
|
} finally {
|
|
723
|
-
running = false;
|
|
724
748
|
if (completed && !closed) {
|
|
725
749
|
try {
|
|
726
750
|
// A filesystem event can arrive after the build reports completion
|
|
727
|
-
// but before
|
|
751
|
+
// but before post-build reconciliation reaches its re-resolution.
|
|
728
752
|
// Notify for a membership change here as well, otherwise that new
|
|
729
753
|
// input is silently absorbed into the watch set without a rebuild.
|
|
730
|
-
topology?.refresh(true);
|
|
754
|
+
await topology?.refresh(true);
|
|
731
755
|
} catch (error) {
|
|
732
756
|
process.stderr.write(
|
|
733
757
|
`[ttsc] watch error on ${watchMessagePath(cwd, root)}: ${formatError(error)}\n`,
|
|
734
758
|
);
|
|
735
759
|
}
|
|
736
760
|
}
|
|
761
|
+
running = false;
|
|
737
762
|
}
|
|
738
763
|
if (rerun) {
|
|
739
764
|
rerun = false;
|
|
740
765
|
trigger();
|
|
741
766
|
}
|
|
767
|
+
settlement.changed();
|
|
742
768
|
};
|
|
743
769
|
const trigger = (change?: WatchInputChange, reload = false) => {
|
|
744
770
|
if (closed) return;
|
|
745
771
|
pendingChanges.push(change, reload);
|
|
772
|
+
settlement.changed();
|
|
746
773
|
if (running) {
|
|
747
774
|
rerun = true;
|
|
748
775
|
return;
|
|
@@ -758,33 +785,30 @@ function runWatch(
|
|
|
758
785
|
activeSelected: active !== undefined,
|
|
759
786
|
});
|
|
760
787
|
closed = true;
|
|
788
|
+
settlement.changed();
|
|
761
789
|
if (timer) clearTimeout(timer);
|
|
762
790
|
timer = null;
|
|
763
791
|
process.off("SIGINT", onInterrupt);
|
|
764
792
|
process.off("SIGTERM", onTerminate);
|
|
765
793
|
process.off("message", onMessage);
|
|
766
794
|
process.off("disconnect", onDisconnect);
|
|
767
|
-
|
|
768
|
-
try {
|
|
769
|
-
topology?.close();
|
|
770
|
-
} catch (error) {
|
|
771
|
-
topologyError = error;
|
|
772
|
-
}
|
|
795
|
+
const topologyClosing = topology?.close() ?? Promise.resolve();
|
|
773
796
|
const residentClosing = resident?.close() ?? Promise.resolve();
|
|
774
797
|
const activeClosing = active ?? Promise.resolve();
|
|
775
|
-
closing = Promise.allSettled([
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
798
|
+
closing = Promise.allSettled([
|
|
799
|
+
topologyClosing,
|
|
800
|
+
residentClosing,
|
|
801
|
+
activeClosing,
|
|
802
|
+
]).then((results) => {
|
|
803
|
+
const errors = results.flatMap((result) =>
|
|
804
|
+
result.status === "rejected" ? [result.reason] : [],
|
|
805
|
+
);
|
|
806
|
+
if (errors.length !== 0)
|
|
807
|
+
throw new AggregateError(
|
|
808
|
+
errors,
|
|
809
|
+
`ttsc: watch shutdown failed: ${errors.map(formatError).join("; ")}`,
|
|
779
810
|
);
|
|
780
|
-
|
|
781
|
-
if (errors.length !== 0)
|
|
782
|
-
throw new AggregateError(
|
|
783
|
-
errors,
|
|
784
|
-
`ttsc: watch shutdown failed: ${errors.map(formatError).join("; ")}`,
|
|
785
|
-
);
|
|
786
|
-
},
|
|
787
|
-
);
|
|
811
|
+
});
|
|
788
812
|
if (process.env.TTSC_E2E_TRACE) {
|
|
789
813
|
// Observe the original promises without substituting their settlements.
|
|
790
814
|
void residentClosing.then(
|
|
@@ -893,9 +917,48 @@ function runWatch(
|
|
|
893
917
|
const id = message.id;
|
|
894
918
|
E2ETrace.watchShutdown("ipc-stop-received", { stopId: id });
|
|
895
919
|
void finish(id);
|
|
920
|
+
} else if (
|
|
921
|
+
typeof message === "object" &&
|
|
922
|
+
message !== null &&
|
|
923
|
+
"type" in message &&
|
|
924
|
+
message.type === "ttsc.watch.settle" &&
|
|
925
|
+
"id" in message &&
|
|
926
|
+
typeof message.id === "string" &&
|
|
927
|
+
message.id.length !== 0
|
|
928
|
+
) {
|
|
929
|
+
const id = message.id;
|
|
930
|
+
// Build markers retain their exact text and one marker per cycle. IPC
|
|
931
|
+
// travels on another pipe, so its receipt carries the completed count:
|
|
932
|
+
// a caller must drain those stdout markers before using the boundary.
|
|
933
|
+
void settlement
|
|
934
|
+
.wait(
|
|
935
|
+
async () => {
|
|
936
|
+
await topology?.whenIdle();
|
|
937
|
+
if (closed) throw new Error("ttsc: watch closed before settling");
|
|
938
|
+
return !running && !rerun && timer === null;
|
|
939
|
+
},
|
|
940
|
+
async () => {
|
|
941
|
+
if (process.connected && process.send !== undefined)
|
|
942
|
+
await new Promise<void>((resolve, reject) => {
|
|
943
|
+
process.send!(
|
|
944
|
+
{ type: "ttsc.watch.settled", id, builds: completedBuilds },
|
|
945
|
+
(error: Error | null) =>
|
|
946
|
+
error === null ? resolve() : reject(error),
|
|
947
|
+
);
|
|
948
|
+
});
|
|
949
|
+
},
|
|
950
|
+
)
|
|
951
|
+
.catch((error: unknown) => {
|
|
952
|
+
if (!closed) {
|
|
953
|
+
process.stderr.write(`${formatError(error)}\n`);
|
|
954
|
+
lastStatus = lastStatus === 0 ? 2 : lastStatus;
|
|
955
|
+
void finish();
|
|
956
|
+
}
|
|
957
|
+
});
|
|
896
958
|
}
|
|
897
959
|
};
|
|
898
960
|
const startRun = () => {
|
|
961
|
+
timer = null;
|
|
899
962
|
active = runOnce();
|
|
900
963
|
void active.catch((error: unknown) => {
|
|
901
964
|
process.stderr.write(`${formatError(error)}\n`);
|
|
@@ -904,7 +967,13 @@ function runWatch(
|
|
|
904
967
|
});
|
|
905
968
|
};
|
|
906
969
|
|
|
907
|
-
topology = new
|
|
970
|
+
topology = new AsyncWatchTopology(invocation, {
|
|
971
|
+
onFailure: (error) => {
|
|
972
|
+
if (closed) return;
|
|
973
|
+
lastStatus = lastStatus === 0 ? 2 : lastStatus;
|
|
974
|
+
process.stderr.write(`${formatError(error)}\n`);
|
|
975
|
+
void finish();
|
|
976
|
+
},
|
|
908
977
|
onError: (location, error) => {
|
|
909
978
|
process.stderr.write(
|
|
910
979
|
`[ttsc] watch error on ${watchMessagePath(cwd, location)}: ${formatError(error)}\n`,
|
|
@@ -934,16 +1003,25 @@ function runWatch(
|
|
|
934
1003
|
},
|
|
935
1004
|
onTopologyChange: () => trigger(undefined, true),
|
|
936
1005
|
});
|
|
1006
|
+
const start = async (): Promise<void> => {
|
|
1007
|
+
await topology!.refresh(false);
|
|
1008
|
+
if (closed) return;
|
|
1009
|
+
process.stdout.write(`[ttsc] watching ${watchMessagePath(cwd, root)}\n`);
|
|
1010
|
+
startRun();
|
|
1011
|
+
};
|
|
937
1012
|
try {
|
|
938
|
-
topology.refresh(false);
|
|
939
1013
|
process.on("SIGINT", onInterrupt);
|
|
940
1014
|
process.on("SIGTERM", onTerminate);
|
|
941
1015
|
if (process.connected) {
|
|
942
1016
|
process.on("message", onMessage);
|
|
943
1017
|
process.on("disconnect", onDisconnect);
|
|
944
1018
|
}
|
|
945
|
-
|
|
946
|
-
|
|
1019
|
+
void start().catch((error: unknown) => {
|
|
1020
|
+
if (closed) return;
|
|
1021
|
+
lastStatus = lastStatus === 0 ? 2 : lastStatus;
|
|
1022
|
+
process.stderr.write(`${formatError(error)}\n`);
|
|
1023
|
+
void finish();
|
|
1024
|
+
});
|
|
947
1025
|
} catch (error) {
|
|
948
1026
|
// Refresh may fail after acquiring only part of the watch set. Release that
|
|
949
1027
|
// partial ownership as well as setup's signal listeners before returning.
|