ttsc 0.32.0 → 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.
Files changed (164) hide show
  1. package/lib/TtscCompiler.js +1 -1
  2. package/lib/TtscCompiler.js.map +1 -1
  3. package/lib/compiler/internal/ResidentCheckProcess.d.ts +74 -96
  4. package/lib/compiler/internal/ResidentCheckProcess.js +150 -335
  5. package/lib/compiler/internal/ResidentCheckProcess.js.map +1 -1
  6. package/lib/compiler/internal/ResidentCheckProcessHost.d.ts +127 -0
  7. package/lib/compiler/internal/ResidentCheckProcessHost.js +447 -0
  8. package/lib/compiler/internal/ResidentCheckProcessHost.js.map +1 -0
  9. package/lib/compiler/internal/ResidentCheckProcessProtocol.d.ts +64 -0
  10. package/lib/compiler/internal/ResidentCheckProcessProtocol.js +3 -0
  11. package/lib/compiler/internal/ResidentCheckProcessProtocol.js.map +1 -0
  12. package/lib/compiler/internal/build/BuildExecution.d.ts +103 -1
  13. package/lib/compiler/internal/build/BuildExecution.js +91 -11
  14. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  15. package/lib/compiler/internal/build/ResidentCheckWatchSession.d.ts +16 -11
  16. package/lib/compiler/internal/build/ResidentCheckWatchSession.js +37 -21
  17. package/lib/compiler/internal/build/ResidentCheckWatchSession.js.map +1 -1
  18. package/lib/compiler/internal/build/RunBuildOptions.d.ts +6 -5
  19. package/lib/compiler/internal/build/WatchInputPublication.d.ts +59 -0
  20. package/lib/compiler/internal/build/WatchInputPublication.js +93 -0
  21. package/lib/compiler/internal/build/WatchInputPublication.js.map +1 -0
  22. package/lib/compiler/internal/build/runBuild.d.ts +29 -2
  23. package/lib/compiler/internal/build/runBuild.js +45 -19
  24. package/lib/compiler/internal/build/runBuild.js.map +1 -1
  25. package/lib/compiler/internal/buildNativeCompiler.d.ts +2 -0
  26. package/lib/compiler/internal/buildNativeCompiler.js +1 -0
  27. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  28. package/lib/compiler/internal/compileProjectInMemory.js +4 -2
  29. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  30. package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +7 -2
  31. package/lib/compiler/internal/project/resolveOwningProjectConfig.js +6 -3
  32. package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
  33. package/lib/compiler/internal/readEffectiveCompilerOptions.js +1 -1
  34. package/lib/compiler/internal/readEffectiveCompilerOptions.js.map +1 -1
  35. package/lib/compiler/internal/resolveBinary.d.ts +1 -0
  36. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  37. package/lib/compiler/internal/resolveTsgo.d.ts +6 -0
  38. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  39. package/lib/compiler/internal/runSingleFileEmit.d.ts +28 -1
  40. package/lib/compiler/internal/runSingleFileEmit.js +120 -86
  41. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  42. package/lib/compiler/internal/startResidentTransform.js +8 -2
  43. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  44. package/lib/compiler/internal/transformProjectInMemory.js +11 -3
  45. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  46. package/lib/compiler/internal/transformProjectInWorker.d.ts +1 -1
  47. package/lib/compiler/internal/transformProjectInWorker.js +3 -2
  48. package/lib/compiler/internal/transformProjectInWorker.js.map +1 -1
  49. package/lib/internal/workerModuleUrl.d.ts +28 -0
  50. package/lib/internal/workerModuleUrl.js +40 -0
  51. package/lib/internal/workerModuleUrl.js.map +1 -0
  52. package/lib/launcher/internal/buildSingleRootProject.js +2 -1
  53. package/lib/launcher/internal/buildSingleRootProject.js.map +1 -1
  54. package/lib/launcher/internal/getCompilerVersionText.js +3 -2
  55. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  56. package/lib/launcher/internal/prepareExecution.js +2 -1
  57. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  58. package/lib/launcher/internal/runTtsc.js +91 -41
  59. package/lib/launcher/internal/runTtsc.js.map +1 -1
  60. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.d.ts +10 -6
  61. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js +11 -9
  62. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js.map +1 -1
  63. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.d.ts +20 -0
  64. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js +27 -0
  65. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js.map +1 -0
  66. package/lib/launcher/internal/runtime/installRuntimeHooks.d.ts +5 -3
  67. package/lib/launcher/internal/runtime/installRuntimeHooks.js +7 -3
  68. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  69. package/lib/launcher/internal/watch/AsyncWatchTopology.d.ts +113 -0
  70. package/lib/launcher/internal/watch/AsyncWatchTopology.js +232 -0
  71. package/lib/launcher/internal/watch/AsyncWatchTopology.js.map +1 -0
  72. package/lib/launcher/internal/watch/WatchSettlement.d.ts +49 -0
  73. package/lib/launcher/internal/watch/WatchSettlement.js +70 -0
  74. package/lib/launcher/internal/watch/WatchSettlement.js.map +1 -0
  75. package/lib/launcher/internal/watch/WatchTopology.d.ts +29 -1
  76. package/lib/launcher/internal/watch/WatchTopology.js +86 -10
  77. package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
  78. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.d.ts +121 -0
  79. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js +3 -0
  80. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js.map +1 -0
  81. package/lib/launcher/internal/watch/serveWatchTopologyWorker.d.ts +19 -0
  82. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js +62 -0
  83. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js.map +1 -0
  84. package/lib/launcher/internal/watch/watchTopologyWorker.d.ts +1 -0
  85. package/lib/launcher/internal/watch/watchTopologyWorker.js +22 -0
  86. package/lib/launcher/internal/watch/watchTopologyWorker.js.map +1 -0
  87. package/lib/plugin/CapabilityPluginResolver.d.ts +1 -1
  88. package/lib/plugin/CapabilityPluginResolver.js +3 -2
  89. package/lib/plugin/CapabilityPluginResolver.js.map +1 -1
  90. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +15 -12
  91. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +13 -11
  92. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  93. package/lib/plugin/internal/load/PluginPackageResolution.d.ts +2 -2
  94. package/lib/plugin/internal/load/PluginPackageResolution.js +13 -7
  95. package/lib/plugin/internal/load/PluginPackageResolution.js.map +1 -1
  96. package/lib/plugin/internal/load/ProjectPluginEntries.d.ts +23 -7
  97. package/lib/plugin/internal/load/ProjectPluginEntries.js +36 -14
  98. package/lib/plugin/internal/load/ProjectPluginEntries.js.map +1 -1
  99. package/lib/plugin/internal/load/collectProjectHostInputs.d.ts +3 -1
  100. package/lib/plugin/internal/load/collectProjectHostInputs.js +5 -2
  101. package/lib/plugin/internal/load/collectProjectHostInputs.js.map +1 -1
  102. package/lib/plugin/internal/load/hasProjectPluginEntries.d.ts +3 -1
  103. package/lib/plugin/internal/load/hasProjectPluginEntries.js +4 -2
  104. package/lib/plugin/internal/load/hasProjectPluginEntries.js.map +1 -1
  105. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +36 -5
  106. package/lib/plugin/internal/load/loadProjectPlugins.js +466 -416
  107. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  108. package/lib/plugin/internal/load/pluginResolutionFailure.d.ts +18 -0
  109. package/lib/plugin/internal/load/pluginResolutionFailure.js +32 -0
  110. package/lib/plugin/internal/load/pluginResolutionFailure.js.map +1 -0
  111. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +1 -1
  112. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +3 -2
  113. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  114. package/lib/structures/ITtscCompilerContext.d.ts +12 -12
  115. package/lib/structures/ITtscPlugin.d.ts +7 -5
  116. package/lib/structures/ITtscPluginFactoryContext.d.ts +9 -8
  117. package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +12 -6
  118. package/package.json +9 -9
  119. package/src/TtscCompiler.ts +1 -1
  120. package/src/compiler/internal/ResidentCheckProcess.ts +159 -374
  121. package/src/compiler/internal/ResidentCheckProcessHost.ts +502 -0
  122. package/src/compiler/internal/ResidentCheckProcessProtocol.ts +53 -0
  123. package/src/compiler/internal/build/BuildExecution.ts +118 -9
  124. package/src/compiler/internal/build/ResidentCheckWatchSession.ts +41 -24
  125. package/src/compiler/internal/build/RunBuildOptions.ts +9 -5
  126. package/src/compiler/internal/build/WatchInputPublication.ts +106 -0
  127. package/src/compiler/internal/build/runBuild.ts +58 -34
  128. package/src/compiler/internal/buildNativeCompiler.ts +4 -0
  129. package/src/compiler/internal/compileProjectInMemory.ts +8 -2
  130. package/src/compiler/internal/project/resolveOwningProjectConfig.ts +16 -5
  131. package/src/compiler/internal/readEffectiveCompilerOptions.ts +1 -1
  132. package/src/compiler/internal/resolveBinary.ts +4 -1
  133. package/src/compiler/internal/resolveTsgo.ts +7 -0
  134. package/src/compiler/internal/runSingleFileEmit.ts +139 -99
  135. package/src/compiler/internal/startResidentTransform.ts +9 -2
  136. package/src/compiler/internal/transformProjectInMemory.ts +12 -3
  137. package/src/compiler/internal/transformProjectInWorker.ts +10 -2
  138. package/src/internal/workerModuleUrl.ts +34 -0
  139. package/src/launcher/internal/buildSingleRootProject.ts +2 -0
  140. package/src/launcher/internal/getCompilerVersionText.ts +3 -2
  141. package/src/launcher/internal/prepareExecution.ts +2 -0
  142. package/src/launcher/internal/runTtsc.ts +123 -45
  143. package/src/launcher/internal/runtime/CommonJsRuntimeSource.ts +11 -9
  144. package/src/launcher/internal/runtime/advertiseTypeScriptExtensions.ts +25 -0
  145. package/src/launcher/internal/runtime/installRuntimeHooks.ts +7 -3
  146. package/src/launcher/internal/watch/AsyncWatchTopology.ts +267 -0
  147. package/src/launcher/internal/watch/WatchSettlement.ts +67 -0
  148. package/src/launcher/internal/watch/WatchTopology.ts +84 -13
  149. package/src/launcher/internal/watch/WatchTopologyWorkerProtocol.ts +106 -0
  150. package/src/launcher/internal/watch/serveWatchTopologyWorker.ts +75 -0
  151. package/src/launcher/internal/watch/watchTopologyWorker.ts +29 -0
  152. package/src/plugin/CapabilityPluginResolver.ts +9 -2
  153. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +17 -12
  154. package/src/plugin/internal/load/PluginPackageResolution.ts +14 -9
  155. package/src/plugin/internal/load/ProjectPluginEntries.ts +40 -15
  156. package/src/plugin/internal/load/collectProjectHostInputs.ts +7 -1
  157. package/src/plugin/internal/load/hasProjectPluginEntries.ts +8 -3
  158. package/src/plugin/internal/load/loadProjectPlugins.ts +672 -594
  159. package/src/plugin/internal/load/pluginResolutionFailure.ts +37 -0
  160. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +8 -2
  161. package/src/structures/ITtscCompilerContext.ts +12 -12
  162. package/src/structures/ITtscPlugin.ts +7 -5
  163. package/src/structures/ITtscPluginFactoryContext.ts +9 -8
  164. package/src/structures/internal/TtscSingleFileEmitOptions.ts +22 -11
