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.
Files changed (261) hide show
  1. package/cmd/ttsc/compiler_argument_cwd_test.go +38 -38
  2. package/cmd/ttscserver/main.go +2 -12
  3. package/cmd/ttscserver/resolve_launch_uses_native_flag_authority_test.go +7 -7
  4. package/driver/lsp.go +4 -43
  5. package/driver/rewrite.go +1 -1
  6. package/internal/e2etrace/completion_hints.go +3 -3
  7. package/internal/e2etrace/completion_hints_test.go +26 -12
  8. package/internal/graph/build.go +5 -6
  9. package/internal/graph/graph.go +3 -3
  10. package/internal/graph/line_model.go +2 -35
  11. package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +13 -36
  12. package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +9 -3
  13. package/internal/lspserver/lsp_plugin.go +19 -0
  14. package/internal/lspserver/lsp_proxy.go +9 -129
  15. package/internal/lspserver/lsp_server.go +0 -8
  16. package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +6 -36
  17. package/internal/sourceprocess/windows_target_owns_suspended_admission_test.go +5 -5
  18. package/lib/TtscCompiler.js +1 -1
  19. package/lib/TtscCompiler.js.map +1 -1
  20. package/lib/compiler/internal/ResidentCheckProcess.d.ts +74 -96
  21. package/lib/compiler/internal/ResidentCheckProcess.js +150 -335
  22. package/lib/compiler/internal/ResidentCheckProcess.js.map +1 -1
  23. package/lib/compiler/internal/ResidentCheckProcessHost.d.ts +127 -0
  24. package/lib/compiler/internal/ResidentCheckProcessHost.js +447 -0
  25. package/lib/compiler/internal/ResidentCheckProcessHost.js.map +1 -0
  26. package/lib/compiler/internal/ResidentCheckProcessProtocol.d.ts +64 -0
  27. package/lib/compiler/internal/ResidentCheckProcessProtocol.js +3 -0
  28. package/lib/compiler/internal/ResidentCheckProcessProtocol.js.map +1 -0
  29. package/lib/compiler/internal/build/BuildExecution.d.ts +103 -1
  30. package/lib/compiler/internal/build/BuildExecution.js +92 -12
  31. package/lib/compiler/internal/build/BuildExecution.js.map +1 -1
  32. package/lib/compiler/internal/build/ResidentCheckWatchSession.d.ts +16 -11
  33. package/lib/compiler/internal/build/ResidentCheckWatchSession.js +37 -21
  34. package/lib/compiler/internal/build/ResidentCheckWatchSession.js.map +1 -1
  35. package/lib/compiler/internal/build/RunBuildOptions.d.ts +6 -5
  36. package/lib/compiler/internal/build/TsgoArguments.js.map +1 -1
  37. package/lib/compiler/internal/build/WatchInputPublication.d.ts +59 -0
  38. package/lib/compiler/internal/build/WatchInputPublication.js +93 -0
  39. package/lib/compiler/internal/build/WatchInputPublication.js.map +1 -0
  40. package/lib/compiler/internal/build/runBuild.d.ts +29 -2
  41. package/lib/compiler/internal/build/runBuild.js +45 -19
  42. package/lib/compiler/internal/build/runBuild.js.map +1 -1
  43. package/lib/compiler/internal/buildNativeCompiler.d.ts +2 -0
  44. package/lib/compiler/internal/buildNativeCompiler.js +1 -0
  45. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  46. package/lib/compiler/internal/compileProjectInMemory.js +4 -2
  47. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  48. package/lib/compiler/internal/project/CompilerProjectSelection.d.ts +9 -8
  49. package/lib/compiler/internal/project/CompilerProjectSelection.js +11 -9
  50. package/lib/compiler/internal/project/CompilerProjectSelection.js.map +1 -1
  51. package/lib/compiler/internal/project/resolveOwningProjectConfig.d.ts +25 -8
  52. package/lib/compiler/internal/project/resolveOwningProjectConfig.js +26 -14
  53. package/lib/compiler/internal/project/resolveOwningProjectConfig.js.map +1 -1
  54. package/lib/compiler/internal/readEffectiveCompilerOptions.js +1 -1
  55. package/lib/compiler/internal/readEffectiveCompilerOptions.js.map +1 -1
  56. package/lib/compiler/internal/resolveBinary.d.ts +1 -0
  57. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  58. package/lib/compiler/internal/resolveTsgo.d.ts +6 -0
  59. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  60. package/lib/compiler/internal/runSingleFileEmit.d.ts +32 -4
  61. package/lib/compiler/internal/runSingleFileEmit.js +124 -89
  62. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  63. package/lib/compiler/internal/startResidentTransform.js +8 -2
  64. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  65. package/lib/compiler/internal/transformProjectInMemory.js +11 -3
  66. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  67. package/lib/compiler/internal/transformProjectInWorker.d.ts +1 -1
  68. package/lib/compiler/internal/transformProjectInWorker.js +3 -2
  69. package/lib/compiler/internal/transformProjectInWorker.js.map +1 -1
  70. package/lib/flags/ParseOptions.d.ts +2 -2
  71. package/lib/internal/E2ETrace.js.map +1 -1
  72. package/lib/internal/OwnedNativeProcess.d.ts +2 -2
  73. package/lib/internal/OwnedNativeProcess.js +2 -2
  74. package/lib/internal/SourceNativeRetirement.d.ts +8 -7
  75. package/lib/internal/SourceNativeRetirement.js +19 -10
  76. package/lib/internal/SourceNativeRetirement.js.map +1 -1
  77. package/lib/internal/TtsxNodeOptions.d.ts +79 -0
  78. package/lib/internal/TtsxNodeOptions.js +121 -0
  79. package/lib/internal/TtsxNodeOptions.js.map +1 -0
  80. package/lib/internal/javascriptRuntimeCapabilities.js +4 -1
  81. package/lib/internal/javascriptRuntimeCapabilities.js.map +1 -1
  82. package/lib/internal/parseWindowsDirectoryCaseSensitivity.d.ts +5 -5
  83. package/lib/internal/parseWindowsDirectoryCaseSensitivity.js +5 -5
  84. package/lib/internal/pathIdentity/createCaseSensitivityObservations.d.ts +35 -0
  85. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js +209 -0
  86. package/lib/internal/pathIdentity/createCaseSensitivityObservations.js.map +1 -0
  87. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js +2 -143
  88. package/lib/internal/pathIdentity/createFilesystemPathIdentityContext.js.map +1 -1
  89. package/lib/internal/projectInputPathIdentity.d.ts +1 -0
  90. package/lib/internal/projectInputPathIdentity.js +1 -0
  91. package/lib/internal/projectInputPathIdentity.js.map +1 -1
  92. package/lib/internal/workerModuleUrl.d.ts +28 -0
  93. package/lib/internal/workerModuleUrl.js +40 -0
  94. package/lib/internal/workerModuleUrl.js.map +1 -0
  95. package/lib/launcher/internal/buildSingleRootProject.js +2 -1
  96. package/lib/launcher/internal/buildSingleRootProject.js.map +1 -1
  97. package/lib/launcher/internal/getCompilerVersionText.js +3 -2
  98. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  99. package/lib/launcher/internal/parseTtsxCLI.d.ts +5 -4
  100. package/lib/launcher/internal/parseTtsxCLI.js +5 -4
  101. package/lib/launcher/internal/parseTtsxCLI.js.map +1 -1
  102. package/lib/launcher/internal/prepareExecution.js +2 -1
  103. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  104. package/lib/launcher/internal/resolveSingleFileOutput.d.ts +8 -9
  105. package/lib/launcher/internal/resolveSingleFileOutput.js +8 -9
  106. package/lib/launcher/internal/resolveSingleFileOutput.js.map +1 -1
  107. package/lib/launcher/internal/runTtsc.js +91 -41
  108. package/lib/launcher/internal/runTtsc.js.map +1 -1
  109. package/lib/launcher/internal/runTtsx.js +5 -2
  110. package/lib/launcher/internal/runTtsx.js.map +1 -1
  111. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.d.ts +10 -6
  112. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js +11 -9
  113. package/lib/launcher/internal/runtime/CommonJsRuntimeSource.js.map +1 -1
  114. package/lib/launcher/internal/runtime/RuntimeEntrySources.d.ts +40 -0
  115. package/lib/launcher/internal/runtime/RuntimeEntrySources.js +57 -0
  116. package/lib/launcher/internal/runtime/RuntimeEntrySources.js.map +1 -0
  117. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.d.ts +5 -4
  118. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js +8 -6
  119. package/lib/launcher/internal/runtime/RuntimeProjectOwnership.js.map +1 -1
  120. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.d.ts +20 -0
  121. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js +27 -0
  122. package/lib/launcher/internal/runtime/advertiseTypeScriptExtensions.js.map +1 -0
  123. package/lib/launcher/internal/runtime/commonJsExportNames.d.ts +2 -1
  124. package/lib/launcher/internal/runtime/commonJsExportNames.js +2 -1
  125. package/lib/launcher/internal/runtime/commonJsExportNames.js.map +1 -1
  126. package/lib/launcher/internal/runtime/installRuntimeHooks.d.ts +5 -3
  127. package/lib/launcher/internal/runtime/installRuntimeHooks.js +42 -12
  128. package/lib/launcher/internal/runtime/installRuntimeHooks.js.map +1 -1
  129. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js +3 -1
  130. package/lib/launcher/internal/runtime/resolveRuntimeCleanTargets.js.map +1 -1
  131. package/lib/launcher/internal/watch/AsyncWatchTopology.d.ts +113 -0
  132. package/lib/launcher/internal/watch/AsyncWatchTopology.js +232 -0
  133. package/lib/launcher/internal/watch/AsyncWatchTopology.js.map +1 -0
  134. package/lib/launcher/internal/watch/WatchSettlement.d.ts +49 -0
  135. package/lib/launcher/internal/watch/WatchSettlement.js +70 -0
  136. package/lib/launcher/internal/watch/WatchSettlement.js.map +1 -0
  137. package/lib/launcher/internal/watch/WatchTopology.d.ts +103 -13
  138. package/lib/launcher/internal/watch/WatchTopology.js +380 -95
  139. package/lib/launcher/internal/watch/WatchTopology.js.map +1 -1
  140. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.d.ts +121 -0
  141. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js +3 -0
  142. package/lib/launcher/internal/watch/WatchTopologyWorkerProtocol.js.map +1 -0
  143. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.d.ts +4 -1
  144. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js +3 -2
  145. package/lib/launcher/internal/watch/projectInputReloadEventShouldNotify.js.map +1 -1
  146. package/lib/launcher/internal/watch/serveWatchTopologyWorker.d.ts +19 -0
  147. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js +62 -0
  148. package/lib/launcher/internal/watch/serveWatchTopologyWorker.js.map +1 -0
  149. package/lib/launcher/internal/watch/watchTopologyWorker.d.ts +1 -0
  150. package/lib/launcher/internal/watch/watchTopologyWorker.js +22 -0
  151. package/lib/launcher/internal/watch/watchTopologyWorker.js.map +1 -0
  152. package/lib/plugin/CapabilityPluginResolver.d.ts +1 -1
  153. package/lib/plugin/CapabilityPluginResolver.js +3 -2
  154. package/lib/plugin/CapabilityPluginResolver.js.map +1 -1
  155. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.d.ts +15 -12
  156. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js +13 -11
  157. package/lib/plugin/internal/load/PluginDescriptorEvaluationCache.js.map +1 -1
  158. package/lib/plugin/internal/load/PluginPackageResolution.d.ts +2 -2
  159. package/lib/plugin/internal/load/PluginPackageResolution.js +13 -7
  160. package/lib/plugin/internal/load/PluginPackageResolution.js.map +1 -1
  161. package/lib/plugin/internal/load/ProjectPluginEntries.d.ts +23 -7
  162. package/lib/plugin/internal/load/ProjectPluginEntries.js +36 -14
  163. package/lib/plugin/internal/load/ProjectPluginEntries.js.map +1 -1
  164. package/lib/plugin/internal/load/collectProjectHostInputs.d.ts +3 -1
  165. package/lib/plugin/internal/load/collectProjectHostInputs.js +5 -2
  166. package/lib/plugin/internal/load/collectProjectHostInputs.js.map +1 -1
  167. package/lib/plugin/internal/load/hasProjectPluginEntries.d.ts +3 -1
  168. package/lib/plugin/internal/load/hasProjectPluginEntries.js +4 -2
  169. package/lib/plugin/internal/load/hasProjectPluginEntries.js.map +1 -1
  170. package/lib/plugin/internal/load/loadProjectPlugins.d.ts +36 -5
  171. package/lib/plugin/internal/load/loadProjectPlugins.js +477 -421
  172. package/lib/plugin/internal/load/loadProjectPlugins.js.map +1 -1
  173. package/lib/plugin/internal/load/pluginResolutionFailure.d.ts +18 -0
  174. package/lib/plugin/internal/load/pluginResolutionFailure.js +32 -0
  175. package/lib/plugin/internal/load/pluginResolutionFailure.js.map +1 -0
  176. package/lib/plugin/internal/load/resolveNativeSource.js +2 -3
  177. package/lib/plugin/internal/load/resolveNativeSource.js.map +1 -1
  178. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.d.ts +1 -1
  179. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js +3 -2
  180. package/lib/plugin/internal/source/PluginBuildEnvironmentReadings.js.map +1 -1
  181. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.d.ts +4 -1
  182. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js +9 -2
  183. package/lib/plugin/internal/source/pluginModuleReplaceDirectories.js.map +1 -1
  184. package/lib/structures/ITtscCompilerContext.d.ts +12 -12
  185. package/lib/structures/ITtscPlugin.d.ts +7 -5
  186. package/lib/structures/ITtscPluginFactoryContext.d.ts +9 -8
  187. package/lib/structures/internal/TtscCommonOptions.d.ts +2 -2
  188. package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +12 -6
  189. package/package.json +9 -9
  190. package/src/TtscCompiler.ts +1 -1
  191. package/src/compiler/internal/ResidentCheckProcess.ts +159 -374
  192. package/src/compiler/internal/ResidentCheckProcessHost.ts +502 -0
  193. package/src/compiler/internal/ResidentCheckProcessProtocol.ts +53 -0
  194. package/src/compiler/internal/build/BuildExecution.ts +123 -11
  195. package/src/compiler/internal/build/ResidentCheckWatchSession.ts +41 -24
  196. package/src/compiler/internal/build/RunBuildOptions.ts +9 -5
  197. package/src/compiler/internal/build/TsgoArguments.ts +4 -1
  198. package/src/compiler/internal/build/WatchInputPublication.ts +106 -0
  199. package/src/compiler/internal/build/runBuild.ts +58 -34
  200. package/src/compiler/internal/buildNativeCompiler.ts +4 -0
  201. package/src/compiler/internal/compileProjectInMemory.ts +8 -2
  202. package/src/compiler/internal/project/CompilerProjectSelection.ts +14 -14
  203. package/src/compiler/internal/project/resolveOwningProjectConfig.ts +42 -16
  204. package/src/compiler/internal/readEffectiveCompilerOptions.ts +1 -1
  205. package/src/compiler/internal/resolveBinary.ts +4 -1
  206. package/src/compiler/internal/resolveTsgo.ts +7 -0
  207. package/src/compiler/internal/runSingleFileEmit.ts +145 -101
  208. package/src/compiler/internal/startResidentTransform.ts +9 -2
  209. package/src/compiler/internal/transformProjectInMemory.ts +12 -3
  210. package/src/compiler/internal/transformProjectInWorker.ts +10 -2
  211. package/src/flags/ParseOptions.ts +2 -2
  212. package/src/internal/E2ETrace.ts +3 -1
  213. package/src/internal/OwnedNativeProcess.ts +2 -2
  214. package/src/internal/SourceNativeRetirement.ts +42 -19
  215. package/src/internal/TtsxNodeOptions.ts +124 -0
  216. package/src/internal/javascriptRuntimeCapabilities.ts +6 -1
  217. package/src/internal/parseWindowsDirectoryCaseSensitivity.ts +5 -5
  218. package/src/internal/pathIdentity/createCaseSensitivityObservations.ts +242 -0
  219. package/src/internal/pathIdentity/createFilesystemPathIdentityContext.ts +2 -169
  220. package/src/internal/projectInputPathIdentity.ts +1 -0
  221. package/src/internal/workerModuleUrl.ts +34 -0
  222. package/src/launcher/internal/buildSingleRootProject.ts +2 -0
  223. package/src/launcher/internal/getCompilerVersionText.ts +3 -2
  224. package/src/launcher/internal/parseTtsxCLI.ts +5 -4
  225. package/src/launcher/internal/prepareExecution.ts +6 -1
  226. package/src/launcher/internal/resolveSingleFileOutput.ts +8 -9
  227. package/src/launcher/internal/runTtsc.ts +127 -49
  228. package/src/launcher/internal/runTtsx.ts +17 -8
  229. package/src/launcher/internal/runtime/CommonJsRuntimeSource.ts +11 -9
  230. package/src/launcher/internal/runtime/RuntimeEntrySources.ts +57 -0
  231. package/src/launcher/internal/runtime/RuntimeProjectOwnership.ts +23 -10
  232. package/src/launcher/internal/runtime/advertiseTypeScriptExtensions.ts +25 -0
  233. package/src/launcher/internal/runtime/commonJsExportNames.ts +11 -4
  234. package/src/launcher/internal/runtime/installRuntimeHooks.ts +62 -16
  235. package/src/launcher/internal/runtime/resolveRuntimeCleanTargets.ts +17 -5
  236. package/src/launcher/internal/watch/AsyncWatchTopology.ts +267 -0
  237. package/src/launcher/internal/watch/WatchSettlement.ts +67 -0
  238. package/src/launcher/internal/watch/WatchTopology.ts +461 -102
  239. package/src/launcher/internal/watch/WatchTopologyWorkerProtocol.ts +106 -0
  240. package/src/launcher/internal/watch/projectInputReloadEventShouldNotify.ts +14 -9
  241. package/src/launcher/internal/watch/serveWatchTopologyWorker.ts +75 -0
  242. package/src/launcher/internal/watch/watchTopologyWorker.ts +29 -0
  243. package/src/plugin/CapabilityPluginResolver.ts +9 -2
  244. package/src/plugin/internal/load/PluginDescriptorEvaluationCache.ts +17 -12
  245. package/src/plugin/internal/load/PluginPackageResolution.ts +14 -9
  246. package/src/plugin/internal/load/ProjectPluginEntries.ts +40 -15
  247. package/src/plugin/internal/load/collectProjectHostInputs.ts +7 -1
  248. package/src/plugin/internal/load/hasProjectPluginEntries.ts +8 -3
  249. package/src/plugin/internal/load/loadProjectPlugins.ts +683 -593
  250. package/src/plugin/internal/load/pluginResolutionFailure.ts +37 -0
  251. package/src/plugin/internal/load/resolveNativeSource.ts +10 -4
  252. package/src/plugin/internal/source/PluginBuildEnvironmentReadings.ts +8 -2
  253. package/src/plugin/internal/source/pluginModuleReplaceDirectories.ts +10 -1
  254. package/src/structures/ITtscCompilerContext.ts +12 -12
  255. package/src/structures/ITtscPlugin.ts +7 -5
  256. package/src/structures/ITtscPluginFactoryContext.ts +9 -8
  257. package/src/structures/internal/TtscCommonOptions.ts +2 -2
  258. package/src/structures/internal/TtscSingleFileEmitOptions.ts +22 -11
  259. package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +0 -78
  260. package/internal/graphsymbols/provider.go +0 -650
  261. package/internal/lspserver/lsp_symbols.go +0 -212
