@ttsc/unplugin 0.31.5 → 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 (248) hide show
  1. package/lib/core/bridge/TtscProjectRecord.d.cts +8 -0
  2. package/lib/core/bridge/TtscProjectRecord.d.mts +8 -0
  3. package/lib/core/bridge/TtscProjectRecord.d.ts +8 -0
  4. package/lib/core/bridge/readProjectRecordFile.js +5 -1
  5. package/lib/core/bridge/readProjectRecordFile.js.map +1 -1
  6. package/lib/core/bridge/readProjectRecordFile.mjs +5 -1
  7. package/lib/core/bridge/readProjectRecordFile.mjs.map +1 -1
  8. package/lib/core/observer/WatchScope.d.cts +3 -3
  9. package/lib/core/observer/WatchScope.d.mts +3 -3
  10. package/lib/core/observer/WatchScope.d.ts +3 -3
  11. package/lib/core/observer/createInputObserver.d.cts +7 -8
  12. package/lib/core/observer/createInputObserver.d.mts +7 -8
  13. package/lib/core/observer/createInputObserver.d.ts +7 -8
  14. package/lib/core/observer/createInputObserver.js +7 -8
  15. package/lib/core/observer/createInputObserver.js.map +1 -1
  16. package/lib/core/observer/createInputObserver.mjs +7 -8
  17. package/lib/core/observer/createInputObserver.mjs.map +1 -1
  18. package/lib/core/options/ResolvedTtscUnpluginOptions.d.cts +2 -0
  19. package/lib/core/options/ResolvedTtscUnpluginOptions.d.mts +2 -0
  20. package/lib/core/options/ResolvedTtscUnpluginOptions.d.ts +2 -0
  21. package/lib/core/options/TtscUnpluginOptions.d.cts +12 -2
  22. package/lib/core/options/TtscUnpluginOptions.d.mts +12 -2
  23. package/lib/core/options/TtscUnpluginOptions.d.ts +12 -2
  24. package/lib/core/options/resolveOptions.d.cts +4 -3
  25. package/lib/core/options/resolveOptions.d.mts +4 -3
  26. package/lib/core/options/resolveOptions.d.ts +4 -3
  27. package/lib/core/options/resolveOptions.js +5 -3
  28. package/lib/core/options/resolveOptions.js.map +1 -1
  29. package/lib/core/options/resolveOptions.mjs +5 -3
  30. package/lib/core/options/resolveOptions.mjs.map +1 -1
  31. package/lib/core/transform/TtscPrecompilation.d.cts +52 -0
  32. package/lib/core/transform/TtscPrecompilation.d.mts +52 -0
  33. package/lib/core/transform/TtscPrecompilation.d.ts +52 -0
  34. package/lib/core/transform/TtscPrecompilation.js +101 -0
  35. package/lib/core/transform/TtscPrecompilation.js.map +1 -0
  36. package/lib/core/transform/TtscPrecompilation.mjs +101 -0
  37. package/lib/core/transform/TtscPrecompilation.mjs.map +1 -0
  38. package/lib/core/transform/cache/selectCachedGenerationAction.d.cts +2 -0
  39. package/lib/core/transform/cache/selectCachedGenerationAction.d.mts +2 -0
  40. package/lib/core/transform/cache/selectCachedGenerationAction.d.ts +2 -0
  41. package/lib/core/transform/cache/selectCachedGenerationAction.js +1 -1
  42. package/lib/core/transform/cache/selectCachedGenerationAction.js.map +1 -1
  43. package/lib/core/transform/cache/selectCachedGenerationAction.mjs +1 -1
  44. package/lib/core/transform/cache/selectCachedGenerationAction.mjs.map +1 -1
  45. package/lib/core/transform/cache/withdrawTtscTransformGenerations.js +2 -0
  46. package/lib/core/transform/cache/withdrawTtscTransformGenerations.js.map +1 -1
  47. package/lib/core/transform/cache/withdrawTtscTransformGenerations.mjs +2 -0
  48. package/lib/core/transform/cache/withdrawTtscTransformGenerations.mjs.map +1 -1
  49. package/lib/core/transform/envelope/generationCaseSensitivity.d.cts +32 -0
  50. package/lib/core/transform/envelope/generationCaseSensitivity.d.mts +32 -0
  51. package/lib/core/transform/envelope/generationCaseSensitivity.d.ts +32 -0
  52. package/lib/core/transform/envelope/generationCaseSensitivity.js +39 -0
  53. package/lib/core/transform/envelope/generationCaseSensitivity.js.map +1 -0
  54. package/lib/core/transform/envelope/generationCaseSensitivity.mjs +37 -0
  55. package/lib/core/transform/envelope/generationCaseSensitivity.mjs.map +1 -0
  56. package/lib/core/transform/envelope/selectExternalInputPaths.js +2 -1
  57. package/lib/core/transform/envelope/selectExternalInputPaths.js.map +1 -1
  58. package/lib/core/transform/envelope/selectExternalInputPaths.mjs +2 -1
  59. package/lib/core/transform/envelope/selectExternalInputPaths.mjs.map +1 -1
  60. package/lib/core/transform/envelope/selectNotifiableAbsentInputs.js +2 -1
  61. package/lib/core/transform/envelope/selectNotifiableAbsentInputs.js.map +1 -1
  62. package/lib/core/transform/envelope/selectNotifiableAbsentInputs.mjs +2 -1
  63. package/lib/core/transform/envelope/selectNotifiableAbsentInputs.mjs.map +1 -1
  64. package/lib/core/transform/filesystem/createHostPathIdentityContext.d.cts +9 -1
  65. package/lib/core/transform/filesystem/createHostPathIdentityContext.d.mts +9 -1
  66. package/lib/core/transform/filesystem/createHostPathIdentityContext.d.ts +9 -1
  67. package/lib/core/transform/filesystem/createHostPathIdentityContext.js +10 -2
  68. package/lib/core/transform/filesystem/createHostPathIdentityContext.js.map +1 -1
  69. package/lib/core/transform/filesystem/createHostPathIdentityContext.mjs +10 -2
  70. package/lib/core/transform/filesystem/createHostPathIdentityContext.mjs.map +1 -1
  71. package/lib/core/transform/generation/captureTransformGeneration.d.cts +2 -0
  72. package/lib/core/transform/generation/captureTransformGeneration.d.mts +2 -0
  73. package/lib/core/transform/generation/captureTransformGeneration.d.ts +2 -0
  74. package/lib/core/transform/generation/captureTransformGeneration.js +17 -4
  75. package/lib/core/transform/generation/captureTransformGeneration.js.map +1 -1
  76. package/lib/core/transform/generation/captureTransformGeneration.mjs +17 -4
  77. package/lib/core/transform/generation/captureTransformGeneration.mjs.map +1 -1
  78. package/lib/core/transform/generation/selectPersistentHostInputs.js +2 -1
  79. package/lib/core/transform/generation/selectPersistentHostInputs.js.map +1 -1
  80. package/lib/core/transform/generation/selectPersistentHostInputs.mjs +2 -1
  81. package/lib/core/transform/generation/selectPersistentHostInputs.mjs.map +1 -1
  82. package/lib/core/transform/generation/transformProject.d.cts +2 -0
  83. package/lib/core/transform/generation/transformProject.d.mts +2 -0
  84. package/lib/core/transform/generation/transformProject.d.ts +2 -0
  85. package/lib/core/transform/generation/transformProject.js.map +1 -1
  86. package/lib/core/transform/generation/transformProject.mjs.map +1 -1
  87. package/lib/core/transform/inputs/nativeInputPredicateState.js.map +1 -1
  88. package/lib/core/transform/inputs/nativeInputPredicateState.mjs.map +1 -1
  89. package/lib/core/transform/tracker/createHostInputMutationTracker.d.cts +7 -1
  90. package/lib/core/transform/tracker/createHostInputMutationTracker.d.mts +7 -1
  91. package/lib/core/transform/tracker/createHostInputMutationTracker.d.ts +7 -1
  92. package/lib/core/transform/tracker/createHostInputMutationTracker.js +8 -2
  93. package/lib/core/transform/tracker/createHostInputMutationTracker.js.map +1 -1
  94. package/lib/core/transform/tracker/createHostInputMutationTracker.mjs +8 -2
  95. package/lib/core/transform/tracker/createHostInputMutationTracker.mjs.map +1 -1
  96. package/lib/core/transform/tracker/createProjectMutationTracker.d.cts +6 -4
  97. package/lib/core/transform/tracker/createProjectMutationTracker.d.mts +6 -4
  98. package/lib/core/transform/tracker/createProjectMutationTracker.d.ts +6 -4
  99. package/lib/core/transform/tracker/createProjectMutationTracker.js +29 -6
  100. package/lib/core/transform/tracker/createProjectMutationTracker.js.map +1 -1
  101. package/lib/core/transform/tracker/createProjectMutationTracker.mjs +29 -6
  102. package/lib/core/transform/tracker/createProjectMutationTracker.mjs.map +1 -1
  103. package/lib/core/transform/transformTtsc.d.cts +8 -24
  104. package/lib/core/transform/transformTtsc.d.mts +8 -24
  105. package/lib/core/transform/transformTtsc.d.ts +8 -24
  106. package/lib/core/transform/transformTtsc.js +56 -32
  107. package/lib/core/transform/transformTtsc.js.map +1 -1
  108. package/lib/core/transform/transformTtsc.mjs +56 -32
  109. package/lib/core/transform/transformTtsc.mjs.map +1 -1
  110. package/lib/core/transform/validation/captureUniversalHostInputValidation.d.cts +1 -1
  111. package/lib/core/transform/validation/captureUniversalHostInputValidation.d.mts +1 -1
  112. package/lib/core/transform/validation/captureUniversalHostInputValidation.d.ts +1 -1
  113. package/lib/core/transform/validation/captureUniversalHostInputValidation.js +12 -4
  114. package/lib/core/transform/validation/captureUniversalHostInputValidation.js.map +1 -1
  115. package/lib/core/transform/validation/captureUniversalHostInputValidation.mjs +12 -4
  116. package/lib/core/transform/validation/captureUniversalHostInputValidation.mjs.map +1 -1
  117. package/lib/core/transform/validation/matchesCachedSource.d.cts +9 -2
  118. package/lib/core/transform/validation/matchesCachedSource.d.mts +9 -2
  119. package/lib/core/transform/validation/matchesCachedSource.d.ts +9 -2
  120. package/lib/core/transform/validation/matchesCachedSource.js +11 -3
  121. package/lib/core/transform/validation/matchesCachedSource.js.map +1 -1
  122. package/lib/core/transform/validation/matchesCachedSource.mjs +11 -3
  123. package/lib/core/transform/validation/matchesCachedSource.mjs.map +1 -1
  124. package/lib/core/transform/watch/TtscTransformHooks.d.cts +9 -0
  125. package/lib/core/transform/watch/TtscTransformHooks.d.mts +9 -0
  126. package/lib/core/transform/watch/TtscTransformHooks.d.ts +9 -0
  127. package/lib/core/transform/watch/TtscWatchInputBaseline.d.cts +2 -1
  128. package/lib/core/transform/watch/TtscWatchInputBaseline.d.mts +2 -1
  129. package/lib/core/transform/watch/TtscWatchInputBaseline.d.ts +2 -1
  130. package/lib/core/transform/watch/isWatchInputKeyBaseline.js.map +1 -1
  131. package/lib/core/transform/watch/isWatchInputKeyBaseline.mjs.map +1 -1
  132. package/lib/core/transform/watch/notifyProjectRecord.d.cts +3 -4
  133. package/lib/core/transform/watch/notifyProjectRecord.d.mts +3 -4
  134. package/lib/core/transform/watch/notifyProjectRecord.d.ts +3 -4
  135. package/lib/core/transform/watch/notifyProjectRecord.js +5 -4
  136. package/lib/core/transform/watch/notifyProjectRecord.js.map +1 -1
  137. package/lib/core/transform/watch/notifyProjectRecord.mjs +5 -4
  138. package/lib/core/transform/watch/notifyProjectRecord.mjs.map +1 -1
  139. package/lib/core/transform/watch/recordedDependencyWitnesses.d.cts +30 -0
  140. package/lib/core/transform/watch/recordedDependencyWitnesses.d.mts +30 -0
  141. package/lib/core/transform/watch/recordedDependencyWitnesses.d.ts +30 -0
  142. package/lib/core/transform/watch/recordedDependencyWitnesses.js +51 -0
  143. package/lib/core/transform/watch/recordedDependencyWitnesses.js.map +1 -0
  144. package/lib/core/transform/watch/recordedDependencyWitnesses.mjs +49 -0
  145. package/lib/core/transform/watch/recordedDependencyWitnesses.mjs.map +1 -0
  146. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.d.cts +1 -2
  147. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.d.mts +1 -2
  148. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.d.ts +1 -2
  149. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.js +1 -2
  150. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.js.map +1 -1
  151. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.mjs +1 -2
  152. package/lib/core/transform/watch/watchInputEvidenceMatchesBaseline.mjs.map +1 -1
  153. package/lib/core/tsconfig/TsconfigReadTransaction.d.cts +11 -8
  154. package/lib/core/tsconfig/TsconfigReadTransaction.d.mts +11 -8
  155. package/lib/core/tsconfig/TsconfigReadTransaction.d.ts +11 -8
  156. package/lib/core/tsconfig/TsconfigReadTransaction.js +11 -8
  157. package/lib/core/tsconfig/TsconfigReadTransaction.js.map +1 -1
  158. package/lib/core/tsconfig/TsconfigReadTransaction.mjs +11 -8
  159. package/lib/core/tsconfig/TsconfigReadTransaction.mjs.map +1 -1
  160. package/lib/core/tsconfig/findDeclaredCompilerOption.d.cts +3 -2
  161. package/lib/core/tsconfig/findDeclaredCompilerOption.d.mts +3 -2
  162. package/lib/core/tsconfig/findDeclaredCompilerOption.d.ts +3 -2
  163. package/lib/core/tsconfig/findDeclaredCompilerOption.js +3 -2
  164. package/lib/core/tsconfig/findDeclaredCompilerOption.js.map +1 -1
  165. package/lib/core/tsconfig/findDeclaredCompilerOption.mjs +3 -2
  166. package/lib/core/tsconfig/findDeclaredCompilerOption.mjs.map +1 -1
  167. package/lib/core/tsconfig/findDeclaredFileSpecs.d.cts +3 -2
  168. package/lib/core/tsconfig/findDeclaredFileSpecs.d.mts +3 -2
  169. package/lib/core/tsconfig/findDeclaredFileSpecs.d.ts +3 -2
  170. package/lib/core/tsconfig/findDeclaredFileSpecs.js +3 -2
  171. package/lib/core/tsconfig/findDeclaredFileSpecs.js.map +1 -1
  172. package/lib/core/tsconfig/findDeclaredFileSpecs.mjs +3 -2
  173. package/lib/core/tsconfig/findDeclaredFileSpecs.mjs.map +1 -1
  174. package/lib/core/tsconfig/findDeclaredPaths.d.cts +3 -3
  175. package/lib/core/tsconfig/findDeclaredPaths.d.mts +3 -3
  176. package/lib/core/tsconfig/findDeclaredPaths.d.ts +3 -3
  177. package/lib/core/tsconfig/findDeclaredPaths.js +2 -2
  178. package/lib/core/tsconfig/findDeclaredPaths.mjs +2 -2
  179. package/lib/core/tsconfig/findDeclaredValue.d.cts +2 -1
  180. package/lib/core/tsconfig/findDeclaredValue.d.mts +2 -1
  181. package/lib/core/tsconfig/findDeclaredValue.d.ts +2 -1
  182. package/lib/core/tsconfig/findDeclaredValue.js +2 -1
  183. package/lib/core/tsconfig/findDeclaredValue.js.map +1 -1
  184. package/lib/core/tsconfig/findDeclaredValue.mjs +2 -1
  185. package/lib/core/tsconfig/findDeclaredValue.mjs.map +1 -1
  186. package/lib/core/tsconfig/readEffectiveTsconfigTemplateCompilerOptions.js +1 -1
  187. package/lib/core/tsconfig/readEffectiveTsconfigTemplateCompilerOptions.mjs +1 -1
  188. package/lib/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.d.cts +2 -2
  189. package/lib/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.d.mts +2 -2
  190. package/lib/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.d.ts +2 -2
  191. package/lib/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.js +2 -2
  192. package/lib/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.mjs +2 -2
  193. package/lib/core/tsconfig/readProjectMembershipPolicy.d.cts +3 -3
  194. package/lib/core/tsconfig/readProjectMembershipPolicy.d.mts +3 -3
  195. package/lib/core/tsconfig/readProjectMembershipPolicy.d.ts +3 -3
  196. package/lib/core/tsconfig/readProjectMembershipPolicy.js +4 -4
  197. package/lib/core/tsconfig/readProjectMembershipPolicy.mjs +4 -4
  198. package/lib/core/unplugin.js +14 -0
  199. package/lib/core/unplugin.js.map +1 -1
  200. package/lib/core/unplugin.mjs +14 -0
  201. package/lib/core/unplugin.mjs.map +1 -1
  202. package/lib/core/vite/installViteDependencyScanPlugins.d.cts +46 -0
  203. package/lib/core/vite/installViteDependencyScanPlugins.d.mts +46 -0
  204. package/lib/core/vite/installViteDependencyScanPlugins.d.ts +46 -0
  205. package/lib/core/vite/installViteDependencyScanPlugins.js +186 -0
  206. package/lib/core/vite/installViteDependencyScanPlugins.js.map +1 -0
  207. package/lib/core/vite/installViteDependencyScanPlugins.mjs +184 -0
  208. package/lib/core/vite/installViteDependencyScanPlugins.mjs.map +1 -0
  209. package/package.json +5 -4
  210. package/src/core/bridge/TtscProjectRecord.ts +9 -0
  211. package/src/core/bridge/readProjectRecordFile.ts +5 -1
  212. package/src/core/observer/WatchScope.ts +3 -3
  213. package/src/core/observer/createInputObserver.ts +7 -8
  214. package/src/core/options/ResolvedTtscUnpluginOptions.ts +3 -0
  215. package/src/core/options/TtscUnpluginOptions.ts +13 -2
  216. package/src/core/options/resolveOptions.ts +6 -3
  217. package/src/core/transform/TtscPrecompilation.ts +103 -0
  218. package/src/core/transform/cache/selectCachedGenerationAction.ts +3 -0
  219. package/src/core/transform/cache/withdrawTtscTransformGenerations.ts +2 -0
  220. package/src/core/transform/envelope/generationCaseSensitivity.ts +46 -0
  221. package/src/core/transform/envelope/selectExternalInputPaths.ts +5 -1
  222. package/src/core/transform/envelope/selectNotifiableAbsentInputs.ts +5 -1
  223. package/src/core/transform/filesystem/createHostPathIdentityContext.ts +9 -1
  224. package/src/core/transform/generation/captureTransformGeneration.ts +22 -2
  225. package/src/core/transform/generation/selectPersistentHostInputs.ts +5 -1
  226. package/src/core/transform/generation/transformProject.ts +3 -0
  227. package/src/core/transform/inputs/nativeInputPredicateState.ts +1 -3
  228. package/src/core/transform/tracker/createHostInputMutationTracker.ts +10 -1
  229. package/src/core/transform/tracker/createProjectMutationTracker.ts +34 -6
  230. package/src/core/transform/transformTtsc.ts +86 -37
  231. package/src/core/transform/validation/captureUniversalHostInputValidation.ts +12 -4
  232. package/src/core/transform/validation/matchesCachedSource.ts +9 -2
  233. package/src/core/transform/watch/TtscTransformHooks.ts +10 -0
  234. package/src/core/transform/watch/TtscWatchInputBaseline.ts +2 -1
  235. package/src/core/transform/watch/isWatchInputKeyBaseline.ts +4 -2
  236. package/src/core/transform/watch/notifyProjectRecord.ts +5 -4
  237. package/src/core/transform/watch/recordedDependencyWitnesses.ts +50 -0
  238. package/src/core/transform/watch/watchInputEvidenceMatchesBaseline.ts +1 -2
  239. package/src/core/tsconfig/TsconfigReadTransaction.ts +11 -8
  240. package/src/core/tsconfig/findDeclaredCompilerOption.ts +3 -2
  241. package/src/core/tsconfig/findDeclaredFileSpecs.ts +3 -2
  242. package/src/core/tsconfig/findDeclaredPaths.ts +3 -3
  243. package/src/core/tsconfig/findDeclaredValue.ts +2 -1
  244. package/src/core/tsconfig/readEffectiveTsconfigTemplateCompilerOptions.ts +1 -1
  245. package/src/core/tsconfig/readEffectiveTsconfigTemplateFileSpecs.ts +2 -2
  246. package/src/core/tsconfig/readProjectMembershipPolicy.ts +4 -4
  247. package/src/core/unplugin.ts +32 -0
  248. package/src/core/vite/installViteDependencyScanPlugins.ts +219 -0