@@ -52,6 +52,8 @@ import { selectReferencedProject } from "./selectReferencedProject";
52
52
  * @param props.tsconfig - The config project discovery found for the file.
53
53
  * @param props.file - The file whose project is asked for.
54
54
  * @param props.binary - An explicit TypeScript-Go binary, when one was given.
55
+ * @param props.env - Complete environment shared by compiler selection and
56
+ * root-file expansion; omission inherits the current process environment.
55
57
  * @param props.onConfig - Called with every config this reads, so a caller that
56
58
  * fingerprints its inputs can record them.
57
59
  * @evidence contracts/common.md#principled-implementation Successful compiler showConfig supplies root membership observations, best-effort filesystem identity compares aliases, and visited identity keys terminate repeated graph states while declaration-order DFS selects the first observed containing project. Failed observations preserve the discovered fallback without certifying membership.
@@ -76,13 +78,17 @@ export function resolveOwningProjectConfig(props: {
76
78
  */
77
79
  binary?: string;
78
80
 
81
+ /** Complete environment for compiler selection and root-file expansion. */
82
+ env?: NodeJS.ProcessEnv;
83
+
79
84
  /** Observe each directly read config, including ones with no reference edges. */
80
85
  onConfig?: (config: string) => void;
81
86
 
82
87
  /**
83
88
  * Expanded root files kept across lookups, by config path, for a caller whose
84
- * selected builds already hold for the same lifetime: ttsx builds each owning
85
- * project once per process (samchon/ttsc#1747).
89
+ * selected builds and compiler environment already hold for the same
90
+ * lifetime: ttsx builds each owning project once per process
91
+ * (samchon/ttsc#1747).
86
92
  */
87
93
  expansions?: Map<string, { files: readonly string[]; takenAt: number }>;
88
94
  }): string {
@@ -128,7 +134,7 @@ export function resolveOwningProjectConfig(props: {
128
134
 
129
135
  /** Expand `config` now, and keep the expansion when the caller asked to. */
130
136
  function expand(config: string): RootFiles {
131
- const roots = rootFiles(config, props.binary);
137
+ const roots = rootFiles(config, props.binary, props.env);
132
138
  props.expansions?.set(config, roots);
133
139
  return roots;
134
140
  }
@@ -214,14 +220,19 @@ interface RootFiles {
214
220
  * include approximation or permanent failed answer substitutes for compiler
215
221
  * behavior.
216
222
  */
217
- function rootFiles(config: string, binary: string | undefined): RootFiles {
223
+ function rootFiles(
224
+ config: string,
225
+ binary: string | undefined,
226
+ env: NodeJS.ProcessEnv | undefined,
227
+ ): RootFiles {
218
228
  const takenAt = Date.now();
219
229
  let files: string[] = [];
220
230
  try {
221
231
  const directory = path.dirname(config);
222
- const tsgo = resolveTsgo({ binary, cwd: directory });
232
+ const tsgo = resolveTsgo({ binary, cwd: directory, env });
223
233
  const result = spawnNative(tsgo.binary, ["-p", config, "--showConfig"], {
224
234
  cwd: directory,
235
+ env,
225
236
  encoding: "utf8",
226
237
  });
227
238
  if (result.status === 0) {
@@ -84,7 +84,7 @@ export function readEffectiveCompilerOptions(
84
84
  } catch {
85
85
  return null;
86
86
  }
87
- const tsgo = resolveTsgo({ cwd: project.root, binary });
87
+ const tsgo = resolveTsgo({ cwd: project.root, binary, env });
88
88
  const result = spawnNative(
89
89
  tsgo.binary,
90
90
  ["-p", project.path, ...passthrough, "-p", project.path, "--showConfig"],
@@ -29,7 +29,10 @@ import { SidecarEnvironment } from "./sharedHost/SidecarEnvironment";
29
29
  * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Resolution owns no persistent map, descriptor or task and returns only the selected path spelling.
30
30
  */
31
31
  export function resolveBinary(
32
- opts: { env?: NodeJS.ProcessEnv } = {},
32
+ opts: {
33
+ /** Complete selection environment; omission reads the current process. */
34
+ env?: NodeJS.ProcessEnv;
35
+ } = {},
33
36
  ): string | null {
34
37
  const env = opts.env ?? process.env;
35
38
  const explicit = SidecarEnvironment.read(env, "TTSC_BINARY");
@@ -41,6 +41,13 @@ export function resolveTsgo(
41
41
 
42
42
  /** Directory from which to discover `typescript`. */
43
43
  cwd?: string;
44
+
45
+ /**
46
+ * Complete environment for executable selection; omission reads the current
47
+ * process environment. Public compiler owners merge caller overrides before
48
+ * invoking this resolver, retaining their original overlay for channel
49
+ * ownership.
50
+ */
44
51
  env?: NodeJS.ProcessEnv;
45
52
 
46
53
  /**
@@ -4,10 +4,12 @@ import path from "node:path";
4
4
  import { createCanonicalTempDirectory } from "../../internal/createCanonicalTempDirectory";
5
5
  import type { TtscSingleFileEmitOptions } from "../../structures/internal/TtscSingleFileEmitOptions";
6
6
  import { EmitOwnershipIndex } from "./EmitOwnershipIndex";
7
+ import { WatchInputPublication } from "./build/WatchInputPublication";
7
8
  import { runBuild } from "./build/runBuild";
8
9
  import { CompilerProjectSelection } from "./project/CompilerProjectSelection";
9
10
  import { readProjectConfig } from "./project/readProjectConfig";
10
11
  import { readEffectiveCompilerOptions } from "./readEffectiveCompilerOptions";
12
+ import { SidecarEnvironment } from "./sharedHost/SidecarEnvironment";
11
13
 
12
14
  /**
13
15
  * Emit one source file by building its project into a temporary directory.
@@ -24,7 +26,8 @@ import { readEffectiveCompilerOptions } from "./readEffectiveCompilerOptions";
24
26
  * argument cwd with public output planning. Response freshness is checked
25
27
  * before native work and publication; these sequential checks do not pin bytes
26
28
  * atomically. Callers without that record retain explicit config/nearest-file
27
- * discovery.
29
+ * discovery. Watch-input handlers must complete synchronously; use
30
+ * {@link runSingleFileEmit.async} for promise-returning handlers.
28
31
  *
29
32
  * @returns The transformed JavaScript source text.
30
33
  * @throws When the build exits non-zero, proves no JavaScript for the requested
@@ -44,110 +47,147 @@ export function runSingleFileEmit(
44
47
  options: TtscSingleFileEmitOptions,
45
48
  selection?: ReturnType<typeof CompilerProjectSelection.read>,
46
49
  ): string {
47
- const cwd = path.resolve(options.cwd ?? process.cwd());
48
- const sourceFile = realpathIfExists(
49
- path.isAbsolute(options.file)
50
- ? options.file
51
- : path.resolve(cwd, options.file),
52
- );
53
- const project =
54
- selection?.project ??
55
- readProjectConfig({
56
- cwd,
57
- file: options.file,
58
- projectRoot: options.projectRoot,
59
- tsconfig: options.tsconfig,
60
- });
61
- const tsconfig = project.path;
62
- const compilerArgsCwd = selection?.compilerArgsCwd ?? project.root;
63
- if (selection !== undefined)
64
- CompilerProjectSelection.assertCurrent(selection);
65
- // The root tsgo lays the outputs against: a `--rootDir` forwarded on the
66
- // command line, which reaches it after the config; otherwise the declared
67
- // one, which `readProjectConfig` already absolutized; otherwise the project's
68
- // own directory, which `pinInferredRootDir` hands it below.
69
- const effective = readEffectiveCompilerOptions(
70
- project,
71
- options.passthrough,
72
- options.binary,
73
- options.env,
74
- compilerArgsCwd,
75
- )?.("rootDir");
76
- const rootDir =
77
- typeof effective === "string"
78
- ? path.resolve(compilerArgsCwd, effective)
79
- : project.root;
80
- if (selection !== undefined)
81
- CompilerProjectSelection.assertCurrent(selection);
82
- const outDir = createCanonicalTempDirectory("ttsc-single-file-");
83
- let failed = false;
84
- let failure: unknown;
85
- try {
86
- const result = runBuild({
87
- ...options,
88
- cwd,
50
+ return WatchInputPublication.run(runSingleFileEmit.steps(options, selection));
51
+ }
52
+
53
+ /**
54
+ * Keep synchronous and awaited single-file emission on one temporary lifetime.
55
+ *
56
+ * @evidence contracts/common.md#principled-implementation Both entries share project/root selection, producing build, response freshness, provenance lookup and temporary-output cleanup; awaited completion joins every nested publication before compilation and output selection.
57
+ * @evidence contracts/common.md#clear-and-simple-design One generator owns project emission, optional public output and cleanup for both completion modes; the companion adds a runner without another output-selection implementation.
58
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Pending registration cannot authorize a build, and rejected publication reaches the same temporary cleanup path. Completion mode preserves response checks and provenance instead of reconstructing source ownership from filenames.
59
+ * @evidence contracts/common.md#meaningful-documentation Each entry states completion mode and the shared temporary lifetime; sequence prose requires rejection delivery into its finally path, while the primary entry documents output selection and cleanup failures.
60
+ * @evidence contracts/portability.md#os-neutral-implementation The shared sequence retains existing native source/output identity, cwd bases and private-directory handling across suspension without another native boundary.
61
+ * @evidence contracts/performance.md#efficient-algorithms One project selection and full-project build serve one provenance lookup; each handoff is awaited once with no additional source or output traversal.
62
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work Each sequence owns fresh compiler effects rather than an output cache or shared generation; higher-level owners decide equivalent build reuse.
63
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One accepted private output directory spans nested handoff and build work, and the existing finally path removes it on success or failure and preserves cleanup error aggregation.
64
+ */
65
+ export namespace runSingleFileEmit {
66
+ /**
67
+ * Await watch-input registration before emitting and selecting one source's
68
+ * JavaScript. Rejected publication follows the same temporary cleanup path.
69
+ */
70
+ export function async(
71
+ options: TtscSingleFileEmitOptions,
72
+ selection?: ReturnType<typeof CompilerProjectSelection.read>,
73
+ ): Promise<string> {
74
+ return WatchInputPublication.runAsync(steps(options, selection));
75
+ }
76
+
77
+ /**
78
+ * Emit through one selected project while suspending at watch handoffs.
79
+ * Internal completion runners must deliver rejection into this sequence so
80
+ * its finally block retains temporary-output cleanup ownership.
81
+ */
82
+ export function* steps(
83
+ options: TtscSingleFileEmitOptions,
84
+ selection?: ReturnType<typeof CompilerProjectSelection.read>,
85
+ ): WatchInputPublication.Sequence<string> {
86
+ const cwd = path.resolve(options.cwd ?? process.cwd());
87
+ const sourceFile = realpathIfExists(
88
+ path.isAbsolute(options.file)
89
+ ? options.file
90
+ : path.resolve(cwd, options.file),
91
+ );
92
+ const project =
93
+ selection?.project ??
94
+ readProjectConfig({
95
+ cwd,
96
+ file: options.file,
97
+ projectRoot: options.projectRoot,
98
+ tsconfig: options.tsconfig,
99
+ });
100
+ const tsconfig = project.path;
101
+ const compilerArgsCwd = selection?.compilerArgsCwd ?? project.root;
102
+ if (selection !== undefined)
103
+ CompilerProjectSelection.assertCurrent(selection);
104
+ // The root tsgo lays the outputs against: a `--rootDir` forwarded on the
105
+ // command line, which reaches it after the config; otherwise the declared
106
+ // one, which `readProjectConfig` already absolutized; otherwise the project's
107
+ // own directory, which `pinInferredRootDir` hands it below.
108
+ const effective = readEffectiveCompilerOptions(
109
+ project,
110
+ options.passthrough,
111
+ options.binary,
112
+ SidecarEnvironment.merge(process.env, options.env),
89
113
  compilerArgsCwd,
90
- emit: true,
91
- forceEmitProvenance: true,
92
- isolateOutputsTo: outDir,
93
- outDir,
94
- // The private temp directory above is an `outDir` this lane injected, not
95
- // one the project declared, and tsgo answers an inferred common source
96
- // directory with TS5011 as soon as any `outDir` is in play. Pinning the
97
- // root tsgo would infer keeps `ttsc <file.ts>` working on a project that
98
- // declares no output at all, while the producing invocation reports its
99
- // actual source/output ownership separately.
100
- pinInferredRootDir: true,
101
- resolvedProject: project,
102
- tsconfig,
103
- });
104
- if (result.status !== 0) {
105
- throw new Error(
106
- "ttsc single-file emit exited " +
107
- result.status +
108
- "\n" +
109
- (result.stderr || result.stdout),
110
- );
111
- }
112
- const emitted = new EmitOwnershipIndex({
113
- emitDir: outDir,
114
- rootDir,
115
- emittedSources: result.emittedSources,
116
- emittedSourceProofFailures: result.emittedSourceProofFailures,
117
- outputs: EmitOwnershipIndex.listOutputs(outDir),
118
- }).find(sourceFile);
119
- if (emitted === null) {
120
- throw new Error(
121
- `ttsc single-file emit: ${tsconfig} emitted no JavaScript owned by ${sourceFile}; check that the project includes the source and its options permit JavaScript emission`,
122
- );
123
- }
124
- const transformed = fs.readFileSync(emitted, "utf8");
114
+ )?.("rootDir");
115
+ const rootDir =
116
+ typeof effective === "string"
117
+ ? path.resolve(compilerArgsCwd, effective)
118
+ : project.root;
125
119
  if (selection !== undefined)
126
120
  CompilerProjectSelection.assertCurrent(selection);
127
- if (options.out) {
128
- const target = path.isAbsolute(options.out)
129
- ? options.out
130
- : path.resolve(cwd, options.out);
131
- fs.mkdirSync(path.dirname(target), { recursive: true });
132
- fs.writeFileSync(target, transformed, "utf8");
133
- }
134
- return transformed;
135
- } catch (error) {
136
- failed = true;
137
- failure = error;
138
- throw error;
139
- } finally {
121
+ const outDir = createCanonicalTempDirectory("ttsc-single-file-");
122
+ let failed = false;
123
+ let failure: unknown;
140
124
  try {
141
- fs.rmSync(outDir, { recursive: true, force: true });
142
- } catch (cleanupError) {
143
- if (failed) {
144
- throw new AggregateError(
145
- [failure, cleanupError],
146
- "ttsc: single-file emission and temporary output cleanup failed",
147
- { cause: failure },
125
+ const result = yield* runBuild.steps({
126
+ ...options,
127
+ cwd,
128
+ compilerArgsCwd,
129
+ emit: true,
130
+ forceEmitProvenance: true,
131
+ isolateOutputsTo: outDir,
132
+ outDir,
133
+ // The private temp directory above is an `outDir` this lane injected, not
134
+ // one the project declared, and tsgo answers an inferred common source
135
+ // directory with TS5011 as soon as any `outDir` is in play. Pinning the
136
+ // root tsgo would infer keeps `ttsc <file.ts>` working on a project that
137
+ // declares no output at all, while the producing invocation reports its
138
+ // actual source/output ownership separately.
139
+ pinInferredRootDir: true,
140
+ resolvedProject: project,
141
+ tsconfig,
142
+ });
143
+ if (result.status !== 0) {
144
+ throw new Error(
145
+ "ttsc single-file emit exited " +
146
+ result.status +
147
+ "\n" +
148
+ (result.stderr || result.stdout),
148
149
  );
149
150
  }
150
- throw cleanupError;
151
+ const emitted = new EmitOwnershipIndex({
152
+ emitDir: outDir,
153
+ rootDir,
154
+ emittedSources: result.emittedSources,
155
+ emittedSourceProofFailures: result.emittedSourceProofFailures,
156
+ outputs: EmitOwnershipIndex.listOutputs(outDir),
157
+ }).find(sourceFile);
158
+ if (emitted === null) {
159
+ throw new Error(
160
+ `ttsc single-file emit: ${tsconfig} emitted no JavaScript owned by ${sourceFile}; check that the project includes the source and its options permit JavaScript emission`,
161
+ );
162
+ }
163
+ const transformed = fs.readFileSync(emitted, "utf8");
164
+ if (selection !== undefined)
165
+ CompilerProjectSelection.assertCurrent(selection);
166
+ if (options.out) {
167
+ const target = path.isAbsolute(options.out)
168
+ ? options.out
169
+ : path.resolve(cwd, options.out);
170
+ fs.mkdirSync(path.dirname(target), { recursive: true });
171
+ fs.writeFileSync(target, transformed, "utf8");
172
+ }
173
+ return transformed;
174
+ } catch (error) {
175
+ failed = true;
176
+ failure = error;
177
+ throw error;
178
+ } finally {
179
+ try {
180
+ fs.rmSync(outDir, { recursive: true, force: true });
181
+ } catch (cleanupError) {
182
+ if (failed) {
183
+ throw new AggregateError(
184
+ [failure, cleanupError],
185
+ "ttsc: single-file emission and temporary output cleanup failed",
186
+ { cause: failure },
187
+ );
188
+ }
189
+ throw cleanupError;
190
+ }
151
191
  }
152
192
  }
153
193
  }
@@ -62,7 +62,10 @@ export function startResidentTransform(
62
62
  tsconfig: context.tsconfig,
63
63
  });
64
64
  const loaded = loadProjectPlugins({
65
- binary: resolveBinary(context) ?? "",
65
+ binary:
66
+ resolveBinary({
67
+ env: SidecarEnvironment.merge(process.env, context.env),
68
+ }) ?? "",
66
69
  cacheDir:
67
70
  context.cacheDir ??
68
71
  SidecarEnvironment.read(context.env, "TTSC_CACHE_DIR"),
@@ -85,7 +88,11 @@ export function startResidentTransform(
85
88
  assertSharedHostCompatibility(transformers, "source-to-source");
86
89
 
87
90
  const host = selectSharedHostPlugin(transformers);
88
- const tsgoBinary = resolveTsgo({ ...context, cwd: project.root }).binary;
91
+ const tsgoBinary = resolveTsgo({
92
+ ...context,
93
+ cwd: project.root,
94
+ env: SidecarEnvironment.merge(process.env, context.env),
95
+ }).binary;
89
96
  const resident = new ResidentTransformProcess({
90
97
  args: [
91
98
  "serve",
@@ -109,7 +109,10 @@ export function transformProjectInMemory(options: ITtscCompilerContext): {
109
109
  } {
110
110
  const cwd = path.resolve(options.cwd ?? process.cwd());
111
111
  const loaded = loadProjectPlugins({
112
- binary: resolveBinary(options) ?? "",
112
+ binary:
113
+ resolveBinary({
114
+ env: SidecarEnvironment.merge(process.env, options.env),
115
+ }) ?? "",
113
116
  cacheDir:
114
117
  options.cacheDir ??
115
118
  SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
@@ -188,11 +191,13 @@ function transformProjectWithNativeHost(
188
191
  baseline?.hostInputs ?? [],
189
192
  projectHostInputs,
190
193
  );
194
+ const env = inheritedSidecarEnv(options.env);
191
195
  const binary = buildNativeCompiler({
192
196
  cacheBaseDir: project.root,
193
197
  cacheDir:
194
198
  options.cacheDir ??
195
199
  SidecarEnvironment.read(options.env, "TTSC_CACHE_DIR"),
200
+ env,
196
201
  packageRoot: packageRootDir(),
197
202
  });
198
203
  const res = spawnNative(
@@ -200,7 +205,7 @@ function transformProjectWithNativeHost(
200
205
  ["api-transform", "--cwd", project.root, "--tsconfig", project.path],
201
206
  {
202
207
  cwd: project.root,
203
- env: inheritedSidecarEnv(options.env),
208
+ env,
204
209
  },
205
210
  );
206
211
  if (res.error) {
@@ -295,7 +300,11 @@ function transformProjectWithPlugins(
295
300
  const tsgoBinary =
296
301
  loaded.nativePlugins.length === 0
297
302
  ? ""
298
- : resolveTsgo({ ...options, cwd: project.root }).binary;
303
+ : resolveTsgo({
304
+ ...options,
305
+ cwd: project.root,
306
+ env: SidecarEnvironment.merge(process.env, options.env),
307
+ }).binary;
299
308
  const checked = runNativeChecks(
300
309
  options,
301
310
  project,
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { Worker } from "node:worker_threads";
4
4
 
5
5
  import { CompilerContextSnapshot } from "../../internal/CompilerContextSnapshot";
6
+ import { workerModuleUrl } from "../../internal/workerModuleUrl";
6
7
  import type { ITtscCompilerContext } from "../../structures/ITtscCompilerContext";
7
8
  import type { TransformProjectWorkerReply } from "./TransformProjectWorkerReply";
8
9
  import type { TransformProjectWorkerRequest } from "./TransformProjectWorkerRequest";
@@ -50,7 +51,7 @@ import type { transformProjectInMemory } from "./transformProjectInMemory";
50
51
  * @evidence contracts/common.md#clear-and-simple-design The public operation owns checkout, exclusive event listeners and return to the idle pool; two private helpers isolate idle-worker retirement from active request settlement.
51
52
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Blocking work runs in a supported worker boundary rather than rewriting the caller's environment around synchronous work; failures preserve real exceptions and do not substitute measured or expected results.
52
53
  * @evidence contracts/common.md#meaningful-documentation Native paragraphs explain why all transform phases move off-thread, environment isolation, warm reuse and retained-population limits following the documentation skill.
53
- * @evidence contracts/portability.md#os-neutral-implementation Worker entry is a native path and the environment snapshot uses native name identity; Windows worker copies are case-sensitive, so canonical merging prevents aliases from defeating caller overrides inside the worker.
54
+ * @evidence contracts/portability.md#os-neutral-implementation A supported module URL requires the native CommonJS source or emitted worker entry while Node retains inherited execution options and preload hooks. The environment snapshot uses native name identity; Windows worker copies are case-sensitive, so canonical merging prevents aliases from defeating caller overrides inside the worker.
54
55
  * @evidence contracts/performance.md#efficient-algorithms Pool checkout removes one entry and listeners; environment merge and plugin JSON serialization visit caller data before structured cloning, whose cost includes context, serialized payload and environment bytes. Caller JSON conversion can add arbitrary work. Idle-worker retirement searches/compacts at most the pool budget; worker startup and native transform costs remain delegated, and output cloning is transport work rather than a second transform.
55
56
  * @evidence contracts/performance.md#reuse-equivalent-work An exclusively checked-out warm worker reuses the loader's input-validated caches; each request adopts its complete current environment and still executes the transform, so thread reuse is not an unsupported response cache.
56
57
  * @evidence contracts/performance.md#bound-retention-and-release-resources At most the module-initialization CPU budget remains in the idle pool with event-loop references removed. Excess/failed workers receive unawaited termination requests; retiring native threads are not covered by the reusable-entry count. Terminal settlement removes active listeners, while pooled module caches retain their owning data policy. Active requests and live transforms have no concurrency cap or implicit deadline.
@@ -65,7 +66,14 @@ export function transformProjectInWorker(
65
66
  };
66
67
  const worker =
67
68
  takeIdleWorker() ??
68
- new Worker(path.join(__dirname, "transformProjectWorker.js"));
69
+ new Worker(
70
+ workerModuleUrl(
71
+ path.join(
72
+ __dirname,
73
+ `transformProjectWorker${path.extname(__filename)}`,
74
+ ),
75
+ ),
76
+ );
69
77
  worker.ref();
70
78
  return new Promise((resolve, reject) => {
71
79
  let settled = false;
@@ -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 tsgo = resolveTsgo(options);
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: SidecarEnvironment.merge(process.env, options.env),
31
+ env,
31
32
  });
32
33
  if (res.error || res.status !== 0) {
33
34
  throw new Error(
@@ -324,6 +324,7 @@ function createProjectContext(
324
324
  project,
325
325
  options.passthrough,
326
326
  options.binary,
327
+ env,
327
328
  );
328
329
  const privateEmitRootDir = resolveRuntimeSourceRoot(
329
330
  project,
@@ -440,6 +441,7 @@ function discoverOwningProject(
440
441
  });
441
442
  const owning = resolveOwningProjectConfig({
442
443
  binary: options.binary,
444
+ env: SidecarEnvironment.merge(process.env, options.env),
443
445
  file,
444
446
  tsconfig: nearest.path,
445
447
  });