@@ -9,11 +9,13 @@ const node_fs_1 = __importDefault(require("node:fs"));
9
9
  const node_module_1 = require("node:module");
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const node_url_1 = require("node:url");
12
+ const WatchInputPublication_1 = require("../../../compiler/internal/build/WatchInputPublication");
12
13
  const readJsonFile_1 = require("../../../compiler/internal/project/readJsonFile");
13
14
  const readProjectConfig_1 = require("../../../compiler/internal/project/readProjectConfig");
14
15
  const SidecarEnvironment_1 = require("../../../compiler/internal/sharedHost/SidecarEnvironment");
15
16
  const E2ETrace_1 = require("../../../internal/E2ETrace");
16
17
  const OwnedSynchronousProcess_1 = require("../../../internal/OwnedSynchronousProcess");
18
+ const TtsxNodeOptions_1 = require("../../../internal/TtsxNodeOptions");
17
19
  const createCanonicalTempDirectory_1 = require("../../../internal/createCanonicalTempDirectory");
18
20
  const javascriptRuntimeCapabilities_1 = require("../../../internal/javascriptRuntimeCapabilities");
19
21
  const resolveNodeBinary_1 = require("../../../internal/resolveNodeBinary");
@@ -79,6 +81,9 @@ const visitImportMappedCandidates_1 = require("./visitImportMappedCandidates");
79
81
  * declaration. Sequential content, metadata and physical-path observations are