@@ -0,0 +1,103 @@
1
+ import path from "node:path";
2
+
3
+ import { isTransformTarget } from "../isTransformTarget";
4
+ import type { ResolvedTtscUnpluginOptions } from "../options/ResolvedTtscUnpluginOptions";
5
+ import type { TtscTransformCache } from "./cache/TtscTransformCache";
6
+ import { transformCacheEpoch } from "./cache/transformCacheEpoch";
7
+ import { transformFilesystem } from "./cache/transformFilesystem";
8
+ import { pluginsAreDisabled } from "./utils/pluginsAreDisabled";
9
+
10
+ /**
11
+ * Schedule explicitly selected source entries before the host reaches their
12
+ * imports. Compilation and proof stay with the ordinary transform core.
13
+ *
14
+ * A host often discovers its next workspace project only after the preceding
15
+ * project's transform finishes (#1764). Starting known entries together avoids
16
+ * that dependency on host traversal order. Walking unrelated projects would
17
+ * execute plugins the user never selected, so this owner uses only the supplied
18
+ * entries. Preparation leaves module delivery and host registration to the
19
+ * actual request.
20
+ *
21
+ * @evidence contracts/common.md#principled-implementation Explicit entries authorize scheduling; the supplied core capture retains project selection, configuration identity and all generation proofs. The context contains the current working directory, options, aliases and delivery epoch, not new cache authority.
22
+ * @evidence contracts/common.md#clear-and-simple-design One scheduler owns per-cache startup deduplication and reset; the transform core owns compiled generations and their disposal.
23
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No directory-wide discovery, plugin substitution or skipped proof is introduced; preparation never impersonates a host module delivery.
24
+ * @evidence contracts/common.md#meaningful-documentation Native prose explains host traversal order, explicit effectful selection and separation from delivery; members state context lifetime and failure ownership.
25
+ * @evidence contracts/portability.md#os-neutral-implementation Entries resolve through native path semantics from the current cwd and read through the cache's filesystem boundary; source eligibility uses the adapter's existing shared predicate.
26
+ * @evidence contracts/performance.md#efficient-algorithms Each new context serializes cwd, options and aliases, then resolves and deduplicates E entries and reads their source bytes once. Capture starts without waiting for other entries; native capture and proof retain their own costs. Repeated requests serialize the context but do not reread the entries.
27
+ * @evidence contracts/performance.md#reuse-equivalent-work One latest context per cache and epoch starts each entry once; ordinary project keys deduplicate entries in the same project. Context equality controls scheduling only, and retained results still need normal admission.
28
+ * @evidence contracts/performance.md#bound-retention-and-release-resources A WeakMap retains one context string per live cache. Startup rejection handlers consume each detached promise; running captures and native resources remain cache-owned and withdrawal disposes their fulfilled generations. No cancellation deadline or entry-count cap is added.
29
+ */
30
+ export namespace TtscPrecompilation {
31
+ const contexts = new WeakMap<TtscTransformCache, string>();
32
+
33
+ /**
34
+ * Start every eligible explicit entry without waiting for its siblings.
35
+ * Failed speculative preparation is reported when its module is requested, so
36
+ * an unrelated delivered project's load can proceed.
37
+ *
38
+ * @evidence contracts/common.md#principled-implementation The context is installed before captures start, so recursive and concurrent deliveries cannot restart the same batch; only supported source entries reach normal core capture.
39
+ * @evidence contracts/common.md#clear-and-simple-design One synchronous loop separates startup ownership from asynchronous capture settlement.
40
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Failed entry reads are not fabricated as empty source, and rejected captures do not become successful output. Actual delivery remains the error boundary.
41
+ * @evidence contracts/common.md#meaningful-documentation Prose states parallel startup and deferred error reporting; callback and context ownership remain explicit.
42
+ * @evidence contracts/portability.md#os-neutral-implementation Native absolute entry paths are passed as exact paths to capture and source reads use the same cache-owned filesystem as generation validation.
43
+ * @evidence contracts/performance.md#efficient-algorithms Context serialization precedes one E-entry deduplicating loop and actual source-byte reads; captures overlap their asynchronous work rather than being awaited sequentially.
44
+ * @evidence contracts/performance.md#reuse-equivalent-work Identical latest cwd, options, aliases and epoch skip scheduling; project Promise identity and normal input proof decide compile reuse, independently of this scheduling record.
45
+ * @evidence contracts/performance.md#bound-retention-and-release-resources Context state follows its weak cache key. Every started capture receives a rejection handler immediately; its own core/cache owners release native resources, including withdrawal during capture.
46
+ */
47
+ export function start(
48
+ cache: TtscTransformCache,
49
+ options: ResolvedTtscUnpluginOptions,
50
+ aliasPaths: Record<string, string[]> | undefined,
51
+ capture: (file: string, source: string) => Promise<unknown>,
52
+ ): void {
53
+ if (
54
+ options.precompile?.length === 0 ||
55
+ options.precompile === undefined ||
56
+ pluginsAreDisabled(options.plugins)
57
+ )
58
+ return;
59
+ // Entry, project and projectRoot paths all use cwd. The same relative
60
+ // options under another directory select different work, even if the
61
+ // host retains its cache; capture coordinates must belong to this call.
62
+ const cwd = process.cwd();
63
+ const context = JSON.stringify([
64
+ cwd,
65
+ options,
66
+ aliasPaths,
67
+ transformCacheEpoch(cache),
68
+ ]);
69
+ if (contexts.get(cache) === context) return;
70
+ contexts.set(cache, context);
71
+ const entries = new Set<string>();
72
+ const filesystem = transformFilesystem(cache);
73
+ for (const entry of options.precompile) {
74
+ if (!isTransformTarget(entry)) continue;
75
+ const file = path.resolve(cwd, entry);
76
+ if (entries.has(file)) continue;
77
+ entries.add(file);
78
+ try {
79
+ const source = filesystem.readFile(file).toString("utf8");
80
+ void capture(file, source).catch(() => undefined);
81
+ } catch {
82
+ // A missing entry must not poison another project's load. Its actual
83
+ // host delivery still owns the native read error and recovery.
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * End startup ownership when the cache withdraws its generations.
90
+ *
91
+ * @evidence contracts/common.md#principled-implementation Withdrawal removes scheduling state with generation ownership so a later lifecycle can prepare the same entries again.
92
+ * @evidence contracts/common.md#clear-and-simple-design One weak-key deletion shares the cache's existing reset boundary.
93
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts Clearing scheduling permission does not admit or cancel any compiled result.
94
+ * @evidence contracts/common.md#meaningful-documentation The headline identifies the same generation withdrawal boundary used by callers.
95
+ * @evidenceExclude contracts/portability.md#os-neutral-implementation WeakMap deletion performs no native operation.
96
+ * @evidenceExclude contracts/performance.md#efficient-algorithms One identity deletion chooses no traversal or processing strategy.
97
+ * @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation withdraws the previous scheduling record rather than reusing work.
98
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources Deletion releases the single scheduling record; native resources remain owned by generation withdrawal.
99
+ */
100
+ export function clear(cache: TtscTransformCache): void {
101
+ contexts.delete(cache);
102
+ }
103
+ }
@@ -43,6 +43,8 @@ export function selectCachedGenerationAction(props: {
43
43
  generation: Promise<TtscCachedProjectTransform>;
44
44
  key: string;
45
45
  source: string;
46
+ /** False only for speculative source reads before an actual module delivery. */
47
+ reportDivergence?: boolean;
46
48
  proof?: TtscGenerationProof.Transaction;
47
49
  }): "serve" | "retry" | "capture" {
48
50
  if (
@@ -57,6 +59,7 @@ export function selectCachedGenerationAction(props: {
57
59
  props.source,
58
60
  props.epoch,
59
61
  props.proof,
62
+ props.reportDivergence,
60
63
  )
61
64
  ) {
62
65
  return "serve";
@@ -1,3 +1,4 @@
1
+ import { TtscPrecompilation } from "../TtscPrecompilation";
1
2
  import type { TtscTransformCache } from "./TtscTransformCache";
2
3
  import { disposeCachedTransform } from "./disposeCachedTransform";
3
4
 
@@ -35,6 +36,7 @@ export function withdrawTtscTransformGenerations(
35
36
  cache: TtscTransformCache,
36
37
  ): void {
37
38
  const generations = [...cache.values()];
39
+ TtscPrecompilation.clear(cache);
38
40
  cache.clear();
39
41
  for (const generation of generations) {
40
42
  void generation.then(disposeCachedTransform, () => undefined);
@@ -0,0 +1,46 @@
1
+ import type { ITtscCompilerTransformation } from "ttsc";
2
+
3
+ import { resultFilesystem } from "../cache/resultFilesystem";
4
+ import type { TtscTransformFilesystemOperations } from "../filesystem/TtscTransformFilesystemOperations";
5
+ import { envelopeDerivation } from "./envelopeDerivation";
6
+
7
+ /**
8
+ * The directory case answers of one generation's identity context
9
+ * (`envelopeDerivation`), for another identity context built while that
10
+ * generation is captured over the same filesystem view.
11
+ *
12
+ * Capturing a generation builds several identity contexts: the input selections
13
+ * and the trackers' baselines. Each asked every directory's case policy again,
14
+ * which on Windows costs an `fsutil` process for a directory whose names cannot
15
+ * settle it (samchon/ttsc#1742). They now ask the generation's own context,
16
+ * which memoizes each directory once for the generation. A view other than the
17
+ * one registered for the result observes for itself, and a tracker's re-check
18
+ * after an event still builds a context of its own.
19
+ *
20
+ * @evidence contracts/common.md#principled-implementation Sharing is admitted only for the filesystem view registered for the result, the view the generation context observes; any other view keeps its own observations.
21
+ * @evidence contracts/common.md#clear-and-simple-design One selector returns the generation's case operation, or the view's own for another view, and callers pass it to the existing context factory.
22
+ * @evidence contracts/common.md#prohibited-implementation-shortcuts No answer is cached beyond the generation context that observed it, and a tracker's event re-check is not routed through it.
23
+ * @evidence contracts/common.md#meaningful-documentation Native paragraphs explain the repeated probes, the view condition and the re-check exception.
24
+ * @evidence contracts/portability.md#os-neutral-implementation The generation context observes case policy through the registered view's own probes; no platform assumption is introduced.
25
+ * @evidence contracts/performance.md#efficient-algorithms One registry lookup and one derivation lookup; the returned operation delegates to the generation context's memo.
26
+ * @evidence contracts/performance.md#reuse-equivalent-work Every context capturing one generation shares the generation's answer for each directory.
27
+ * @evidenceExclude contracts/performance.md#bound-retention-and-release-resources The answers belong to the generation context, which the generation already owns; this selector retains nothing.
28
+ */
29
+ export function generationCaseSensitivity(props: {
30
+ /** The view the caller's context observes through. */
31
+ filesystem: TtscTransformFilesystemOperations;
32
+
33
+ /** Native project directory of the generation. */
34
+ projectRoot: string;
35
+
36
+ /** The generation's compiler result. */
37
+ result: ITtscCompilerTransformation;
38
+ }): TtscTransformFilesystemOperations["caseSensitive"] {
39
+ if (resultFilesystem(props.result) !== props.filesystem)
40
+ return props.filesystem.caseSensitive;
41
+ const context = envelopeDerivation({
42
+ projectRoot: props.projectRoot,
43
+ result: props.result,
44
+ }).identityContext;
45
+ return (directory) => context.caseSensitive(directory);
46
+ }
@@ -10,6 +10,7 @@ import { isProjectWalkPath } from "../project/isProjectWalkPath";
10
10
  import { isTransformScratchInput } from "../tsconfig/isTransformScratchInput";
11
11
  import { envelopeDerivation } from "./envelopeDerivation";
12
12
  import { envelopeGraphIndexes } from "./envelopeGraphIndexes";
13
+ import { generationCaseSensitivity } from "./generationCaseSensitivity";
13
14
 
14
15
  /**
15
16
  * Derive the absolute out-of-walk input set of a whole project transform: the
@@ -63,7 +64,10 @@ export function selectExternalInputPaths(props: {
63
64
  }
64
65
  const members: string[] = [];
65
66
  const filesystem = props.filesystem ?? DEFAULT_FILESYSTEM_OPERATIONS;
66
- const identities = createHostPathIdentityContext(filesystem);
67
+ const identities = createHostPathIdentityContext(
68
+ filesystem,
69
+ generationCaseSensitivity({ ...props, filesystem }),
70
+ );
67
71
  const resolutionCandidates = new Set<string>();
68
72
  const graph = props.result.graph;
69
73
  const graphState = envelopeDerivation({
@@ -8,6 +8,7 @@ import { relativeToProject } from "../filesystem/relativeToProject";
8
8
  import { isTransformScratchInput } from "../tsconfig/isTransformScratchInput";
9
9
  import { envelopeDerivation } from "./envelopeDerivation";
10
10
  import { envelopeGraphIndexes } from "./envelopeGraphIndexes";
11
+ import { generationCaseSensitivity } from "./generationCaseSensitivity";
11
12
 
12
13
  /**
13
14
  * The generation's resolution candidates recorded as not files, so its
@@ -72,7 +73,10 @@ export function selectNotifiableAbsentInputs(props: {
72
73
  if (graph === undefined) {
73
74
  return empty;
74
75
  }
75
- const identities = createHostPathIdentityContext(props.filesystem);
76
+ const identities = createHostPathIdentityContext(
77
+ props.filesystem,
78
+ generationCaseSensitivity(props),
79
+ );
76
80
  const graphState = envelopeDerivation({
77
81
  projectRoot: props.projectRoot,
78
82
  result: props.result,
@@ -29,6 +29,8 @@ import type { TtscTransformFilesystemOperations } from "./TtscTransformFilesyste
29
29
  * The returned context memoizes path, realpath and case observations for
30
30
  * repeated questions within one filesystem transaction. Callers must create
31
31
  * a new context when later filesystem changes can invalidate those observations.
32
+ * A caller capturing one generation can hand in that generation's case answers,
33
+ * so its contexts do not each ask the filesystem again (samchon/ttsc#1742).
32
34
  * @evidence contracts/performance.md#bound-retention-and-release-resources
33
35
  * Ownership of the context and its maps transfers to the caller. Retained
34
36
  * entries grow with queried paths and ancestors and have no internal eviction;
@@ -36,9 +38,15 @@ import type { TtscTransformFilesystemOperations } from "./TtscTransformFilesyste
36
38
  */
37
39
  export function createHostPathIdentityContext(
38
40
  filesystem: TtscTransformFilesystemOperations = DEFAULT_FILESYSTEM_OPERATIONS,
41
+ /**
42
+ * Case answers this context shares with the others of its unit of work,
43
+ * observed through the same `filesystem`; omitted, the context asks for
44
+ * itself.
45
+ */
46
+ caseSensitive: TtscTransformFilesystemOperations["caseSensitive"] = filesystem.caseSensitive,
39
47
  ): FilesystemPathIdentityContext {
40
48
  return createFilesystemPathIdentityContext({
41
- caseSensitive: filesystem.caseSensitive,
49
+ caseSensitive,
42
50
  lstat: filesystem.lstat,
43
51
  platform: filesystem.platform,
44
52
  readdir: (directory) =>
@@ -12,6 +12,7 @@ import { TRANSFORM_RESULT_MEMBERSHIP } from "../cache/TRANSFORM_RESULT_MEMBERSHI
12
12
  import type { TtscCachedProjectTransform } from "../cache/TtscCachedProjectTransform";
13
13
  import { refreshFilesystemClockReference } from "../clock/refreshFilesystemClockReference";
14
14
  import { reportDivergentDelivery } from "../diagnostics/reportDivergentDelivery";
15
+ import { generationCaseSensitivity } from "../envelope/generationCaseSensitivity";
15
16
  import { selectDeclaredProjectInputKeys } from "../envelope/selectDeclaredProjectInputKeys";
16
17
  import { selectExternalInputPaths } from "../envelope/selectExternalInputPaths";
17
18
  import { selectNotifiableAbsentInputs } from "../envelope/selectNotifiableAbsentInputs";
@@ -126,6 +127,9 @@ export async function captureTransformGeneration(props: {
126
127
  /** Host-delivered text compared with the compiler's on-disk source baseline. */
127
128
  currentSource: string;
128
129
 
130
+ /** False for preparation, whose source read is not a host module delivery. */
131
+ reportDivergence?: boolean;
132
+
129
133
  /** Optional delivery epoch to which this captured observation belongs. */
130
134
  deliveryEpoch?: number;
131
135
 
@@ -457,6 +461,13 @@ export async function captureTransformGeneration(props: {
457
461
  clockReferenceDirectory ?? scratchDirectory,
458
462
  props.filesystem,
459
463
  );
464
+ // Every context capturing this generation below shares the generation's
465
+ // directory case answers, rather than each asking again (samchon/ttsc#1742).
466
+ const generationCase = generationCaseSensitivity({
467
+ filesystem: props.filesystem,
468
+ projectRoot,
469
+ result,
470
+ });
460
471
  const persistentHostInputs = selectPersistentHostInputs({
461
472
  filesystem: props.filesystem,
462
473
  projectRoot,
@@ -518,6 +529,7 @@ export async function captureTransformGeneration(props: {
518
529
  projectRoot,
519
530
  result,
520
531
  }),
532
+ generationCase,
521
533
  )
522
534
  : undefined;
523
535
  compileTrace?.("bridge-tracker-lifecycle", {
@@ -566,6 +578,7 @@ export async function captureTransformGeneration(props: {
566
578
  .filter((input) => !absentCandidates.has(input))
567
579
  .map((input) => [path.resolve(input), "presence" as const]),
568
580
  ),
581
+ generationCase,
569
582
  )
570
583
  : undefined;
571
584
  compileTrace?.("bridge-tracker-lifecycle", {
@@ -628,7 +641,7 @@ export async function captureTransformGeneration(props: {
628
641
  // this generation. A delivered text that differs, because a plugin ordered
629
642
  // before ttsc rewrote the module or the file changed after the host read
630
643
  // it, must not replace them: every sibling delivery compares the disk and
631
- // would recompile the project (samchon/ttsc#1394). It is reported once
644
+ // would recompile the project (samchon/ttsc#1394). An actual delivery is reported once
632
645
  // instead, and the file's own compile is served.
633
646
  const currentFileKey = toProjectKey(
634
647
  projectRoot,
@@ -681,7 +694,14 @@ export async function captureTransformGeneration(props: {
681
694
  props.currentFile,
682
695
  diskHash ?? deliveredHash,
683
696
  );
684
- if (diskHash !== undefined && diskHash !== deliveredHash) {
697
+ // Preparation reads native source only to seed the ordinary capture. A
698
+ // later edit is handled by generation proof, not a warning about a host
699
+ // rewrite: no host delivered that speculative module (#1764).
700
+ if (
701
+ props.reportDivergence !== false &&
702
+ diskHash !== undefined &&
703
+ diskHash !== deliveredHash
704
+ ) {
685
705
  reportDivergentDelivery(cached, props.currentFile);
686
706
  }
687
707
  // An adopted compile was certified by its publisher, and is matched below
@@ -1,5 +1,6 @@
1
1
  import type { ITtscCompilerTransformation } from "ttsc";
2
2
 
3
+ import { generationCaseSensitivity } from "../envelope/generationCaseSensitivity";
3
4
  import { selectListedFiles } from "../envelope/selectListedFiles";
4
5
  import type { TtscTransformFilesystemOperations } from "../filesystem/TtscTransformFilesystemOperations";
5
6
  import { createHostPathIdentityContext } from "../filesystem/createHostPathIdentityContext";
@@ -43,7 +44,10 @@ export function selectPersistentHostInputs(props: {
43
44
  props.temporaryTsconfig === undefined
44
45
  )
45
46
  return inputs;
46
- const identities = createHostPathIdentityContext(props.filesystem);
47
+ const identities = createHostPathIdentityContext(
48
+ props.filesystem,
49
+ generationCaseSensitivity(props),
50
+ );
47
51
  const temporary =
48
52
  props.temporaryTsconfig === undefined
49
53
  ? undefined
@@ -97,6 +97,9 @@ export async function transformProject(props: {
97
97
  /** Delivered module text compared against the compiler's disk observation. */
98
98
  currentSource: string;
99
99
 
100
+ /** False for preparation, whose source read is not a host module delivery. */
101
+ reportDivergence?: boolean;
102
+
100
103
  /**
101
104
  * Delivery pass this compile was started for; see
102
105
  * {@link TtscCachedProjectTransform.deliveryEpoch}.
@@ -69,9 +69,7 @@ export function nativeInputPredicateState(
69
69
  } else if (kind === "optional-file") {
70
70
  try {
71
71
  digest = filesystem.stat(file).isFile()
72
- ? hash(
73
- Buffer.concat([Buffer.from("file\0"), readBytes()]),
74
- )
72
+ ? hash(Buffer.concat([Buffer.from("file\0"), readBytes()]))
75
73
  : hash("missing\0");
76
74
  } catch (error) {
77
75
  if (!missing(error)) return undefined;
@@ -114,12 +114,21 @@ export async function createHostInputMutationTracker(
114
114
  * filesystem.
115
115
  */
116
116
  scopes?: ReadonlyMap<string, TtscTrackedInputScope>,
117
+ /**
118
+ * The case answers of the generation this tracker watches, for its baseline
119
+ * (`generationCaseSensitivity`); omitted, the baseline observes for itself.
120
+ * An event's re-check always observes afresh.
121
+ */
122
+ caseSensitive?: TtscTransformFilesystemOperations["caseSensitive"],
117
123
  ): Promise<TtscProjectMutationTracker> {
118
124
  const paths =
119
125
  (filesystem.platform ?? process.platform) === "win32"
120
126
  ? path.win32
121
127
  : path.posix;
122
- const identities = createHostPathIdentityContext(filesystem);
128
+ const identities = createHostPathIdentityContext(
129
+ filesystem,
130
+ caseSensitive ?? filesystem.caseSensitive,
131
+ );
123
132
  // The project root in both of its spellings: an input the compiler reported
124
133
  // physically is the project's as much as one spelled as the project was
125
134
  // named, and the root's observer is opened once, under the name.
@@ -38,10 +38,12 @@ import { watchLocationIdentity } from "./watchLocationIdentity";
38
38
  * directory excluded in both lexical and native spellings. Unknown, missing and
39
39
  * linked ancestry remains conservative. Membership rejection withdraws
40
40
  * notification authority except below such a proven excluded directory, so a
41
- * recursive backend's writes to a cache or output tree cost no proof. Named
42
- * events also compare current native identity and case policy with the retained
43
- * identity transaction. Retargeting withdraws notification authority without
44
- * changing the meaning of earlier recorded event spellings.
41
+ * recursive backend's writes to a cache or output tree cost no proof, nor does
42
+ * an edit to an existing unlinked file whose every name the program cannot
43
+ * admit (samchon/ttsc#1737). Named events also compare current native identity
44
+ * and case policy with the retained identity transaction. Retargeting withdraws
45
+ * notification authority without changing the meaning of earlier recorded event
46
+ * spellings.
45
47
  *
46
48
  * @evidence contracts/common.md#principled-implementation
47
49
  * The project walk and membership policy own structural relevance; content
@@ -193,6 +195,26 @@ export async function createProjectMutationTracker(
193
195
  }
194
196
  return excluded;
195
197
  };
198
+ // Whether an event names an existing file no spelling can make a program
199
+ // member: a regular file with no second hard link, whose own and physical
200
+ // names are both names the program cannot admit. A directory alias can move
201
+ // a file into the program but cannot rename it, and a removed name, a link
202
+ // or a directory stays conservative.
203
+ const existingNonProgramFile = (changed: string): boolean => {
204
+ try {
205
+ const stats = filesystem.lstat(changed);
206
+ if (!stats.isFile() || stats.nlink > 1n) return false;
207
+ return (
208
+ !isPossibleProgramFileName(paths.basename(changed), policy) &&
209
+ !isPossibleProgramFileName(
210
+ paths.basename(filesystem.realpath(changed)),
211
+ policy,
212
+ )
213
+ );
214
+ } catch {
215
+ return false;
216
+ }
217
+ };
196
218
  const reportsMembership = (location: string, filename: string): boolean => {
197
219
  const changed = paths.join(location, filename);
198
220
  const current = createHostPathIdentityContext(filesystem);
@@ -221,8 +243,14 @@ export async function createProjectMutationTracker(
221
243
  // except below a directory the walk provably excludes. A recursive backend
222
244
  // (macOS, Windows) hears every write under the root, a cache or an output
223
245
  // directory among them, and withdrawing authority for each sent every later
224
- // delivery to the whole-snapshot proof.
225
- if (!membership && !excludedFromWalk(changed, true))
246
+ // delivery to the whole-snapshot proof. An existing file whose every name
247
+ // the program cannot admit is no member either, a stylesheet or an asset
248
+ // edited in a source directory among them, on every backend.
249
+ if (
250
+ !membership &&
251
+ !excludedFromWalk(changed, true) &&
252
+ !existingNonProgramFile(changed)
253
+ )
226
254
  tracker.unverified = true;
227
255
  return membership;
228
256
  };