80
82
  * not an atomic snapshot or detection of every omitted read.
81
83
  *
84
+ * Synchronous callers require synchronous watch-input handlers; use
85
+ * {@link loadProjectPlugins.async} to await a worker's baseline publication.
86
+ *
82
87
  * @param options.binary - Absolute path to the ttsc native helper binary.
83
88
  * @param options.cacheDir - Override the plugin binary cache directory.
84
89
  * @param options.cwd - Working directory for resolving relative paths.
@@ -89,11 +94,12 @@ const visitImportMappedCandidates_1 = require("./visitImportMappedCandidates");
89
94
  * native environment-name identity. Defaults to `process.env` for CLI
90
95
  * callers, so ambient behavior is unchanged.
91
96
  * @param options.file - Path to the tsconfig/jsconfig file.
92
- * @param options.pluginConfigDir - Caller-declared anchor for plugin
93
- * config-file discovery (see `ITtscPluginFactoryContext.pluginConfigDir`).
94
- * @param options.projectRoot - Override the project root directory.
97
+ * @param options.pluginConfigDir - Caller-declared consumer directory for
98
+ * plugin packages, factory contexts and config-file discovery.
99
+ * @param options.projectRoot - Override the source root, native working
100
+ * directory, cache anchor and returned relative Go-source base.
95
101
  * @param options.tsconfig - Alias for `file`.
96
- * @evidence contracts/common.md#principled-implementation The loader brackets project discovery observations, evaluates descriptors in isolated processes and validates declarations before publishing conservative source repair interests. Metadata refusal remains terminal; valid records narrow watch interests before final admission/build, while contradictory content or physical observations are omitted from proof rather than retroactively blessed.
102
+ * @evidence contracts/common.md#principled-implementation The loader brackets discovery at the selected config or explicit pluginConfigDir and factories receive that consumer anchor, independently of the source root retained for descriptor/native process cwd, caches and relative Go sources. It validates declarations before publishing conservative source repair interests. Metadata refusal remains terminal; valid records narrow watch interests before final admission/build, while contradictory content or physical observations are omitted from proof rather than retroactively blessed.
97
103
  * @evidence contracts/common.md#clear-and-simple-design The operation owns one ordered load generation; private helpers separate discovery, evaluation, validation, composition and proof merging, while package resolution and source building remain their own modules.
98
104
  * @evidence contracts/common.md#prohibited-implementation-shortcuts Descriptor module caches are isolated rather than deleting application singletons. ttsx retry occurs only for explicit supported TypeScript loader incompatibility, with plugins disabled to avoid recursive self-hosting; arbitrary descriptor failures are not retried into false success.
99
105
  * @evidence contracts/common.md#meaningful-documentation Native JSDoc explains result provenance, environment and path options; helper comments explain input races, conservative proof omission, fallback authority and cleanup. Member/tag spacing and separated concepts follow the documentation skill.
@@ -103,420 +109,463 @@ const visitImportMappedCandidates_1 = require("./visitImportMappedCandidates");
103
109
  * @evidence contracts/performance.md#bound-retention-and-release-resources Finally restores injected environment locators, attempts evaluator directory removal and closes direct-evaluator diagnostic descriptors. Close/removal can fail; synchronous completion concerns the selected evaluator and does not certify arbitrary descendants are gone. Generation maps/results scale with observed data, and disk diagnostics/result/observation files have no independent byte ceiling here. Default disk pruning has its own interval/protection/failure policy; explicit roots remain caller-owned.
104
110
  */
105
111
  function loadProjectPlugins(options) {
106
- // Snapshot the caller environment before `withPluginLoaderEnv` injects
107
- // host-owned Node/ttsx locators into process.env. Under a Bun parent the
108
- // direct descriptor evaluator must remain Bun unless the caller explicitly
109
- // selected another runtime.
110
- const effectiveEnv = SidecarEnvironment_1.SidecarEnvironment.merge(options.env ?? process.env);
111
- const projectSnapshot = readProjectHostInputSnapshot({
112
- cwd: options.cwd,
113
- file: options.file,
114
- includePluginDiscovery: options.entries === undefined,
115
- projectRoot: options.projectRoot,
116
- tsconfig: options.tsconfig,
117
- });
118
- const { project } = projectSnapshot;
119
- const projectHostInputs = projectSnapshot.hostInputs;
120
- const projectHostInputHashes = projectSnapshot.hostInputHashes;
121
- const projectHostInputRealpaths = projectSnapshot.hostInputRealpaths;
122
- const entries = options.entries === false
123
- ? []
124
- : ProjectPluginEntries_1.ProjectPluginEntries.resolvePluginEntries(project, options.entries).filter((entry) => entry.config.enabled !== false);
125
- if (entries.length === 0) {
126
- options.onWatchInputs?.([]);
127
- return {
128
- ...collectHostInputSnapshot(project, {}, [], projectHostInputs, revalidateHostInputHashes(projectHostInputHashes, projectHostInputs), revalidateHostInputRealpaths(projectHostInputRealpaths, projectHostInputs)),
129
- descriptorReadsDeclared: true,
130
- discoveryInputsComplete: project.configInputsComplete === true,
131
- nativePlugins: [],
132
- pluginSources: {},
133
- project,
134
- };
112
+ return WatchInputPublication_1.WatchInputPublication.run(loadProjectPlugins.steps(options));
113
+ }
114
+ /**
115
+ * Keep synchronous loading and awaited watch handoffs on one load sequence.
116
+ *
117
+ * @evidence contracts/common.md#principled-implementation Both completion modes advance one ordered discovery, admission, publication and build sequence. Awaited stepping preserves baseline-before-read handoffs before package selection and source builds, including empty selection and conservative repair roots.
118
+ * @evidence contracts/common.md#clear-and-simple-design The namespace adds an awaited runner beside synchronous loading and exposes one generator to build orchestration; existing helpers retain discovery, validation and compilation responsibilities.
119
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Publication suspension retains failed-build interests and source proof rather than rebuilding a baseline after compilation. Pending publication cannot establish observation; rejection enters the real load failure path without replacing admission or native builds.
120
+ * @evidence contracts/common.md#meaningful-documentation Entry prose distinguishes completion modes, baseline-before-read ordering and internal sequence completion/rejection duties; the primary loader documents input and result provenance.
121
+ * @evidence contracts/portability.md#os-neutral-implementation The shared sequence retains existing native resolution, paths, environment and argument-array handling; the namespace adds no independent boundary.
122
+ * @evidence contracts/performance.md#efficient-algorithms Discovery, source hashing, Go metadata, validation and builds retain their original input costs; each publication adds one suspension without repeating those phases.
123
+ * @evidence contracts/performance.md#reuse-equivalent-work Validated descriptor/source records and load-local digest/reader sharing remain available across suspension; the completion wrapper retains no reusable load or in-flight cache.
124
+ * @evidence contracts/performance.md#bound-retention-and-release-resources One load generation stays live while its current handoff settles; evaluator cleanup and returned-generation ownership remain with the sequence, and handler rejection enters generator cleanup.
125
+ */
126
+ (function (loadProjectPlugins) {
127
+ /**
128
+ * Load plugins after each watch owner confirms its reported input baseline. A
129
+ * rejected handoff fails loading before the next source read or build.
130
+ */
131
+ function async(options) {
132
+ return WatchInputPublication_1.WatchInputPublication.runAsync(steps(options));
135
133
  }
136
- const cwd = node_path_1.default.resolve(options.cwd ?? process.cwd());
137
- const context = {
138
- binary: options.binary,
139
- cwd,
140
- ...(options.pluginConfigDir === undefined || options.pluginConfigDir === ""
141
- ? {}
142
- : { pluginConfigDir: node_path_1.default.resolve(cwd, options.pluginConfigDir) }),
143
- projectRoot: project.root,
144
- tsconfig: project.path,
145
- };
146
- // Where isolated descriptor evaluations keep their answers across launches
147
- // (`PluginDescriptorEvaluationCache`).
148
- // WARNING (#1721, #1722, #1723): a warm load must prove its unchanged inputs
149
- // from this store, not re-read them. Every launch is a new process, so any
150
- // module-level memo of this load's work dies with it; a 27 s warm typia
151
- // launch whose native build took 0.28 s is what that looked like.
152
- const identities = options.identities ??
153
- PluginContentIdentities_1.PluginContentIdentities.open({
154
- projectRoot: project.root,
155
- cacheDir: options.cacheDir,
156
- env: effectiveEnv,
134
+ loadProjectPlugins.async = async;
135
+ /**
136
+ * Produce one load, suspending at each watch-input handoff before continuing.
137
+ * Internal build orchestration consumes the whole sequence with its chosen
138
+ * completion runner; it must deliver rejection back into the sequence.
139
+ */
140
+ function* steps(options) {
141
+ // Snapshot the caller environment before `withPluginLoaderEnv` injects
142
+ // host-owned Node/ttsx locators into process.env. Under a Bun parent the
143
+ // direct descriptor evaluator must remain Bun unless the caller explicitly
144
+ // selected another runtime.
145
+ const effectiveEnv = SidecarEnvironment_1.SidecarEnvironment.merge(options.env ?? process.env);
146
+ const projectSnapshot = readProjectHostInputSnapshot({
147
+ cwd: options.cwd,
148
+ file: options.file,
149
+ includePluginDiscovery: options.entries === undefined,
150
+ pluginConfigDir: options.pluginConfigDir,
151
+ projectRoot: options.projectRoot,
152
+ tsconfig: options.tsconfig,
157
153
  });
158
- const descriptorCache = {
159
- cacheDir: options.cacheDir,
160
- identities,
161
- version: (0, pluginBuildVersions_1.pluginBuildVersions)(project.root).ttsc,
162
- };
163
- const loadedEntries = withPluginLoaderEnv(() => entries.map((entry) => {
164
- const specifier = entry.config.transform;
165
- if (typeof specifier !== "string" || specifier.length === 0) {
166
- throw new Error(`ttsc: plugin entry is missing a string "transform" field`);
167
- }
168
- const entryParent = node_path_1.default.join(entry.baseDir, "package.json");
169
- const probedCandidates = collectModuleResolutionCandidates(specifier, entryParent, undefined);
170
- // Capture every candidate before resolution chooses the descriptor entry.
171
- // A post-resolution snapshot could bless a higher-priority file created
172
- // after the resolver had already selected the old entry.
173
- const probedCandidateHashes = (0, hashHostInputPaths_1.hashHostInputPaths)(probedCandidates);
174
- const probedCandidateRealpaths = (0, realpathHostInputPaths_1.realpathHostInputPaths)(probedCandidates);
175
- const request = PluginPackageResolution_1.PluginPackageResolution.resolvePluginRequest(specifier, entry.baseDir);
176
- // Keep the candidates of the search roots up to the one the entry
177
- // resolved in: the lookup never read the roots after it
178
- // (`moduleResolutionBaseSelects`).
179
- const read = new Set(collectModuleResolutionCandidates(specifier, entryParent, request).map((candidate) => node_path_1.default.resolve(candidate)));
180
- const entryCandidates = probedCandidates.filter((candidate) => read.has(node_path_1.default.resolve(candidate)));
181
- const entryCandidateHashes = pickHostInputEntries(probedCandidateHashes, entryCandidates);
182
- const entryCandidateRealpaths = pickHostInputEntries(probedCandidateRealpaths, entryCandidates);
183
- const loaded = loadPluginEntry(entry.config, { ...context, plugin: entry.config }, request, effectiveEnv, descriptorCache);
184
- const loadedHostInputHashes = mergeObservedHostInputHashes(loaded.hostInputHashes, (0, hashHostInputPaths_1.hashHostInputPaths)(Object.keys(loaded.hostInputHashes)));
185
- const loadedHostInputRealpaths = mergeObservedHostInputRealpaths(loaded.hostInputRealpaths, (0, realpathHostInputPaths_1.realpathHostInputPaths)(Object.keys(loaded.hostInputRealpaths)));
186
- const hostInputHashes = mergeObservedHostInputHashes(entryCandidateHashes, loadedHostInputHashes);
187
- const hostInputRealpaths = mergeObservedHostInputRealpaths(entryCandidateRealpaths, loadedHostInputRealpaths);
188
- for (const input of loaded.hostInputs) {
189
- const absolute = node_path_1.default.resolve(input);
190
- if (!Object.prototype.hasOwnProperty.call(loadedHostInputHashes, absolute)) {
191
- delete hostInputHashes[absolute];
192
- }
154
+ const { project } = projectSnapshot;
155
+ const projectHostInputs = projectSnapshot.hostInputs;
156
+ const projectHostInputHashes = projectSnapshot.hostInputHashes;
157
+ const projectHostInputRealpaths = projectSnapshot.hostInputRealpaths;
158
+ const entries = options.entries === false
159
+ ? []
160
+ : ProjectPluginEntries_1.ProjectPluginEntries.resolvePluginEntries(project, options.entries, options.pluginConfigDir).filter((entry) => entry.config.enabled !== false);
161
+ if (entries.length === 0) {
162
+ yield options.onWatchInputs?.([]);
163
+ return {
164
+ ...collectHostInputSnapshot(project, {}, [], projectHostInputs, revalidateHostInputHashes(projectHostInputHashes, projectHostInputs), revalidateHostInputRealpaths(projectHostInputRealpaths, projectHostInputs)),
165
+ descriptorReadsDeclared: true,
166
+ discoveryInputsComplete: project.configInputsComplete === true,
167
+ nativePlugins: [],
168
+ pluginSources: {},
169
+ project,
170
+ };
193
171
  }
194
- return {
195
- ...loaded,
196
- hostInputHashes,
197
- hostInputRealpaths,
198
- hostInputs: [...loaded.hostInputs, ...entryCandidates],
199
- request,
172
+ const cwd = node_path_1.default.resolve(options.cwd ?? process.cwd());
173
+ const context = {
174
+ binary: options.binary,
175
+ cwd,
176
+ ...(options.pluginConfigDir === undefined ||
177
+ options.pluginConfigDir === ""
178
+ ? {}
179
+ : { pluginConfigDir: node_path_1.default.resolve(cwd, options.pluginConfigDir) }),
180
+ projectRoot: project.root,
181
+ tsconfig: project.path,
200
182
  };
201
- }), identities);
202
- const plugins = (0, composePluginSources_1.composePluginSources)(entries, loadedEntries.map((entry) => entry.plugin));
203
- const { ttsc: ttscVersion, tsgo: tsgoVersion } = (0, pluginBuildVersions_1.pluginBuildVersions)(context.projectRoot);
204
- const candidates = plugins
205
- .map((plugin, index) => {
206
- const stage = PluginDescriptorAdmission_1.PluginDescriptorAdmission.stage(plugin);
207
- (0, validatePluginSource_1.validatePluginSource)(plugin);
208
- const contributors = (0, validatePluginContributors_1.validatePluginContributors)(plugin);
209
- const source = resolvePluginSource(plugin.source, context.projectRoot);
210
- const label = (0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index);
211
- (0, requirePluginSource_1.requirePluginSource)(source, label);
212
- return { plugin, index, stage, contributors, source, label };
213
- })
214
- .map((candidate) => ({
215
- ...candidate,
216
- ...(0, resolvePluginGoModule_1.resolvePluginGoModule)(candidate.source, candidate.label),
217
- }));
218
- // One reading of each source directory, shared by package selection and
219
- // every build below and reported as the state the binaries were keyed on.
220
- const sourceDigests = new Map();
221
- // And one reading of the environment each build directory is keyed on, which
222
- // a plugin module root's state reports as it is.
223
- const environmentDigests = new Map();
224
- // With the witness of each such reading, so a build of a directory package
225
- // selection already read keys on that reading instead of probing Go again.
226
- const environmentWitnesses = new Map();
227
- const readings = {
228
- sources: sourceDigests,
229
- environments: environmentDigests,
230
- witnesses: environmentWitnesses,
231
- };
232
- // Records describing sources must never land inside one, so the store is
233
- // admitted for source proofs only once every source of the load is known.
234
- const sourceIdentities = PluginContentIdentities_1.PluginContentIdentities.outside(identities, [
235
- ...candidates.flatMap((candidate) => [
236
- candidate.moduleRoot,
237
- candidate.packageDir,
238
- ...(candidate.contributors ?? []).map((input) => input.source),
239
- ]),
240
- ...SourcePluginWorkspace_1.SourcePluginWorkspace.findTtscOverlayDirs(),
241
- ]);
242
- // One fixed-effective-environment load owns manifest syntax sharing from the
243
- // first watch query through metadata and builds. Do not allocate this after
244
- // watch projection: that silently repeats selected-Go work (#1712).
245
- const packageReaders = new Map();
246
- const watchReplacements = options.onWatchInputs === undefined
247
- ? undefined
248
- : new Map();
249
- if (options.onWatchInputs !== undefined) {
250
- // The prospective context reads the admitted module layout and anchors its
251
- // tool there before kind exists. Keep both possible final-root baselines:
252
- // a linked package keeps packageDir, and an executable keeps moduleRoot.
253
- const interests = new Set(candidates
254
- .flatMap((candidate) => [
255
- candidate.moduleRoot,
256
- candidate.packageDir,
257
- ...(candidate.contributors ?? []).map((input) => input.source),
258
- ])
259
- .filter(reportsPluginSource));
260
- // WARNING (#1728): hand the watch owner this load's store with the roots.
261
- // It baselines each root before any build reads it; without the store it
262
- // read every source the load proves from records a moment later.
263
- options.onWatchInputs([...interests].sort(), sourceIdentities);
264
- const initialSize = interests.size;
265
- for (const candidate of candidates) {
266
- // Nested source-only proposals replace the outer root manifests. Do not
267
- // activate their irrelevant module graph just to discover watch inputs.
268
- if (candidate.stage === "transform" && candidate.entry !== ".")
269
- continue;
270
- for (const directory of pluginReplacementWatchDirectories(candidate.moduleRoot, effectiveEnv, watchReplacements, packageReaders))
271
- if (reportsPluginSource(directory))
272
- interests.add(directory);
273
- }
274
- if (interests.size !== initialSize)
275
- options.onWatchInputs([...interests].sort(), sourceIdentities);
276
- }
277
- const proposals = NativeSourcePackages_1.NativeSourcePackages.propose(candidates.map((candidate) => ({
278
- ...candidate,
279
- ownModule: candidate.stage !== "transform",
280
- })), effectiveEnv, packageReaders, sourceIdentities, readings);
281
- const records = candidates.map(({ plugin, index, stage, contributors, source, packageDir }) => {
282
- const { kind, moduleRoot } = (0, resolveNativeSource_1.resolveNativeSource)(source, plugin, entries[index].config, index, {
283
- env: effectiveEnv,
284
- observation: proposals[index].observation,
285
- });
286
- if (kind === "linked" && stage !== "transform") {
287
- throw new Error(`ttsc: plugin "${(0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index)}" source is a linked Go package, but only transform-stage plugins can be linked into a compiler host`);
288
- }
289
- const linkedContributorName = kind === "linked"
290
- ? `linked_${String(index).padStart(6, "0")}`
291
- : undefined;
292
- const hostInputs = validatePluginHostInputs(plugin, index);
293
- const pluginHostInputHashes = validatePluginHostInputHashes(plugin, index, hostInputs);
294
- const pluginHostInputRealpaths = validatePluginHostInputRealpaths(plugin, index, hostInputs);
295
- return {
296
- capabilities: plugin.capabilities,
297
- contributors,
298
- config: entries[index].config,
299
- kind,
300
- label: (0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index),
301
- linkedContributorName,
302
- name: plugin.name,
303
- reportsTypeScriptDiagnostics: plugin.reportsTypeScriptDiagnostics === true,
304
- request: loadedEntries[index].request,
305
- hostInputHashes: mergePluginHostInputHashes(loadedEntries[index].hostInputHashes, pluginHostInputHashes, loadedEntries[index].hostInputs, hostInputs),
306
- hostInputRealpaths: mergePluginHostInputHashes(loadedEntries[index].hostInputRealpaths, pluginHostInputRealpaths, loadedEntries[index].hostInputs, hostInputs),
307
- hostInputs: [...loadedEntries[index].hostInputs, ...hostInputs],
308
- moduleRoot,
309
- packageDir,
310
- source,
311
- stage,
183
+ // Where isolated descriptor evaluations keep their answers across launches
184
+ // (`PluginDescriptorEvaluationCache`).
185
+ // WARNING (#1721, #1722, #1723): a warm load must prove its unchanged inputs
186
+ // from this store, not re-read them. Every launch is a new process, so any
187
+ // module-level memo of this load's work dies with it; a 27 s warm typia
188
+ // launch whose native build took 0.28 s is what that looked like.
189
+ const identities = options.identities ??
190
+ PluginContentIdentities_1.PluginContentIdentities.open({
191
+ projectRoot: project.root,
192
+ cacheDir: options.cacheDir,
193
+ env: effectiveEnv,
194
+ });
195
+ const descriptorCache = {
196
+ cacheDir: options.cacheDir,
197
+ identities,
198
+ projectRoot: project.root,
199
+ version: (0, pluginBuildVersions_1.pluginBuildVersions)(project.root).ttsc,
312
200
  };
313
- });
314
- const executableCandidates = candidates.filter((candidate) => records[candidate.index].kind === "executable" &&
315
- !proposals[candidate.index].ownModule);
316
- const buildDirectories = pluginBuildDirectories(records, effectiveEnv, watchReplacements, packageReaders);
317
- // Admission can now narrow the provisional interests without restamping the
318
- // retained package/module baselines. Final host errors still keep these roots.
319
- options.onWatchInputs?.(buildDirectories, sourceIdentities);
320
- const keyedDirectories = new Set(buildDirectories);
321
- const linkedContributors = records
322
- .filter((record) => record.stage === "transform")
323
- .flatMap((record) => record.kind === "linked"
324
- ? [{ name: record.linkedContributorName, source: record.source }]
325
- : []);
326
- const transformHosts = records.filter((record) => record.stage === "transform" && record.kind === "executable");
327
- const hostContributors = linkedContributors.length === 0 ? undefined : linkedContributors;
328
- // Every host compiles the linked sources under its own module/tool context.
329
- // Equivalent entries in one module share this metadata command and reader;
330
- // a second executable host can select a genuinely different Go context.
331
- const admittedExecutables = new Set();
332
- if (linkedContributors.length !== 0) {
333
- const hosts = transformHosts.length === 0
334
- ? [
335
- {
336
- source: node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"),
337
- label: "linked-plugin-host",
338
- },
339
- ]
340
- : transformHosts;
341
- const contexts = new Map();
342
- for (const host of hosts) {
343
- const moduleRoot = (0, resolvePluginGoModule_1.resolvePluginGoModule)(host.source, host.label).moduleRoot;
344
- let context = contexts.get(moduleRoot);
345
- if (context === undefined) {
346
- context = { source: host.source, label: host.label, records: [] };
347
- contexts.set(moduleRoot, context);
201
+ const loadedEntries = withPluginLoaderEnv(() => entries.map((entry) => {
202
+ const specifier = entry.config.transform;
203
+ if (typeof specifier !== "string" || specifier.length === 0) {
204
+ throw new Error(`ttsc: plugin entry is missing a string "transform" field`);
348
205
  }
206
+ const entryParent = node_path_1.default.join(entry.baseDir, "package.json");
207
+ const probedCandidates = collectModuleResolutionCandidates(specifier, entryParent, undefined);
208
+ // Capture every candidate before resolution chooses the descriptor entry.
209
+ // A post-resolution snapshot could bless a higher-priority file created
210
+ // after the resolver had already selected the old entry.
211
+ const probedCandidateHashes = (0, hashHostInputPaths_1.hashHostInputPaths)(probedCandidates);
212
+ const probedCandidateRealpaths = (0, realpathHostInputPaths_1.realpathHostInputPaths)(probedCandidates);
213
+ const request = PluginPackageResolution_1.PluginPackageResolution.resolvePluginRequest(specifier, entry.baseDir);
214
+ // Keep the candidates of the search roots up to the one the entry
215
+ // resolved in: the lookup never read the roots after it
216
+ // (`moduleResolutionBaseSelects`).
217
+ const read = new Set(collectModuleResolutionCandidates(specifier, entryParent, request).map((candidate) => node_path_1.default.resolve(candidate)));
218
+ const entryCandidates = probedCandidates.filter((candidate) => read.has(node_path_1.default.resolve(candidate)));
219
+ const entryCandidateHashes = pickHostInputEntries(probedCandidateHashes, entryCandidates);
220
+ const entryCandidateRealpaths = pickHostInputEntries(probedCandidateRealpaths, entryCandidates);
221
+ const loaded = loadPluginEntry(entry.config, {
222
+ ...context,
223
+ plugin: entry.config,
224
+ // Package factories such as typia resolve their own package from
225
+ // this consumer anchor. Keep the compiler source root below for
226
+ // native cwd, cache ownership and returned relative Go sources.
227
+ projectRoot: ProjectPluginEntries_1.ProjectPluginEntries.resolvePluginBaseDir(project, options.pluginConfigDir),
228
+ }, request, effectiveEnv, descriptorCache);
229
+ const loadedHostInputHashes = mergeObservedHostInputHashes(loaded.hostInputHashes, (0, hashHostInputPaths_1.hashHostInputPaths)(Object.keys(loaded.hostInputHashes)));
230
+ const loadedHostInputRealpaths = mergeObservedHostInputRealpaths(loaded.hostInputRealpaths, (0, realpathHostInputPaths_1.realpathHostInputPaths)(Object.keys(loaded.hostInputRealpaths)));
231
+ const hostInputHashes = mergeObservedHostInputHashes(entryCandidateHashes, loadedHostInputHashes);
232
+ const hostInputRealpaths = mergeObservedHostInputRealpaths(entryCandidateRealpaths, loadedHostInputRealpaths);
233
+ for (const input of loaded.hostInputs) {
234
+ const absolute = node_path_1.default.resolve(input);
235
+ if (!Object.prototype.hasOwnProperty.call(loadedHostInputHashes, absolute)) {
236
+ delete hostInputHashes[absolute];
237
+ }
238
+ }
239
+ return {
240
+ ...loaded,
241
+ hostInputHashes,
242
+ hostInputRealpaths,
243
+ hostInputs: [...loaded.hostInputs, ...entryCandidates],
244
+ request,
245
+ };
246
+ }), identities);
247
+ const plugins = (0, composePluginSources_1.composePluginSources)(entries, loadedEntries.map((entry) => entry.plugin));
248
+ const { ttsc: ttscVersion, tsgo: tsgoVersion } = (0, pluginBuildVersions_1.pluginBuildVersions)(context.projectRoot);
249
+ const candidates = plugins
250
+ .map((plugin, index) => {
251
+ const stage = PluginDescriptorAdmission_1.PluginDescriptorAdmission.stage(plugin);
252
+ (0, validatePluginSource_1.validatePluginSource)(plugin);
253
+ const contributors = (0, validatePluginContributors_1.validatePluginContributors)(plugin);
254
+ const source = resolvePluginSource(plugin.source, context.projectRoot);
255
+ const label = (0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index);
256
+ (0, requirePluginSource_1.requirePluginSource)(source, label);
257
+ return { plugin, index, stage, contributors, source, label };
258
+ })
259
+ .map((candidate) => ({
260
+ ...candidate,
261
+ ...(0, resolvePluginGoModule_1.resolvePluginGoModule)(candidate.source, candidate.label),
262
+ }));
263
+ // One reading of each source directory, shared by package selection and
264
+ // every build below and reported as the state the binaries were keyed on.
265
+ const sourceDigests = new Map();
266
+ // And one reading of the environment each build directory is keyed on, which
267
+ // a plugin module root's state reports as it is.
268
+ const environmentDigests = new Map();
269
+ // With the witness of each such reading, so a build of a directory package
270
+ // selection already read keys on that reading instead of probing Go again.
271
+ const environmentWitnesses = new Map();
272
+ const readings = {
273
+ sources: sourceDigests,
274
+ environments: environmentDigests,
275
+ witnesses: environmentWitnesses,
276
+ };
277
+ // Records describing sources must never land inside one, so the store is
278
+ // admitted for source proofs only once every source of the load is known.
279
+ const sourceIdentities = PluginContentIdentities_1.PluginContentIdentities.outside(identities, [
280
+ ...candidates.flatMap((candidate) => [
281
+ candidate.moduleRoot,
282
+ candidate.packageDir,
283
+ ...(candidate.contributors ?? []).map((input) => input.source),
284
+ ]),
285
+ ...SourcePluginWorkspace_1.SourcePluginWorkspace.findTtscOverlayDirs(),
286
+ ]);
287
+ // One fixed-effective-environment load owns manifest syntax sharing from the
288
+ // first watch query through metadata and builds. Do not allocate this after
289
+ // watch projection: that silently repeats selected-Go work (#1712).
290
+ const packageReaders = new Map();
291
+ const watchReplacements = options.onWatchInputs === undefined
292
+ ? undefined
293
+ : new Map();
294
+ if (options.onWatchInputs !== undefined) {
295
+ // The prospective context reads the admitted module layout and anchors its
296
+ // tool there before kind exists. Keep both possible final-root baselines:
297
+ // a linked package keeps packageDir, and an executable keeps moduleRoot.
298
+ const interests = new Set(candidates
299
+ .flatMap((candidate) => [
300
+ candidate.moduleRoot,
301
+ candidate.packageDir,
302
+ ...(candidate.contributors ?? []).map((input) => input.source),
303
+ ])
304
+ .filter(reportsPluginSource));
305
+ // WARNING (#1728): hand the watch owner this load's store with the roots.
306
+ // It baselines each root before any build reads it; without the store it
307
+ // read every source the load proves from records a moment later.
308
+ // A worker owns the watch baseline now. Complete this handoff before Go
309
+ // metadata or source proof consumes the roots, preserving #1728 (#1758).
310
+ yield options.onWatchInputs([...interests].sort(), sourceIdentities);
311
+ const initialSize = interests.size;
312
+ for (const candidate of candidates) {
313
+ // Nested source-only proposals replace the outer root manifests. Do not
314
+ // activate their irrelevant module graph just to discover watch inputs.
315
+ if (candidate.stage === "transform" && candidate.entry !== ".")
316
+ continue;
317
+ for (const directory of pluginReplacementWatchDirectories(candidate.moduleRoot, effectiveEnv, watchReplacements, packageReaders, sourceIdentities))
318
+ if (reportsPluginSource(directory))
319
+ interests.add(directory);
320
+ }
321
+ if (interests.size !== initialSize)
322
+ yield options.onWatchInputs([...interests].sort(), sourceIdentities);
349
323
  }
350
- for (const record of transformHosts)
351
- contexts.get(record.moduleRoot).records.push(record);
352
- for (const host of contexts.values()) {
353
- const moduleHosts = host.records;
354
- const hostEntries = moduleHosts.map((record) => ({
355
- entry: (0, resolvePluginGoModule_1.resolvePluginGoModule)(record.source, record.label).entry,
356
- }));
357
- const observations = NativeSourcePackages_1.NativeSourcePackages.inspect({
358
- source: host.source,
359
- pluginName: host.label,
324
+ const proposals = NativeSourcePackages_1.NativeSourcePackages.propose(candidates.map((candidate) => ({
325
+ ...candidate,
326
+ ownModule: candidate.stage !== "transform",
327
+ })), effectiveEnv, packageReaders, sourceIdentities, readings);
328
+ const records = candidates.map(({ plugin, index, stage, contributors, source, packageDir }) => {
329
+ const { kind, moduleRoot } = (0, resolveNativeSource_1.resolveNativeSource)(source, plugin, entries[index].config, index, {
360
330
  env: effectiveEnv,
361
- readers: packageReaders,
362
- identities: sourceIdentities,
363
- readings,
364
- packages: [
365
- ...hostEntries,
366
- ...linkedContributors.map((input) => ({
367
- ...input,
368
- entry: `./contrib/${input.name}`,
369
- })),
370
- ],
371
- });
372
- moduleHosts.forEach((record, index) => {
373
- NativeSourcePackages_1.NativeSourcePackages.kind(observations[index], record.label, "executable");
374
- admittedExecutables.add(record.source);
331
+ observation: proposals[index].observation,
375
332
  });
376
- linkedContributors.forEach((input, index) => {
377
- NativeSourcePackages_1.NativeSourcePackages.kind(observations[hostEntries.length + index], `${input.name} inside host ${host.label}`, "linked");
378
- });
379
- }
380
- }
381
- // A nested main proposal still needs its actual owning manifests. Hosts
382
- // with linked contributors were admitted together above; other equivalent
383
- // entries share one standalone observation, with no repeated host copy.
384
- const standaloneCandidates = executableCandidates.filter((candidate) => !admittedExecutables.has(candidate.source));
385
- const executablePackages = NativeSourcePackages_1.NativeSourcePackages.ownPackages(standaloneCandidates, effectiveEnv, packageReaders, sourceIdentities, readings);
386
- standaloneCandidates.forEach((candidate, index) => {
387
- NativeSourcePackages_1.NativeSourcePackages.kind(executablePackages[index], candidate.label, "executable");
388
- });
389
- const packageOwnership = (source, label, linked) => [
390
- {
391
- entry: (0, resolvePluginGoModule_1.resolvePluginGoModule)(source, label).entry,
392
- kind: "executable",
393
- },
394
- ...(linked
395
- ? linkedContributors.map((input) => ({
396
- entry: `./contrib/${input.name}`,
397
- kind: "linked",
398
- }))
399
- : []),
400
- ];
401
- const builtTransformHosts = new Map();
402
- for (const record of transformHosts) {
403
- builtTransformHosts.set(record, (0, buildSourcePlugin_1.buildSourcePlugin)({
404
- baseDir: context.projectRoot,
405
- cacheDir: options.cacheDir,
406
- contributors: mergeContributors(record.contributors, hostContributors),
407
- env: effectiveEnv,
408
- goModReaders: packageReaders,
409
- identities: sourceIdentities,
410
- pluginName: record.label,
411
- packageOwnership: packageOwnership(record.source, record.label, true),
412
- source: record.source,
413
- environmentDigests,
414
- environmentWitnesses,
415
- sourceDigests,
416
- ttscVersion,
417
- tsgoVersion,
418
- }));
419
- }
420
- const fallbackDriverHost = transformHosts.length === 0 && linkedContributors.length !== 0
421
- ? (0, buildSourcePlugin_1.buildSourcePlugin)({
422
- baseDir: context.projectRoot,
423
- cacheDir: options.cacheDir,
424
- contributors: linkedContributors,
425
- env: effectiveEnv,
426
- goModReaders: packageReaders,
427
- identities: sourceIdentities,
428
- label: "linked plugin host",
429
- pluginName: "linked-plugin-host",
430
- source: node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"),
431
- packageOwnership: packageOwnership(node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"), "linked-plugin-host", true),
432
- environmentDigests,
433
- environmentWitnesses,
434
- sourceDigests,
435
- ttscVersion,
436
- tsgoVersion,
437
- })
438
- : undefined;
439
- const selectedTransformHost = transformHosts.length === 0
440
- ? fallbackDriverHost
441
- : builtTransformHosts.get(transformHosts[0]);
442
- const nativePlugins = records.map((record) => {
443
- const binary = record.stage === "transform" && record.kind === "linked"
444
- ? selectedTransformHost
445
- : record.stage === "transform"
446
- ? builtTransformHosts.get(record)
447
- : (0, buildSourcePlugin_1.buildSourcePlugin)({
448
- baseDir: context.projectRoot,
449
- cacheDir: options.cacheDir,
450
- contributors: record.contributors,
333
+ if (kind === "linked" && stage !== "transform") {
334
+ throw new Error(`ttsc: plugin "${(0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index)}" source is a linked Go package, but only transform-stage plugins can be linked into a compiler host`);
335
+ }
336
+ const linkedContributorName = kind === "linked"
337
+ ? `linked_${String(index).padStart(6, "0")}`
338
+ : undefined;
339
+ const hostInputs = validatePluginHostInputs(plugin, index);
340
+ const pluginHostInputHashes = validatePluginHostInputHashes(plugin, index, hostInputs);
341
+ const pluginHostInputRealpaths = validatePluginHostInputRealpaths(plugin, index, hostInputs);
342
+ return {
343
+ capabilities: plugin.capabilities,
344
+ contributors,
345
+ config: entries[index].config,
346
+ kind,
347
+ label: (0, pluginLabel_1.pluginLabel)(plugin, entries[index].config, index),
348
+ linkedContributorName,
349
+ name: plugin.name,
350
+ reportsTypeScriptDiagnostics: plugin.reportsTypeScriptDiagnostics === true,
351
+ request: loadedEntries[index].request,
352
+ hostInputHashes: mergePluginHostInputHashes(loadedEntries[index].hostInputHashes, pluginHostInputHashes, loadedEntries[index].hostInputs, hostInputs),
353
+ hostInputRealpaths: mergePluginHostInputHashes(loadedEntries[index].hostInputRealpaths, pluginHostInputRealpaths, loadedEntries[index].hostInputs, hostInputs),
354
+ hostInputs: [...loadedEntries[index].hostInputs, ...hostInputs],
355
+ moduleRoot,
356
+ packageDir,
357
+ source,
358
+ stage,
359
+ };
360
+ });
361
+ const executableCandidates = candidates.filter((candidate) => records[candidate.index].kind === "executable" &&
362
+ !proposals[candidate.index].ownModule);
363
+ const buildDirectories = pluginBuildDirectories(records, effectiveEnv, watchReplacements, packageReaders, sourceIdentities);
364
+ // Admission can now narrow the provisional interests without restamping the
365
+ // retained package/module baselines. Final host errors still keep these roots.
366
+ yield options.onWatchInputs?.(buildDirectories, sourceIdentities);
367
+ const keyedDirectories = new Set(buildDirectories);
368
+ const linkedContributors = records
369
+ .filter((record) => record.stage === "transform")
370
+ .flatMap((record) => record.kind === "linked"
371
+ ? [{ name: record.linkedContributorName, source: record.source }]
372
+ : []);
373
+ const transformHosts = records.filter((record) => record.stage === "transform" && record.kind === "executable");
374
+ const hostContributors = linkedContributors.length === 0 ? undefined : linkedContributors;
375
+ // Every host compiles the linked sources under its own module/tool context.
376
+ // Equivalent entries in one module share this metadata command and reader;
377
+ // a second executable host can select a genuinely different Go context.
378
+ const admittedExecutables = new Set();
379
+ if (linkedContributors.length !== 0) {
380
+ const hosts = transformHosts.length === 0
381
+ ? [
382
+ {
383
+ source: node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"),
384
+ label: "linked-plugin-host",
385
+ },
386
+ ]
387
+ : transformHosts;
388
+ const contexts = new Map();
389
+ for (const host of hosts) {
390
+ const moduleRoot = (0, resolvePluginGoModule_1.resolvePluginGoModule)(host.source, host.label).moduleRoot;
391
+ let context = contexts.get(moduleRoot);
392
+ if (context === undefined) {
393
+ context = { source: host.source, label: host.label, records: [] };
394
+ contexts.set(moduleRoot, context);
395
+ }
396
+ }
397
+ for (const record of transformHosts)
398
+ contexts.get(record.moduleRoot).records.push(record);
399
+ for (const host of contexts.values()) {
400
+ const moduleHosts = host.records;
401
+ const hostEntries = moduleHosts.map((record) => ({
402
+ entry: (0, resolvePluginGoModule_1.resolvePluginGoModule)(record.source, record.label).entry,
403
+ }));
404
+ const observations = NativeSourcePackages_1.NativeSourcePackages.inspect({
405
+ source: host.source,
406
+ pluginName: host.label,
451
407
  env: effectiveEnv,
452
- goModReaders: packageReaders,
408
+ readers: packageReaders,
453
409
  identities: sourceIdentities,
454
- pluginName: record.label,
455
- packageOwnership: packageOwnership(record.source, record.label, false),
456
- source: record.source,
457
- environmentDigests,
458
- environmentWitnesses,
459
- sourceDigests,
460
- ttscVersion,
461
- tsgoVersion,
410
+ readings,
411
+ packages: [
412
+ ...hostEntries,
413
+ ...linkedContributors.map((input) => ({
414
+ ...input,
415
+ entry: `./contrib/${input.name}`,
416
+ })),
417
+ ],
418
+ });
419
+ moduleHosts.forEach((record, index) => {
420
+ NativeSourcePackages_1.NativeSourcePackages.kind(observations[index], record.label, "executable");
421
+ admittedExecutables.add(record.source);
462
422
  });
463
- if (binary === undefined) {
464
- throw new Error(`ttsc: plugin "${record.label}" is a linked Go package, but no compiler host is available`);
423
+ linkedContributors.forEach((input, index) => {
424
+ NativeSourcePackages_1.NativeSourcePackages.kind(observations[hostEntries.length + index], `${input.name} inside host ${host.label}`, "linked");
425
+ });
426
+ }
427
+ }
428
+ // A nested main proposal still needs its actual owning manifests. Hosts
429
+ // with linked contributors were admitted together above; other equivalent
430
+ // entries share one standalone observation, with no repeated host copy.
431
+ const standaloneCandidates = executableCandidates.filter((candidate) => !admittedExecutables.has(candidate.source));
432
+ const executablePackages = NativeSourcePackages_1.NativeSourcePackages.ownPackages(standaloneCandidates, effectiveEnv, packageReaders, sourceIdentities, readings);
433
+ standaloneCandidates.forEach((candidate, index) => {
434
+ NativeSourcePackages_1.NativeSourcePackages.kind(executablePackages[index], candidate.label, "executable");
435
+ });
436
+ const packageOwnership = (source, label, linked) => [
437
+ {
438
+ entry: (0, resolvePluginGoModule_1.resolvePluginGoModule)(source, label).entry,
439
+ kind: "executable",
440
+ },
441
+ ...(linked
442
+ ? linkedContributors.map((input) => ({
443
+ entry: `./contrib/${input.name}`,
444
+ kind: "linked",
445
+ }))
446
+ : []),
447
+ ];
448
+ const builtTransformHosts = new Map();
449
+ for (const record of transformHosts) {
450
+ builtTransformHosts.set(record, (0, buildSourcePlugin_1.buildSourcePlugin)({
451
+ baseDir: context.projectRoot,
452
+ cacheDir: options.cacheDir,
453
+ contributors: mergeContributors(record.contributors, hostContributors),
454
+ env: effectiveEnv,
455
+ goModReaders: packageReaders,
456
+ identities: sourceIdentities,
457
+ pluginName: record.label,
458
+ packageOwnership: packageOwnership(record.source, record.label, true),
459
+ source: record.source,
460
+ environmentDigests,
461
+ environmentWitnesses,
462
+ sourceDigests,
463
+ ttscVersion,
464
+ tsgoVersion,
465
+ }));
465
466
  }
467
+ const fallbackDriverHost = transformHosts.length === 0 && linkedContributors.length !== 0
468
+ ? (0, buildSourcePlugin_1.buildSourcePlugin)({
469
+ baseDir: context.projectRoot,
470
+ cacheDir: options.cacheDir,
471
+ contributors: linkedContributors,
472
+ env: effectiveEnv,
473
+ goModReaders: packageReaders,
474
+ identities: sourceIdentities,
475
+ label: "linked plugin host",
476
+ pluginName: "linked-plugin-host",
477
+ source: node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"),
478
+ packageOwnership: packageOwnership(node_path_1.default.join(ttscPackageRoot(), "cmd", "utility-host"), "linked-plugin-host", true),
479
+ environmentDigests,
480
+ environmentWitnesses,
481
+ sourceDigests,
482
+ ttscVersion,
483
+ tsgoVersion,
484
+ })
485
+ : undefined;
486
+ const selectedTransformHost = transformHosts.length === 0
487
+ ? fallbackDriverHost
488
+ : builtTransformHosts.get(transformHosts[0]);
489
+ const nativePlugins = records.map((record) => {
490
+ const binary = record.stage === "transform" && record.kind === "linked"
491
+ ? selectedTransformHost
492
+ : record.stage === "transform"
493
+ ? builtTransformHosts.get(record)
494
+ : (0, buildSourcePlugin_1.buildSourcePlugin)({
495
+ baseDir: context.projectRoot,
496
+ cacheDir: options.cacheDir,
497
+ contributors: record.contributors,
498
+ env: effectiveEnv,
499
+ goModReaders: packageReaders,
500
+ identities: sourceIdentities,
501
+ pluginName: record.label,
502
+ packageOwnership: packageOwnership(record.source, record.label, false),
503
+ source: record.source,
504
+ environmentDigests,
505
+ environmentWitnesses,
506
+ sourceDigests,
507
+ ttscVersion,
508
+ tsgoVersion,
509
+ });
510
+ if (binary === undefined) {
511
+ throw new Error(`ttsc: plugin "${record.label}" is a linked Go package, but no compiler host is available`);
512
+ }
513
+ return {
514
+ binary,
515
+ // This fallback is compiled from our utility-host, whose actual writer
516
+ // publishes provenance and supports a separate compiler argument cwd.
517
+ // A separately selected executable host must opt in itself; a linked
518
+ // library cannot certify that foreign host. An undeclared capability
519
+ // only withholds the optional protocol: build execution still runs such
520
+ // a host and observes it from outside, never rejecting it.
521
+ capabilities: record.stage === "transform" &&
522
+ record.kind === "linked" &&
523
+ fallbackDriverHost !== undefined
524
+ ? {
525
+ ...record.capabilities,
526
+ compilerArgsCwd: true,
527
+ emitProvenance: true,
528
+ }
529
+ : record.capabilities,
530
+ config: record.config,
531
+ contributors: record.contributors,
532
+ kind: record.kind,
533
+ name: record.name,
534
+ reportsTypeScriptDiagnostics: record.reportsTypeScriptDiagnostics,
535
+ source: record.source,
536
+ stage: record.stage,
537
+ };
538
+ });
466
539
  return {
467
- binary,
468
- // This fallback is compiled from our utility-host, whose actual writer
469
- // publishes provenance and supports a separate compiler argument cwd.
470
- // A separately selected executable host must opt in itself; a linked
471
- // library cannot certify that foreign host. An undeclared capability
472
- // only withholds the optional protocol: build execution still runs such
473
- // a host and observes it from outside, never rejecting it.
474
- capabilities: record.stage === "transform" &&
475
- record.kind === "linked" &&
476
- fallbackDriverHost !== undefined
477
- ? {
478
- ...record.capabilities,
479
- compilerArgsCwd: true,
480
- emitProvenance: true,
481
- }
482
- : record.capabilities,
483
- config: record.config,
484
- contributors: record.contributors,
485
- kind: record.kind,
486
- name: record.name,
487
- reportsTypeScriptDiagnostics: record.reportsTypeScriptDiagnostics,
488
- source: record.source,
489
- stage: record.stage,
540
+ ...collectHostInputSnapshot(project, context, records, projectHostInputs, revalidateHostInputHashes(projectHostInputHashes, projectHostInputs), revalidateHostInputRealpaths(projectHostInputRealpaths, projectHostInputs)),
541
+ descriptorReadsDeclared: loadedEntries.every((entry) => entry.observationsComplete && (0, declaresHostInputReads_1.declaresHostInputReads)(entry.plugin)),
542
+ discoveryInputsComplete: project.configInputsComplete === true,
543
+ ...(identities === undefined ? {} : { identities }),
544
+ nativePlugins: orderNativePlugins(nativePlugins),
545
+ ...(loadedEntries.some((entry) => !entry.observationsComplete)
546
+ ? { observationsComplete: false }
547
+ : {}),
548
+ // The directories the watch inputs named before the builds, as the builds
549
+ // read them. Package selection shares these readings and also reads a
550
+ // linked plugin's module root, which no build keys on, so only the build
551
+ // directories are reported.
552
+ pluginSources: Object.fromEntries([...sourceDigests]
553
+ .filter(([directory]) => keyedDirectories.has(directory))
554
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
555
+ .map(([directory, sourceDigest]) => [
556
+ directory,
557
+ (0, pluginSourceState_1.pluginSourceState)(directory, {
558
+ env: effectiveEnv,
559
+ sourceDigest,
560
+ environment: environmentDigests.get(directory) ??
561
+ (0, pluginBuildEnvironment_1.pluginBuildEnvironment)(directory, effectiveEnv, undefined, sourceIdentities),
562
+ }),
563
+ ])),
564
+ project,
490
565
  };
491
- });
492
- return {
493
- ...collectHostInputSnapshot(project, context, records, projectHostInputs, revalidateHostInputHashes(projectHostInputHashes, projectHostInputs), revalidateHostInputRealpaths(projectHostInputRealpaths, projectHostInputs)),
494
- descriptorReadsDeclared: loadedEntries.every((entry) => entry.observationsComplete && (0, declaresHostInputReads_1.declaresHostInputReads)(entry.plugin)),
495
- discoveryInputsComplete: project.configInputsComplete === true,
496
- ...(identities === undefined ? {} : { identities }),
497
- nativePlugins: orderNativePlugins(nativePlugins),
498
- ...(loadedEntries.some((entry) => !entry.observationsComplete)
499
- ? { observationsComplete: false }
500
- : {}),
501
- // The directories the watch inputs named before the builds, as the builds
502
- // read them. Package selection shares these readings and also reads a
503
- // linked plugin's module root, which no build keys on, so only the build
504
- // directories are reported.
505
- pluginSources: Object.fromEntries([...sourceDigests]
506
- .filter(([directory]) => keyedDirectories.has(directory))
507
- .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
508
- .map(([directory, sourceDigest]) => [
509
- directory,
510
- (0, pluginSourceState_1.pluginSourceState)(directory, {
511
- env: effectiveEnv,
512
- sourceDigest,
513
- environment: environmentDigests.get(directory) ??
514
- (0, pluginBuildEnvironment_1.pluginBuildEnvironment)(directory, effectiveEnv, undefined, sourceIdentities),
515
- }),
516
- ])),
517
- project,
518
- };
519
- }
566
+ }
567
+ loadProjectPlugins.steps = steps;
568
+ })(loadProjectPlugins || (exports.loadProjectPlugins = loadProjectPlugins = {}));
520
569
  /**
521
570
  * Collect exact JavaScript-host files that universally influence the loaded
522
571
  * transform. Program files belong to the native reference graph; this list is
@@ -597,16 +646,16 @@ function collectHostInputSnapshot(project, context, records, baselineInputs, bas
597
646
  * cache proof for the ambiguous snapshot.
598
647
  */
599
648
  function readProjectHostInputSnapshot(options) {
600
- const { includePluginDiscovery, ...projectOptions } = options;
649
+ const { includePluginDiscovery, pluginConfigDir, ...projectOptions } = options;
601
650
  let project = (0, readProjectConfig_1.readProjectConfig)(projectOptions);
602
- let hostInputs = (0, collectProjectHostInputs_1.collectProjectHostInputs)(project, includePluginDiscovery);
651
+ let hostInputs = (0, collectProjectHostInputs_1.collectProjectHostInputs)(project, includePluginDiscovery, pluginConfigDir);
603
652
  const observedInputs = new Set(hostInputs);
604
653
  for (let attempt = 0; attempt < 3; attempt++) {
605
654
  const before = (0, hashHostInputPaths_1.hashHostInputPaths)(hostInputs);
606
655
  const beforeRealpaths = (0, realpathHostInputPaths_1.realpathHostInputPaths)(hostInputs);
607
656
  const beforeSignatures = hostInputMetadataSignatures(hostInputs);
608
657
  const candidateProject = (0, readProjectConfig_1.readProjectConfig)(projectOptions);
609
- const candidateInputs = (0, collectProjectHostInputs_1.collectProjectHostInputs)(candidateProject, includePluginDiscovery);
658
+ const candidateInputs = (0, collectProjectHostInputs_1.collectProjectHostInputs)(candidateProject, includePluginDiscovery, pluginConfigDir);
610
659
  for (const input of candidateInputs)
611
660
  observedInputs.add(input);
612
661
  const after = (0, hashHostInputPaths_1.hashHostInputPaths)(candidateInputs);
@@ -927,7 +976,7 @@ function loadPluginDescriptor(request, context, effectiveEnv, descriptorCache) {
927
976
  !error.retryWithTtsx) {
928
977
  throw error;
929
978
  }
930
- const loaded = loadDescriptorViaTtsx(request, context, effectiveEnv, descriptorCache.identities);
979
+ const loaded = loadDescriptorViaTtsx(request, context, effectiveEnv, descriptorCache);
931
980
  if (loaded === undefined) {
932
981
  throw error;
933
982
  }
@@ -957,20 +1006,26 @@ class CommonJsDescriptorLoadError extends Error {
957
1006
  * (`PluginDescriptorEvaluationCache`): an unchanged project pays a proof of its
958
1007
  * descriptor inputs instead of a runtime start and a graph load.
959
1008
  */
960
- function loadCommonJsDescriptor(request, context, effectiveEnv, descriptorCache) {
1009
+ function loadCommonJsDescriptor(request, context, loadEnv, descriptorCache) {
1010
+ // The evaluator is ttsc's own helper: inside a ttsx run it gets the user's
1011
+ // NODE_OPTIONS, not ttsx's preloads, which would refuse the descriptor cache
1012
+ // and run in every evaluation (#1740). The runtime probe below does the same.
1013
+ const effectiveEnv = TtsxNodeOptions_1.TtsxNodeOptions.forHelpers(loadEnv);
961
1014
  const runtime = pluginDescriptorRuntimeBinary(effectiveEnv);
962
1015
  const ttsx = effectiveEnv.TTSC_TTSX_BINARY ?? process.env.TTSC_TTSX_BINARY;
963
- const runtimeCapabilities = (0, javascriptRuntimeCapabilities_1.javascriptRuntimeCapabilities)(runtime, effectiveEnv, context.projectRoot, descriptorCache.identities);
1016
+ const runtimeCapabilities = (0, javascriptRuntimeCapabilities_1.javascriptRuntimeCapabilities)(runtime, effectiveEnv, descriptorCache.projectRoot, descriptorCache.identities);
964
1017
  const node = !runtimeCapabilities.bun &&
965
1018
  runtimeCapabilities.registerHooks &&
966
1019
  runtimeCapabilities.executable !== undefined
967
1020
  ? runtimeCapabilities.executable
968
- : (0, resolveNodeBinary_1.resolveNodeBinary)(effectiveEnv, context.projectRoot, descriptorCache.identities);
1021
+ : (0, resolveNodeBinary_1.resolveNodeBinary)(effectiveEnv, descriptorCache.projectRoot, descriptorCache.identities);
969
1022
  const cacheAuthority = {
970
1023
  additionalRuntime: node,
971
1024
  cacheDir: descriptorCache.cacheDir,
972
1025
  identities: descriptorCache.identities,
973
1026
  context,
1027
+ // Factory context anchors the consumer; the evaluator runs at the source root.
1028
+ cwd: descriptorCache.projectRoot,
974
1029
  // Everything the child receives besides the per-evaluation output paths.
975
1030
  env: {
976
1031
  ...effectiveEnv,
@@ -978,7 +1033,7 @@ function loadCommonJsDescriptor(request, context, effectiveEnv, descriptorCache)
978
1033
  ...descriptorRecordRoot(descriptorCache.identities),
979
1034
  TTSC_TTSX_BINARY: ttsx,
980
1035
  },
981
- projectRoot: context.projectRoot,
1036
+ projectRoot: descriptorCache.projectRoot,
982
1037
  request,
983
1038
  runtime,
984
1039
  version: descriptorCache.version,
@@ -1044,7 +1099,7 @@ function loadCommonJsDescriptor(request, context, effectiveEnv, descriptorCache)
1044
1099
  ...(runtimeCapabilities.bun ? [] : ["--input-type", ""]),
1045
1100
  shim,
1046
1101
  ], {
1047
- cwd: context.projectRoot,
1102
+ cwd: descriptorCache.projectRoot,
1048
1103
  env: {
1049
1104
  ...effectiveEnv,
1050
1105
  // The direct evaluator may be Bun, but ttsx and native config
@@ -1307,11 +1362,11 @@ const TS_SOURCE_PATTERN = /\.(?:[cm]?ts|tsx)$/i;
1307
1362
  * shim with plugins disabled across the whole graph. Returns `undefined` when
1308
1363
  * `ttsx` is unavailable, so the caller can rethrow the original load error.
1309
1364
  */
1310
- function loadDescriptorViaTtsx(request, context, effectiveEnv, identities) {
1365
+ function loadDescriptorViaTtsx(request, context, effectiveEnv, descriptorCache) {
1311
1366
  // Binary discovery prefers the instance environment, then the ambient
1312
1367
  // process.env (where `withPluginLoaderEnv` injects ttsc's own node/ttsx paths
1313
1368
  // just before this runs), then the running interpreter.
1314
- const node = (0, resolveNodeBinary_1.resolveNodeBinary)(effectiveEnv, context.projectRoot, identities);
1369
+ const node = (0, resolveNodeBinary_1.resolveNodeBinary)(effectiveEnv, descriptorCache.projectRoot, descriptorCache.identities);
1315
1370
  const ttsx = effectiveEnv.TTSC_TTSX_BINARY ?? process.env.TTSC_TTSX_BINARY;
1316
1371
  if (node === undefined || ttsx === undefined || ttsx.length === 0) {
1317
1372
  return undefined;
@@ -1332,9 +1387,9 @@ function loadDescriptorViaTtsx(request, context, effectiveEnv, identities) {
1332
1387
  },
1333
1388
  }));
1334
1389
  node_fs_1.default.writeFileSync(shim, PLUGIN_DESCRIPTOR_SHIM_SOURCE_1.PLUGIN_DESCRIPTOR_SHIM_SOURCE);
1335
- const trace = E2ETrace_1.E2ETrace.begin(node, [ttsx, "--no-plugins", shim], { cwd: context.projectRoot }, "plugin-descriptor");
1390
+ const trace = E2ETrace_1.E2ETrace.begin(node, [ttsx, "--no-plugins", shim], { cwd: descriptorCache.projectRoot }, "plugin-descriptor");
1336
1391
  const result = spawnOwnedDescriptor(node, [ttsx, "--no-plugins", shim], {
1337
- cwd: context.projectRoot,
1392
+ cwd: descriptorCache.projectRoot,
1338
1393
  encoding: "utf8",
1339
1394
  env: {
1340
1395
  ...effectiveEnv,
@@ -1342,7 +1397,7 @@ function loadDescriptorViaTtsx(request, context, effectiveEnv, identities) {
1342
1397
  // may recurse into further descriptor loads) finds them even when the
1343
1398
  // instance-env snapshot predates `withPluginLoaderEnv`.
1344
1399
  TTSC_NODE_BINARY: node,
1345
- ...descriptorRecordRoot(identities),
1400
+ ...descriptorRecordRoot(descriptorCache.identities),
1346
1401
  TTSC_TTSX_BINARY: ttsx,
1347
1402
  TTSC_PLUGIN_CONTEXT: JSON.stringify({
1348
1403
  binary: context.binary,
@@ -1625,12 +1680,12 @@ function resolvePluginSource(source, projectRoot) {
1625
1680
  * that it replaces a module with, which the build compiles in place
1626
1681
  * (`pluginModuleReplaceDirectories`).
1627
1682
  */
1628
- function pluginBuildDirectories(records, env, replacements, readers) {
1683
+ function pluginBuildDirectories(records, env, replacements, readers, identities) {
1629
1684
  const directories = new Set();
1630
1685
  for (const record of records) {
1631
1686
  directories.add(node_path_1.default.resolve(record.kind === "linked" ? record.packageDir : record.moduleRoot));
1632
1687
  if (record.kind === "executable") {
1633
- for (const directory of pluginReplacementWatchDirectories(record.moduleRoot, env, replacements, readers))
1688
+ for (const directory of pluginReplacementWatchDirectories(record.moduleRoot, env, replacements, readers, identities))
1634
1689
  directories.add(directory);
1635
1690
  }
1636
1691
  for (const contributor of record.contributors ?? [])
@@ -1643,13 +1698,14 @@ function pluginBuildDirectories(records, env, replacements, readers) {
1643
1698
  * The load shares this best-effort watch projection, not package validity or a
1644
1699
  * cross-load manifest cache. Go metadata/build admission still owns errors.
1645
1700
  */
1646
- function pluginReplacementWatchDirectories(moduleRoot, env, observed, readers) {
1701
+ function pluginReplacementWatchDirectories(moduleRoot, env, observed, readers, identities) {
1647
1702
  const previous = observed?.get(moduleRoot);
1648
1703
  if (previous !== undefined)
1649
1704
  return previous;
1650
1705
  let directories;
1651
1706
  try {
1652
- directories = (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(moduleRoot, env, undefined, undefined, readers).map((replacement) => replacement.directory);
1707
+ // The load's store goes with the reader this registers first (#1739).
1708
+ directories = (0, pluginModuleReplaceDirectories_1.pluginModuleReplaceDirectories)(moduleRoot, env, undefined, undefined, readers, identities).map((replacement) => replacement.directory);
1653
1709
  }
1654
1710
  catch {
1655
1711
  // An unreadable manifest/tool is not valid metadata. The already observed