@simplysm/sd-cli 14.0.41 → 14.0.43

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 (321) hide show
  1. package/dist/angular/angular-compiler.d.ts +0 -35
  2. package/dist/angular/angular-compiler.d.ts.map +1 -1
  3. package/dist/angular/angular-compiler.js +0 -374
  4. package/dist/angular/angular-compiler.js.map +1 -1
  5. package/dist/angular/hmr-candidates.d.ts +13 -0
  6. package/dist/angular/hmr-candidates.d.ts.map +1 -0
  7. package/dist/angular/hmr-candidates.js +230 -0
  8. package/dist/angular/hmr-candidates.js.map +1 -0
  9. package/dist/angular/ngtsc-build-core.d.ts +29 -34
  10. package/dist/angular/ngtsc-build-core.d.ts.map +1 -1
  11. package/dist/angular/ngtsc-build-core.js +90 -51
  12. package/dist/angular/ngtsc-build-core.js.map +1 -1
  13. package/dist/angular/vite-angular-plugin.d.ts +1 -1
  14. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  15. package/dist/angular/vite-angular-plugin.js +63 -56
  16. package/dist/angular/vite-angular-plugin.js.map +1 -1
  17. package/dist/angular/web-worker-transformer.d.ts +9 -0
  18. package/dist/angular/web-worker-transformer.d.ts.map +1 -0
  19. package/dist/angular/web-worker-transformer.js +73 -0
  20. package/dist/angular/web-worker-transformer.js.map +1 -0
  21. package/dist/capacitor/capacitor.d.ts.map +1 -1
  22. package/dist/capacitor/capacitor.js +6 -4
  23. package/dist/capacitor/capacitor.js.map +1 -1
  24. package/dist/commands/check.d.ts +1 -1
  25. package/dist/commands/check.d.ts.map +1 -1
  26. package/dist/commands/check.js +15 -65
  27. package/dist/commands/check.js.map +1 -1
  28. package/dist/commands/publish/deployment-phase.d.ts.map +1 -1
  29. package/dist/commands/publish/deployment-phase.js +13 -5
  30. package/dist/commands/publish/deployment-phase.js.map +1 -1
  31. package/dist/commands/publish/npm-publisher.js +1 -1
  32. package/dist/commands/publish/npm-publisher.js.map +1 -1
  33. package/dist/commands/publish/publish-command.js +1 -1
  34. package/dist/commands/publish/publish-command.js.map +1 -1
  35. package/dist/commands/publish/version-upgrade.js +1 -1
  36. package/dist/commands/publish/version-upgrade.js.map +1 -1
  37. package/dist/commands/replace-deps.d.ts.map +1 -1
  38. package/dist/commands/replace-deps.js +2 -1
  39. package/dist/commands/replace-deps.js.map +1 -1
  40. package/dist/deps/replace-deps/collect-deps.d.ts.map +1 -1
  41. package/dist/deps/replace-deps/collect-deps.js +5 -2
  42. package/dist/deps/replace-deps/collect-deps.js.map +1 -1
  43. package/dist/deps/replace-deps/replace-deps.d.ts +21 -3
  44. package/dist/deps/replace-deps/replace-deps.d.ts.map +1 -1
  45. package/dist/deps/replace-deps/replace-deps.js +107 -62
  46. package/dist/deps/replace-deps/replace-deps.js.map +1 -1
  47. package/dist/electron/electron.js +7 -7
  48. package/dist/electron/electron.js.map +1 -1
  49. package/dist/engines/BaseEngine.d.ts.map +1 -1
  50. package/dist/engines/BaseEngine.js +2 -5
  51. package/dist/engines/BaseEngine.js.map +1 -1
  52. package/dist/engines/EsbuildClientEngine.d.ts.map +1 -1
  53. package/dist/engines/EsbuildClientEngine.js +16 -9
  54. package/dist/engines/EsbuildClientEngine.js.map +1 -1
  55. package/dist/engines/NgtscEngine.d.ts +4 -4
  56. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  57. package/dist/engines/NgtscEngine.js +5 -5
  58. package/dist/engines/NgtscEngine.js.map +1 -1
  59. package/dist/engines/TscEngine.d.ts.map +1 -1
  60. package/dist/engines/TscEngine.js +0 -2
  61. package/dist/engines/TscEngine.js.map +1 -1
  62. package/dist/engines/types.d.ts +2 -0
  63. package/dist/engines/types.d.ts.map +1 -1
  64. package/dist/esbuild/esbuild-angular-compiler-plugin.d.ts +36 -0
  65. package/dist/esbuild/esbuild-angular-compiler-plugin.d.ts.map +1 -0
  66. package/dist/esbuild/esbuild-angular-compiler-plugin.js +464 -0
  67. package/dist/esbuild/esbuild-angular-compiler-plugin.js.map +1 -0
  68. package/dist/esbuild/esbuild-client-config.d.ts +8 -2
  69. package/dist/esbuild/esbuild-client-config.d.ts.map +1 -1
  70. package/dist/esbuild/esbuild-client-config.js +48 -33
  71. package/dist/esbuild/esbuild-client-config.js.map +1 -1
  72. package/dist/esbuild/esbuild-tsc-plugin.d.ts +4 -1
  73. package/dist/esbuild/esbuild-tsc-plugin.d.ts.map +1 -1
  74. package/dist/esbuild/esbuild-tsc-plugin.js +27 -23
  75. package/dist/esbuild/esbuild-tsc-plugin.js.map +1 -1
  76. package/dist/esbuild/file-reference-tracker.d.ts +24 -0
  77. package/dist/esbuild/file-reference-tracker.d.ts.map +1 -0
  78. package/dist/esbuild/file-reference-tracker.js +57 -0
  79. package/dist/esbuild/file-reference-tracker.js.map +1 -0
  80. package/dist/esbuild/lmdb-cache-store.d.ts +18 -0
  81. package/dist/esbuild/lmdb-cache-store.d.ts.map +1 -0
  82. package/dist/esbuild/lmdb-cache-store.js +41 -0
  83. package/dist/esbuild/lmdb-cache-store.js.map +1 -0
  84. package/dist/esbuild/load-result-cache.d.ts +17 -0
  85. package/dist/esbuild/load-result-cache.d.ts.map +1 -0
  86. package/dist/esbuild/load-result-cache.js +61 -0
  87. package/dist/esbuild/load-result-cache.js.map +1 -0
  88. package/dist/index.d.ts +3 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +2 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/lint/lint-core.js +7 -7
  93. package/dist/lint/lint-core.js.map +1 -1
  94. package/dist/orchestrators/BaseOrchestrator.js +2 -2
  95. package/dist/orchestrators/BaseOrchestrator.js.map +1 -1
  96. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  97. package/dist/orchestrators/BuildOrchestrator.js +5 -19
  98. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  99. package/dist/orchestrators/DevOrchestrator.js +5 -5
  100. package/dist/orchestrators/DevOrchestrator.js.map +1 -1
  101. package/dist/orchestrators/WatchOrchestrator.js +6 -6
  102. package/dist/orchestrators/WatchOrchestrator.js.map +1 -1
  103. package/dist/runtime/ResultCollector.d.ts +1 -0
  104. package/dist/runtime/ResultCollector.d.ts.map +1 -1
  105. package/dist/runtime/ResultCollector.js.map +1 -1
  106. package/dist/runtime/engine-watch-events.d.ts.map +1 -1
  107. package/dist/runtime/engine-watch-events.js +3 -0
  108. package/dist/runtime/engine-watch-events.js.map +1 -1
  109. package/dist/runtime/rebuild-manager.js +1 -1
  110. package/dist/runtime/rebuild-manager.js.map +1 -1
  111. package/dist/runtime/worker-utils.js +1 -1
  112. package/dist/runtime/worker-utils.js.map +1 -1
  113. package/dist/sd-cli-entry.d.ts.map +1 -1
  114. package/dist/sd-cli-entry.js +4 -3
  115. package/dist/sd-cli-entry.js.map +1 -1
  116. package/dist/sd-cli.js +3 -3
  117. package/dist/sd-cli.js.map +1 -1
  118. package/dist/ts-compiler/SdTsCompiler.d.ts +39 -0
  119. package/dist/ts-compiler/SdTsCompiler.d.ts.map +1 -0
  120. package/dist/ts-compiler/SdTsCompiler.js +593 -0
  121. package/dist/ts-compiler/SdTsCompiler.js.map +1 -0
  122. package/dist/ts-compiler/sd-ts-compiler-options.d.ts +40 -0
  123. package/dist/ts-compiler/sd-ts-compiler-options.d.ts.map +1 -0
  124. package/dist/ts-compiler/sd-ts-compiler-options.js +2 -0
  125. package/dist/ts-compiler/sd-ts-compiler-options.js.map +1 -0
  126. package/dist/ts-compiler/sd-ts-compiler-result.d.ts +34 -0
  127. package/dist/ts-compiler/sd-ts-compiler-result.d.ts.map +1 -0
  128. package/dist/ts-compiler/sd-ts-compiler-result.js +2 -0
  129. package/dist/ts-compiler/sd-ts-compiler-result.js.map +1 -0
  130. package/dist/utils/copy-public.d.ts +6 -4
  131. package/dist/utils/copy-public.d.ts.map +1 -1
  132. package/dist/utils/copy-public.js +9 -7
  133. package/dist/utils/copy-public.js.map +1 -1
  134. package/dist/utils/diagnostic-utils.d.ts +2 -3
  135. package/dist/utils/diagnostic-utils.d.ts.map +1 -1
  136. package/dist/utils/diagnostic-utils.js +8 -9
  137. package/dist/utils/diagnostic-utils.js.map +1 -1
  138. package/dist/utils/output-utils.d.ts +8 -2
  139. package/dist/utils/output-utils.d.ts.map +1 -1
  140. package/dist/utils/output-utils.js +32 -8
  141. package/dist/utils/output-utils.js.map +1 -1
  142. package/dist/workers/client.worker.d.ts +1 -1
  143. package/dist/workers/client.worker.d.ts.map +1 -1
  144. package/dist/workers/client.worker.js +136 -110
  145. package/dist/workers/client.worker.js.map +1 -1
  146. package/dist/workers/library-build.worker.d.ts +0 -2
  147. package/dist/workers/library-build.worker.d.ts.map +1 -1
  148. package/dist/workers/library-build.worker.js +147 -70
  149. package/dist/workers/library-build.worker.js.map +1 -1
  150. package/dist/workers/server-build.worker.d.ts.map +1 -1
  151. package/dist/workers/server-build.worker.js +30 -57
  152. package/dist/workers/server-build.worker.js.map +1 -1
  153. package/dist/workers/server-esbuild-context.d.ts +7 -0
  154. package/dist/workers/server-esbuild-context.d.ts.map +1 -1
  155. package/dist/workers/server-esbuild-context.js +11 -2
  156. package/dist/workers/server-esbuild-context.js.map +1 -1
  157. package/package.json +7 -6
  158. package/src/angular/angular-compiler.ts +0 -502
  159. package/src/angular/hmr-candidates.ts +295 -0
  160. package/src/angular/ngtsc-build-core.ts +125 -92
  161. package/src/angular/vite-angular-plugin.ts +71 -65
  162. package/src/angular/web-worker-transformer.ts +117 -0
  163. package/src/capacitor/capacitor.ts +6 -4
  164. package/src/commands/check.ts +17 -76
  165. package/src/commands/publish/deployment-phase.ts +11 -7
  166. package/src/commands/publish/npm-publisher.ts +1 -1
  167. package/src/commands/publish/publish-command.ts +1 -1
  168. package/src/commands/publish/version-upgrade.ts +1 -1
  169. package/src/commands/replace-deps.ts +3 -1
  170. package/src/deps/replace-deps/collect-deps.ts +4 -2
  171. package/src/deps/replace-deps/replace-deps.ts +114 -66
  172. package/src/electron/electron.ts +7 -7
  173. package/src/engines/BaseEngine.ts +2 -6
  174. package/src/engines/EsbuildClientEngine.ts +16 -10
  175. package/src/engines/NgtscEngine.ts +7 -7
  176. package/src/engines/TscEngine.ts +0 -2
  177. package/src/engines/types.ts +2 -0
  178. package/src/esbuild/esbuild-angular-compiler-plugin.ts +647 -0
  179. package/src/esbuild/esbuild-client-config.ts +57 -41
  180. package/src/esbuild/esbuild-tsc-plugin.ts +33 -23
  181. package/src/esbuild/file-reference-tracker.ts +61 -0
  182. package/src/esbuild/lmdb-cache-store.ts +46 -0
  183. package/src/esbuild/load-result-cache.ts +85 -0
  184. package/src/index.ts +5 -0
  185. package/src/lint/lint-core.ts +7 -7
  186. package/src/orchestrators/BaseOrchestrator.ts +2 -2
  187. package/src/orchestrators/BuildOrchestrator.ts +5 -24
  188. package/src/orchestrators/DevOrchestrator.ts +5 -5
  189. package/src/orchestrators/WatchOrchestrator.ts +6 -6
  190. package/src/runtime/ResultCollector.ts +1 -0
  191. package/src/runtime/engine-watch-events.ts +3 -0
  192. package/src/runtime/rebuild-manager.ts +1 -1
  193. package/src/runtime/worker-utils.ts +1 -1
  194. package/src/sd-cli-entry.ts +5 -3
  195. package/src/sd-cli.ts +4 -4
  196. package/src/ts-compiler/SdTsCompiler.ts +815 -0
  197. package/src/ts-compiler/sd-ts-compiler-options.ts +46 -0
  198. package/src/ts-compiler/sd-ts-compiler-result.ts +34 -0
  199. package/src/utils/copy-public.ts +9 -6
  200. package/src/utils/diagnostic-utils.ts +8 -9
  201. package/src/utils/output-utils.ts +38 -8
  202. package/src/workers/client.worker.ts +160 -126
  203. package/src/workers/library-build.worker.ts +187 -75
  204. package/src/workers/server-build.worker.ts +31 -61
  205. package/src/workers/server-esbuild-context.ts +14 -2
  206. package/tests/angular/fixtures/packages/basic-app/dist/styles.css +3 -0
  207. package/tests/angular/fixtures/packages/basic-app/scss/styles.scss +5 -0
  208. package/tests/angular/vite-angular-plugin-sdtscompiler.verify.md +13 -0
  209. package/tests/angular/web-worker-transformer.spec.ts +154 -0
  210. package/tests/capacitor/capacitor-build.spec.ts +1 -1
  211. package/tests/capacitor/capacitor-icon.spec.ts +1 -1
  212. package/tests/capacitor/capacitor-init.spec.ts +1 -1
  213. package/tests/commands/check.spec.ts +90 -104
  214. package/tests/commands/publish.spec.ts +12 -4
  215. package/tests/commands/slice3-severity-cleanup.verify.md +12 -0
  216. package/tests/deps/replace-deps/collect-deps.acc.spec.ts +62 -0
  217. package/tests/deps/replace-deps/collect-deps.spec.ts +49 -0
  218. package/tests/deps/replace-deps/replace-deps-filter.spec.ts +103 -0
  219. package/tests/deps/replace-deps/replace-deps-setup.acc.spec.ts +156 -0
  220. package/tests/electron/electron.spec.ts +4 -1
  221. package/tests/engines/engine-adapter-isolation.spec.ts +5 -6
  222. package/tests/engines/engine-duplicate-output-removal.verify.md +10 -0
  223. package/tests/engines/esbuild-client-engine.acc.spec.ts +79 -0
  224. package/tests/engines/esbuild-client-engine.spec.ts +73 -3
  225. package/tests/esbuild/esbuild-angular-compiler-plugin-hmr.verify.md +23 -0
  226. package/tests/esbuild/esbuild-angular-compiler-plugin-onload.verify.md +21 -0
  227. package/tests/esbuild/esbuild-angular-compiler-plugin-onstart-extraction.verify.md +16 -0
  228. package/tests/esbuild/esbuild-angular-compiler-plugin-sdtscompiler.verify.md +15 -0
  229. package/tests/esbuild/esbuild-angular-compiler-plugin-stylesheet.verify.md +31 -0
  230. package/tests/esbuild/esbuild-angular-compiler-plugin-worker.verify.md +31 -0
  231. package/tests/esbuild/esbuild-angular-compiler-plugin.spec.ts +397 -0
  232. package/tests/esbuild/esbuild-angular-compiler-plugin.verify.md +21 -0
  233. package/tests/esbuild/esbuild-tsc-plugin-imports.verify.md +13 -0
  234. package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +56 -111
  235. package/tests/esbuild/esbuild-tsc-plugin.spec.ts +116 -52
  236. package/tests/esbuild/file-reference-tracker.spec.ts +99 -0
  237. package/tests/esbuild/lmdb-cache-store.spec.ts +58 -0
  238. package/tests/esbuild/load-result-cache.acc.spec.ts +55 -0
  239. package/tests/esbuild/load-result-cache.spec.ts +133 -0
  240. package/tests/orchestrators/build-orchestrator.spec.ts +4 -3
  241. package/tests/orchestrators/dev-orchestrator.spec.ts +5 -5
  242. package/tests/orchestrators/slice1-stdout-to-consola.verify.md +10 -0
  243. package/tests/orchestrators/typecheck-orchestrator.spec.ts +1 -1
  244. package/tests/orchestrators/watch-orchestrator.spec.ts +7 -7
  245. package/tests/runtime/result-collector.spec.ts +64 -0
  246. package/tests/sd-cli-entry.spec.ts +3 -4
  247. package/tests/sd-cli-log-tag.verify.md +11 -0
  248. package/tests/ts-compiler/SdTsCompiler-affected-files.verify.md +8 -0
  249. package/tests/ts-compiler/SdTsCompiler-diagnostics.verify.md +12 -0
  250. package/tests/ts-compiler/SdTsCompiler-emit.verify.md +9 -0
  251. package/tests/ts-compiler/SdTsCompiler.acc.spec.ts +603 -0
  252. package/tests/ts-compiler/SdTsCompiler.spec.ts +265 -0
  253. package/tests/ts-compiler/SdTsCompiler.verify.md +41 -0
  254. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck-browser.tsbuildinfo +1 -0
  255. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck-node.tsbuildinfo +1 -0
  256. package/tests/ts-compiler/fixtures/non-angular-pkg/.cache/typecheck.tsbuildinfo +1 -0
  257. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/index.d.ts +2 -0
  258. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/index.d.ts.map +1 -0
  259. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/index.js +4 -0
  260. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/index.js.map +1 -0
  261. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/util.d.ts +2 -0
  262. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/util.d.ts.map +1 -0
  263. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/util.js +4 -0
  264. package/tests/ts-compiler/fixtures/non-angular-pkg/dist/util.js.map +1 -0
  265. package/tests/ts-compiler/fixtures/non-angular-pkg/src/index.ts +3 -0
  266. package/tests/ts-compiler/fixtures/non-angular-pkg/src/util.ts +3 -0
  267. package/tests/ts-compiler/fixtures/non-angular-pkg/tests/sample.test-file.ts +3 -0
  268. package/tests/ts-compiler/fixtures/non-angular-pkg/tsconfig.json +12 -0
  269. package/tests/ts-compiler/scss-lint-integration.verify.md +14 -0
  270. package/tests/utils/angular-build.spec.ts +1 -1
  271. package/tests/utils/copy-public-outdir.verify.md +8 -0
  272. package/tests/utils/copy-public.acc.spec.ts +52 -0
  273. package/tests/utils/copy-public.spec.ts +56 -0
  274. package/tests/utils/diagnostic-utils.spec.ts +24 -15
  275. package/tests/utils/engine-watch-events.acc.spec.ts +59 -0
  276. package/tests/utils/engine-watch-events.spec.ts +58 -0
  277. package/tests/utils/esbuild-client-config-integration.verify.md +9 -0
  278. package/tests/utils/esbuild-client-config.acc.spec.ts +45 -61
  279. package/tests/utils/esbuild-client-config.spec.ts +70 -52
  280. package/tests/utils/ngtsc-build-core-write-emit.spec.ts +12 -12
  281. package/tests/utils/ngtsc-build-core.spec.ts +1 -44
  282. package/tests/utils/output-utils.spec.ts +133 -13
  283. package/tests/utils/replace-deps-watch.acc.spec.ts +7 -1
  284. package/tests/utils/replace-deps-watch.spec.ts +57 -1
  285. package/tests/utils/worker-utils.spec.ts +8 -2
  286. package/tests/workers/client-worker-initial-build-error.verify.md +2 -3
  287. package/tests/workers/client-worker-initial-build-warnings.verify.md +7 -0
  288. package/tests/workers/client-worker-refactor.verify.md +22 -0
  289. package/tests/workers/client-worker-ts-cache-invalidation.verify.md +12 -0
  290. package/tests/workers/client-worker.acc.spec.ts +6 -3
  291. package/tests/workers/library-build-lint.spec.ts +40 -45
  292. package/tests/workers/library-build-worker.spec.ts +294 -40
  293. package/tests/workers/server-build-lint.spec.ts +59 -45
  294. package/tests/workers/server-build-worker.spec.ts +63 -24
  295. package/tests/workers/server-esbuild-context.acc.spec.ts +2 -0
  296. package/tests/workers/server-esbuild-context.spec.ts +2 -0
  297. package/tests/workers/server-runtime-worker.spec.ts +1 -1
  298. package/tests/workers/shared-worker-lifecycle.acc.spec.ts +1 -1
  299. package/dist/angular/angular-build-pipeline.d.ts +0 -97
  300. package/dist/angular/angular-build-pipeline.d.ts.map +0 -1
  301. package/dist/angular/angular-build-pipeline.js +0 -285
  302. package/dist/angular/angular-build-pipeline.js.map +0 -1
  303. package/dist/utils/tsc-build.d.ts +0 -51
  304. package/dist/utils/tsc-build.d.ts.map +0 -1
  305. package/dist/utils/tsc-build.js +0 -156
  306. package/dist/utils/tsc-build.js.map +0 -1
  307. package/dist/workers/ngtsc-build.worker.d.ts +0 -23
  308. package/dist/workers/ngtsc-build.worker.d.ts.map +0 -1
  309. package/dist/workers/ngtsc-build.worker.js +0 -267
  310. package/dist/workers/ngtsc-build.worker.js.map +0 -1
  311. package/src/angular/angular-build-pipeline.ts +0 -406
  312. package/src/utils/tsc-build.ts +0 -226
  313. package/src/workers/ngtsc-build.worker.ts +0 -351
  314. package/tests/angular/angular-build-pipeline.spec.ts +0 -247
  315. package/tests/angular/angular-compiler-aot.acc.spec.ts +0 -68
  316. package/tests/angular/angular-compiler-aot.spec.ts +0 -80
  317. package/tests/utils/angular-compiler-emit.spec.ts +0 -666
  318. package/tests/utils/angular-compiler.spec.ts +0 -707
  319. package/tests/utils/tsc-build.spec.ts +0 -527
  320. package/tests/workers/ngtsc-build-lint.spec.ts +0 -141
  321. package/tests/workers/ngtsc-build-worker.spec.ts +0 -199
@@ -0,0 +1,815 @@
1
+ import path from "path";
2
+ import { createHash } from "crypto";
3
+ import ts from "typescript";
4
+ import { consola } from "consola";
5
+ import { pathx } from "@simplysm/core-node";
6
+ import type { ISdTsCompilerOptions, ISdTsCompilerEmitOptions } from "./sd-ts-compiler-options";
7
+ import type { ISdTsCompilerResult } from "./sd-ts-compiler-result";
8
+ import type { EmitResult } from "../angular/angular-compiler";
9
+ import {
10
+ parseTsconfig,
11
+ getPackageSourceFiles,
12
+ getPackageFiles,
13
+ getCompilerOptionsForEnv,
14
+ } from "../utils/tsconfig";
15
+ import { createOutputPathRewriter, addJsExtensionToImports } from "../utils/output-path-rewriter";
16
+ import { serializeDiagnostic, type SerializedDiagnostic } from "../typecheck/typecheck-serialization";
17
+ import { isWorkspaceDiagnostic, formatDiagnosticError } from "../utils/diagnostic-utils";
18
+ import { NgtscProgram, OptimizeFor } from "../angular/angular-build";
19
+ import {
20
+ AngularSourceFileCache,
21
+ augmentHostWithCaching,
22
+ } from "../angular/angular-compiler";
23
+ import {
24
+ createLibraryTransformStylesheet,
25
+ compileGlobalScss,
26
+ compileSideEffectScss as compileSideEffectScssEntries,
27
+ type SideEffectScssEntry,
28
+ } from "../angular/ngtsc-build-core";
29
+ import { LintWithProgramRunner, type LintWithProgramResult } from "../lint/lint-with-program";
30
+
31
+ const logger = consola.withTag("sd:cli:SdTsCompiler");
32
+
33
+ type NgCompiler = NgtscProgram["compiler"];
34
+
35
+ function hasTemplateExtension(file: string): boolean {
36
+ const ext = path.extname(file).toLowerCase();
37
+ return ext === ".htm" || ext === ".html" || ext === ".svg";
38
+ }
39
+
40
+ function ensureSourceFileVersions(program: ts.Program): void {
41
+ const baseGetSourceFiles = program.getSourceFiles;
42
+ program.getSourceFiles = function (...parameters: Parameters<typeof baseGetSourceFiles>) {
43
+ const files = baseGetSourceFiles.apply(this, parameters);
44
+ for (const file of files) {
45
+ if ((file as unknown as { version?: string }).version == null) {
46
+ (file as unknown as { version: string }).version = createHash("sha256")
47
+ .update(file.text)
48
+ .digest("hex");
49
+ }
50
+ }
51
+ return files;
52
+ };
53
+ }
54
+
55
+ export class SdTsCompiler {
56
+ private readonly _options: ISdTsCompilerOptions;
57
+
58
+ // ── 파생 상태 (첫 compileAsync에서 결정) ──
59
+ private _isForAngular?: boolean;
60
+ private _sourceFileCache?: AngularSourceFileCache;
61
+
62
+ // ── 컴파일 상태 (compileAsync마다 갱신) ──
63
+ private _ngtscProgram?: NgtscProgram;
64
+ private _builderProgram?: ts.EmitAndSemanticDiagnosticsBuilderProgram;
65
+
66
+ // ── 영속 상태 (compileAsync 간 재사용) ──
67
+ private _packageJsonCache?: ts.PackageJsonInfoCache;
68
+ private readonly _diagnosticCache = new WeakMap<ts.SourceFile, ts.Diagnostic[]>();
69
+
70
+ // ── affected files (compileAsync에서 갱신, emit/diagnostics에서 사용) ──
71
+ private _affectedSourceFiles = new Set<ts.SourceFile>();
72
+
73
+ // ── SCSS 상태 (compileAsync마다 리셋)
74
+ private readonly _scssErrors: string[] = [];
75
+ private readonly _scssDependencies = new Map<string, Set<string>>();
76
+
77
+ // ── Side-effect SCSS (compileAsync 간 유지) ──
78
+ private readonly _sideEffectScssRegistry = new Map<string, SideEffectScssEntry>();
79
+
80
+ // ── Lint (lazy init, 인스턴스 재사용) ──
81
+ private _lintRunner?: LintWithProgramRunner;
82
+
83
+ constructor(options: ISdTsCompilerOptions) {
84
+ this._options = options;
85
+ }
86
+
87
+ private _getScssLoadPaths(): string[] {
88
+ const { pkgDir, cwd } = this._options;
89
+ return [path.join(pkgDir, "scss"), path.join(cwd, "node_modules")];
90
+ }
91
+
92
+ /** Side-effect SCSS 레지스트리 참조 (emit 코드에서 항목 등록용) */
93
+ get sideEffectScssRegistry(): Map<string, SideEffectScssEntry> {
94
+ return this._sideEffectScssRegistry;
95
+ }
96
+
97
+ /** Side-effect SCSS 레지스트리의 모든 항목을 CSS로 컴파일 */
98
+ compileSideEffectScss(): void {
99
+ const loadPaths = this._getScssLoadPaths();
100
+ compileSideEffectScssEntries(
101
+ this._sideEffectScssRegistry,
102
+ loadPaths,
103
+ this._scssErrors,
104
+ this._scssDependencies,
105
+ );
106
+ }
107
+
108
+ /** SCSS 의존성 역방향 탐색: scssPath에 의존하는 파일 목록 반환 */
109
+ findAffectedByScss(scssPath: string): string[] {
110
+ const normalizedPath = pathx.posix(scssPath);
111
+ const affected: string[] = [];
112
+ for (const [ownerFile, deps] of this._scssDependencies) {
113
+ if (deps.has(normalizedPath)) {
114
+ affected.push(ownerFile);
115
+ }
116
+ }
117
+ return affected;
118
+ }
119
+
120
+ async compileAsync(
121
+ modifiedFiles?: ReadonlySet<string>,
122
+ emitOptions?: ISdTsCompilerEmitOptions,
123
+ ): Promise<ISdTsCompilerResult> {
124
+ const { pkgDir } = this._options;
125
+ const pkgName = path.basename(pkgDir);
126
+
127
+ // 1. 증분: sourceFileCache 무효화 + packageJsonCache 클리어
128
+ if (modifiedFiles != null && modifiedFiles.size > 0) {
129
+ if (this._sourceFileCache != null) {
130
+ this._sourceFileCache.invalidate(modifiedFiles);
131
+ }
132
+
133
+ // node_modules 변경 시 packageJsonCache 클리어 (stale 모듈 해석 방지)
134
+ if (this._packageJsonCache != null) {
135
+ for (const file of modifiedFiles) {
136
+ if (file.includes("node_modules")) {
137
+ this._packageJsonCache.clear();
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ // 2. tsconfig 파싱 (매 호출)
145
+ const parsed = parseTsconfig(pkgDir);
146
+ const isForAngular = parsed.raw?.angularCompilerOptions != null;
147
+ this._isForAngular = isForAngular;
148
+ const angularCompilerOptions = isForAngular
149
+ ? (parsed.raw.angularCompilerOptions as Record<string, unknown>)
150
+ : undefined;
151
+
152
+ logger.debug(`[${pkgName}] isForAngular: ${isForAngular}`);
153
+
154
+ // 3. rootNames 필터링
155
+ const rootNames = this._filterRootNames(parsed);
156
+
157
+ logger.debug(`[${pkgName}] rootNames: ${rootNames.length}개`);
158
+
159
+ // 4. compilerOptions 구성
160
+ const compilerOptions = this._buildCompilerOptions(
161
+ parsed.options,
162
+ isForAngular,
163
+ angularCompilerOptions,
164
+ );
165
+
166
+ // 5. SCSS 상태 리셋
167
+ this._scssErrors.length = 0;
168
+ this._scssDependencies.clear();
169
+
170
+ // 6. transformStylesheet 결정: isForAngular + 미제공 시 라이브러리 콜백 자동 생성
171
+ let effectiveTransformStylesheet = this._options.transformStylesheet;
172
+ if (isForAngular && effectiveTransformStylesheet == null) {
173
+ const loadPaths = this._getScssLoadPaths();
174
+ effectiveTransformStylesheet = createLibraryTransformStylesheet(
175
+ loadPaths,
176
+ this._scssErrors,
177
+ this._scssDependencies,
178
+ );
179
+ }
180
+
181
+ // 7. compiler host 생성
182
+ const host = this._createHost(compilerOptions, isForAngular, effectiveTransformStylesheet);
183
+
184
+ // 8. program 생성
185
+ let program: ts.Program;
186
+ let builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram;
187
+
188
+ if (isForAngular) {
189
+ // Angular: sourceFileCache 확보
190
+ if (this._sourceFileCache == null) {
191
+ this._sourceFileCache = this._options.sourceFileCache ?? new AngularSourceFileCache();
192
+ }
193
+ augmentHostWithCaching(host, this._sourceFileCache);
194
+
195
+ // NgtscProgram 생성
196
+ logger.debug(`[${pkgName}] NgtscProgram 생성 중...`);
197
+ const angularProgram = new NgtscProgram(
198
+ rootNames,
199
+ compilerOptions,
200
+ host,
201
+ this._ngtscProgram,
202
+ );
203
+
204
+ const tsProgram = angularProgram.getTsProgram();
205
+ ensureSourceFileVersions(tsProgram);
206
+
207
+ builderProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
208
+ tsProgram,
209
+ host,
210
+ this._builderProgram,
211
+ );
212
+ program = tsProgram;
213
+
214
+ // 7. Angular analyzeAsync
215
+ logger.debug(`[${pkgName}] AOT analyzeAsync 시작`);
216
+ await angularProgram.compiler.analyzeAsync();
217
+ logger.debug(`[${pkgName}] AOT analyzeAsync 완료`);
218
+
219
+ this._ngtscProgram = angularProgram;
220
+ } else {
221
+ // Non-Angular: BuilderProgram 직접 생성
222
+ builderProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram(
223
+ rootNames,
224
+ compilerOptions,
225
+ host,
226
+ this._builderProgram,
227
+ );
228
+ program = builderProgram.getProgram();
229
+ }
230
+
231
+ // 8. affected files 추적
232
+ let affectedFiles: ReadonlySet<string> | undefined;
233
+ if (isForAngular) {
234
+ const result = this._findAffectedFilesForAngular(
235
+ builderProgram,
236
+ this._ngtscProgram!.compiler,
237
+ this._sourceFileCache,
238
+ );
239
+ this._affectedSourceFiles = result.affectedSourceFiles;
240
+ affectedFiles = result.affectedPaths;
241
+ } else {
242
+ affectedFiles = this._findAffectedFilesForTsc(builderProgram);
243
+ }
244
+
245
+ logger.debug(`[${pkgName}] affected files: ${affectedFiles != null ? `${affectedFiles.size}개` : "전체 (global change)"}`);
246
+
247
+ // 9. emit 처리
248
+ let emitResults: EmitResult[] | undefined;
249
+ if (isForAngular) {
250
+ emitResults = this._emitAngular(
251
+ this._ngtscProgram!,
252
+ builderProgram,
253
+ this._affectedSourceFiles,
254
+ emitOptions,
255
+ );
256
+ } else {
257
+ this._emitTsc(builderProgram);
258
+ }
259
+
260
+ // 10. 진단 수집
261
+ let rawDiagnostics: ts.Diagnostic[];
262
+ if (isForAngular) {
263
+ rawDiagnostics = this._collectDiagnosticsForAngular(
264
+ this._ngtscProgram!,
265
+ builderProgram,
266
+ this._affectedSourceFiles,
267
+ );
268
+ } else {
269
+ rawDiagnostics = this._collectDiagnosticsForTsc(builderProgram);
270
+ }
271
+ const diagResult = this._finalizeDiagnostics(rawDiagnostics);
272
+
273
+ // 11. 글로벌 SCSS + lint 병렬 실행
274
+ const [, lintResult] = await Promise.all([
275
+ // globalScss
276
+ this._options.globalScss === true
277
+ ? Promise.resolve().then(() => {
278
+ const loadPaths = this._getScssLoadPaths();
279
+ const globalScssErrors = compileGlobalScss(pkgDir, loadPaths);
280
+ this._scssErrors.push(...globalScssErrors);
281
+ })
282
+ : Promise.resolve(),
283
+ // lint
284
+ this._options.lint === true
285
+ ? this._runLint(program, affectedFiles)
286
+ : Promise.resolve(undefined),
287
+ ]);
288
+
289
+ // 12. 상태 저장
290
+ this._builderProgram = builderProgram;
291
+
292
+ logger.debug(`[${pkgName}] compileAsync 완료`);
293
+
294
+ return {
295
+ program,
296
+ builderProgram,
297
+ isForAngular,
298
+ affectedFiles,
299
+ diagnostics: diagResult.diagnostics,
300
+ errorCount: diagResult.errorCount,
301
+ warningCount: diagResult.warningCount,
302
+ errors: diagResult.errors,
303
+ ngtscProgram: this._ngtscProgram,
304
+ emitResults,
305
+ lint: lintResult,
306
+ scssErrors: [...this._scssErrors],
307
+ scssDependencies: new Map(this._scssDependencies),
308
+ };
309
+ }
310
+
311
+ private async _runLint(
312
+ program: ts.Program,
313
+ affectedFiles?: ReadonlySet<string>,
314
+ ): Promise<LintWithProgramResult> {
315
+ const { cwd, pkgDir } = this._options;
316
+ const pkgName = path.basename(pkgDir);
317
+
318
+ if (this._lintRunner == null) {
319
+ this._lintRunner = new LintWithProgramRunner({
320
+ cwd,
321
+ pkgName,
322
+ });
323
+ }
324
+
325
+ logger.debug(`[${pkgName}] lint 시작`);
326
+ const result = await this._lintRunner.lint({
327
+ program,
328
+ affectedFiles,
329
+ });
330
+ logger.debug(`[${pkgName}] lint 완료 (에러: ${result.errorCount}, 경고: ${result.warningCount})`);
331
+
332
+ return result;
333
+ }
334
+
335
+ private _filterRootNames(parsed: ts.ParsedCommandLine): string[] {
336
+ const { pkgDir, includeTests } = this._options;
337
+
338
+ if (includeTests === true) {
339
+ return getPackageFiles(pkgDir, parsed);
340
+ }
341
+ return getPackageSourceFiles(pkgDir, parsed);
342
+ }
343
+
344
+ private _buildCompilerOptions(
345
+ baseOptions: ts.CompilerOptions,
346
+ isForAngular: boolean,
347
+ angularCompilerOptions?: Record<string, unknown>,
348
+ ): ts.CompilerOptions {
349
+ const { pkgDir, output, env, compilerOptionsTransformer } = this._options;
350
+ const needsEmit = output.js || output.dts;
351
+
352
+ // env 조정
353
+ let options = env != null
354
+ ? getCompilerOptionsForEnv(baseOptions, env, pkgDir)
355
+ : { ...baseOptions };
356
+
357
+ // output 플래그
358
+ options.sourceMap = !isForAngular && output.js;
359
+ options.incremental = true;
360
+ options.outDir = path.join(pkgDir, "dist");
361
+
362
+ if (output.js && output.dts) {
363
+ options.noEmit = false;
364
+ options.emitDeclarationOnly = false;
365
+ options.declaration = true;
366
+ options.declarationMap = true;
367
+ options.declarationDir = path.join(pkgDir, "dist");
368
+ } else if (output.js) {
369
+ options.noEmit = false;
370
+ options.emitDeclarationOnly = false;
371
+ options.declaration = false;
372
+ options.declarationMap = false;
373
+ } else if (output.dts) {
374
+ options.noEmit = false;
375
+ options.emitDeclarationOnly = true;
376
+ options.declaration = true;
377
+ options.declarationMap = true;
378
+ options.declarationDir = path.join(pkgDir, "dist");
379
+ } else {
380
+ options.noEmit = true;
381
+ options.emitDeclarationOnly = false;
382
+ options.declaration = false;
383
+ options.declarationMap = false;
384
+ }
385
+
386
+ // Angular compilerOptions 병합
387
+ if (angularCompilerOptions != null) {
388
+ options = { ...options, ...(angularCompilerOptions as ts.CompilerOptions) };
389
+ }
390
+
391
+ // tsBuildInfoFile
392
+ const envSuffix = env != null ? `-${env}` : "";
393
+ const prefix = isForAngular ? "ngtsc-" : "";
394
+ options.tsBuildInfoFile = path.join(
395
+ pkgDir,
396
+ ".cache",
397
+ needsEmit
398
+ ? `${prefix}build${output.dts ? "" : "-no-dts"}${envSuffix}.tsbuildinfo`
399
+ : `${prefix}typecheck${envSuffix}.tsbuildinfo`,
400
+ );
401
+
402
+ // compilerOptionsTransformer 적용
403
+ if (compilerOptionsTransformer != null) {
404
+ options = compilerOptionsTransformer(options);
405
+ }
406
+
407
+ return options;
408
+ }
409
+
410
+ private _createHost(
411
+ compilerOptions: ts.CompilerOptions,
412
+ isForAngular: boolean,
413
+ effectiveTransformStylesheet?: ISdTsCompilerOptions["transformStylesheet"],
414
+ ): ts.CompilerHost {
415
+ const host = ts.createIncrementalCompilerHost(compilerOptions);
416
+ const { pkgDir, output } = this._options;
417
+ const needsEmit = output.js || output.dts;
418
+
419
+ // Non-Angular emit용 writeFile 훅
420
+ if (!isForAngular && needsEmit) {
421
+ const rewritePath = createOutputPathRewriter(pkgDir);
422
+ const originalWriteFile = host.writeFile;
423
+ host.writeFile = (fileName, content, writeByteOrderMark, onError, sourceFiles, data) => {
424
+ const result = rewritePath(fileName, content);
425
+ if (result != null) {
426
+ let [newPath, newContent] = result;
427
+ if (newPath.endsWith(".js")) {
428
+ newContent = addJsExtensionToImports(newContent);
429
+ }
430
+ originalWriteFile(newPath, newContent, writeByteOrderMark, onError, sourceFiles, data);
431
+ }
432
+ };
433
+ }
434
+
435
+ // Angular host 확장
436
+ if (isForAngular) {
437
+ this._extendHostForAngular(host, effectiveTransformStylesheet);
438
+ }
439
+
440
+ // moduleResolutionCache
441
+ const moduleResolutionCache = ts.createModuleResolutionCache(
442
+ host.getCurrentDirectory(),
443
+ host.getCanonicalFileName.bind(host),
444
+ compilerOptions,
445
+ this._packageJsonCache,
446
+ );
447
+ host.getModuleResolutionCache = () => moduleResolutionCache;
448
+
449
+ if (this._packageJsonCache == null) {
450
+ this._packageJsonCache = moduleResolutionCache.getPackageJsonInfoCache();
451
+ }
452
+
453
+ return host;
454
+ }
455
+
456
+ private _collectDiagnosticsForTsc(
457
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
458
+ ): ts.Diagnostic[] {
459
+ return [
460
+ ...builderProgram.getConfigFileParsingDiagnostics(),
461
+ ...builderProgram.getSyntacticDiagnostics(),
462
+ ...builderProgram.getOptionsDiagnostics(),
463
+ ...builderProgram.getGlobalDiagnostics(),
464
+ ...builderProgram.getSemanticDiagnostics(),
465
+ ...(!this._options.output.dts ? builderProgram.getProgram().getDeclarationDiagnostics() : []),
466
+ ];
467
+ }
468
+
469
+ private _collectDiagnosticsForAngular(
470
+ ngtscProgram: NgtscProgram,
471
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
472
+ affectedSourceFiles: Set<ts.SourceFile>,
473
+ ): ts.Diagnostic[] {
474
+ const angularCompiler = ngtscProgram.compiler;
475
+ const diagnostics: ts.Diagnostic[] = [];
476
+ const optimization =
477
+ affectedSourceFiles.size === 1 ? OptimizeFor.SingleFile : OptimizeFor.WholeProgram;
478
+
479
+ // 1. Option 진단
480
+ diagnostics.push(...builderProgram.getConfigFileParsingDiagnostics());
481
+ diagnostics.push(...angularCompiler.getOptionDiagnostics());
482
+ diagnostics.push(...builderProgram.getOptionsDiagnostics());
483
+
484
+ // 2. Global
485
+ diagnostics.push(...builderProgram.getGlobalDiagnostics());
486
+
487
+ // 3. 파일별 루프
488
+ for (const sourceFile of builderProgram.getSourceFiles()) {
489
+ if (angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
490
+ continue;
491
+ }
492
+
493
+ diagnostics.push(...builderProgram.getSyntacticDiagnostics(sourceFile));
494
+
495
+ // TypeScript 5.9 + NgtscProgram: getSemanticDiagnostics 크래시 방어
496
+ let semanticDiags: readonly ts.Diagnostic[];
497
+ try {
498
+ semanticDiags = builderProgram.getSemanticDiagnostics(sourceFile);
499
+ } catch {
500
+ logger.debug(`getSemanticDiagnostics 크래시 (무시): ${sourceFile.fileName}`);
501
+ semanticDiags = [];
502
+ }
503
+ diagnostics.push(...semanticDiags);
504
+
505
+ // Declaration files는 Angular 진단 없음
506
+ if (sourceFile.isDeclarationFile) {
507
+ continue;
508
+ }
509
+
510
+ // Angular 템플릿 진단 (diagnosticCache 활용)
511
+ if (affectedSourceFiles.has(sourceFile)) {
512
+ const angularDiagnostics = angularCompiler.getDiagnosticsForFile(
513
+ sourceFile,
514
+ optimization,
515
+ );
516
+ this._diagnosticCache.set(sourceFile, angularDiagnostics);
517
+ diagnostics.push(...angularDiagnostics);
518
+ } else {
519
+ const cached = this._diagnosticCache.get(sourceFile);
520
+ if (cached) {
521
+ diagnostics.push(...cached);
522
+ }
523
+ }
524
+ }
525
+
526
+ return diagnostics;
527
+ }
528
+
529
+ private _finalizeDiagnostics(rawDiagnostics: ts.Diagnostic[]): {
530
+ diagnostics: SerializedDiagnostic[];
531
+ errorCount: number;
532
+ warningCount: number;
533
+ errors?: string[];
534
+ } {
535
+ const filtered = rawDiagnostics.filter((d) => isWorkspaceDiagnostic(d, this._options.cwd));
536
+ const serialized = filtered.map(serializeDiagnostic);
537
+ const errorCount = filtered.filter((d) => d.category === ts.DiagnosticCategory.Error).length;
538
+ const warningCount = filtered.filter(
539
+ (d) => d.category === ts.DiagnosticCategory.Warning,
540
+ ).length;
541
+ const errors = filtered
542
+ .filter((d) => d.category === ts.DiagnosticCategory.Error)
543
+ .map((d) => formatDiagnosticError(d, this._options.cwd));
544
+ return {
545
+ diagnostics: serialized,
546
+ errorCount,
547
+ warningCount,
548
+ errors: errors.length > 0 ? errors : undefined,
549
+ };
550
+ }
551
+
552
+ private _emitTsc(
553
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
554
+ ): void {
555
+ builderProgram.emit();
556
+ }
557
+
558
+ private _emitAngular(
559
+ ngtscProgram: NgtscProgram,
560
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
561
+ affectedSourceFiles: Set<ts.SourceFile>,
562
+ emitOptions?: ISdTsCompilerEmitOptions,
563
+ ): EmitResult[] | undefined {
564
+ const compilerOptions = builderProgram.getCompilerOptions();
565
+
566
+ // noEmit이면 emit 건너뜀
567
+ if (compilerOptions.noEmit) {
568
+ return undefined;
569
+ }
570
+
571
+ const angularCompiler = ngtscProgram.compiler;
572
+ const tsProgram = ngtscProgram.getTsProgram();
573
+
574
+ // prepareEmit() → Angular transformers 획득
575
+ const transformers = angularCompiler.prepareEmit().transformers;
576
+ transformers.before ??= [];
577
+ transformers.after ??= [];
578
+
579
+ // additionalTransformers 병합
580
+ if (emitOptions?.additionalTransformers != null) {
581
+ if (emitOptions.additionalTransformers.before != null) {
582
+ transformers.before.push(...emitOptions.additionalTransformers.before);
583
+ }
584
+ if (emitOptions.additionalTransformers.after != null) {
585
+ transformers.after.push(...emitOptions.additionalTransformers.after);
586
+ }
587
+ }
588
+
589
+ // emit 결과를 수집
590
+ const emitResults: EmitResult[] = [];
591
+ const emitDeclarationOnly = !!compilerOptions.emitDeclarationOnly;
592
+
593
+ const writeFileCallback: ts.WriteFileCallback = (
594
+ filename,
595
+ contents,
596
+ _writeByteOrderMark,
597
+ _onError,
598
+ sourceFiles,
599
+ ) => {
600
+ if (sourceFiles == null || sourceFiles.length === 0) {
601
+ return;
602
+ }
603
+ const sourceFile = ts.getOriginalNode(sourceFiles[0], ts.isSourceFile);
604
+ if (angularCompiler.ignoreForEmit.has(sourceFile)) {
605
+ return;
606
+ }
607
+ angularCompiler.incrementalCompilation.recordSuccessfulEmit(sourceFile);
608
+ emitResults.push({ filename, contents, sourceFileName: sourceFile.fileName });
609
+ };
610
+
611
+ // per-file emit
612
+ for (const sourceFile of tsProgram.getSourceFiles()) {
613
+ if (angularCompiler.ignoreForEmit.has(sourceFile)) {
614
+ continue;
615
+ }
616
+ if (sourceFile.isDeclarationFile) {
617
+ continue;
618
+ }
619
+ if (
620
+ angularCompiler.incrementalCompilation.safeToSkipEmit(sourceFile) &&
621
+ !affectedSourceFiles.has(sourceFile)
622
+ ) {
623
+ continue;
624
+ }
625
+ tsProgram.emit(
626
+ sourceFile,
627
+ writeFileCallback,
628
+ undefined,
629
+ emitDeclarationOnly,
630
+ transformers,
631
+ );
632
+ }
633
+
634
+ // .tsbuildinfo 영속화 (try-catch: TS 5.9 + NgtscProgram 크래시 방어)
635
+ try {
636
+ builderProgram.emit(undefined, () => {});
637
+ } catch {
638
+ logger.debug("builderProgram.emit 크래시 (무시) — tsbuildinfo 영속화 생략");
639
+ }
640
+
641
+ // sourceFilter 적용
642
+ logger.debug(`emitAffectedFiles 완료 (${emitResults.length}개 파일)`);
643
+ if (emitOptions?.sourceFilter != null) {
644
+ return emitResults.filter((r) => emitOptions.sourceFilter!(r.sourceFileName));
645
+ }
646
+ return emitResults;
647
+ }
648
+
649
+ private _findAffectedFilesForTsc(
650
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
651
+ ): ReadonlySet<string> | undefined {
652
+ const affectedFiles = new Set<string>();
653
+ while (true) {
654
+ const result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile();
655
+ if (result == null) break;
656
+ if ("fileName" in result.affected) {
657
+ affectedFiles.add(pathx.posix(result.affected.fileName));
658
+ } else {
659
+ // ts.Program 반환 — 전역 변경, 전체 리빌드로 처리
660
+ return undefined;
661
+ }
662
+ }
663
+ return affectedFiles;
664
+ }
665
+
666
+ private _findAffectedFilesForAngular(
667
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram,
668
+ angularCompiler: NgCompiler,
669
+ sourceFileCache?: AngularSourceFileCache,
670
+ ): { affectedSourceFiles: Set<ts.SourceFile>; affectedPaths: ReadonlySet<string> | undefined } {
671
+ logger.debug("Angular affected 파일 탐색 시작");
672
+ const affectedSourceFiles = new Set<ts.SourceFile>();
673
+ let isGlobalChange = false;
674
+
675
+ while (true) {
676
+ let result: ReturnType<typeof builderProgram.getSemanticDiagnosticsOfNextAffectedFile>;
677
+ try {
678
+ result = builderProgram.getSemanticDiagnosticsOfNextAffectedFile(
679
+ undefined,
680
+ (sourceFile) => {
681
+ if (
682
+ angularCompiler.ignoreForDiagnostics.has(sourceFile) &&
683
+ sourceFile.fileName.endsWith(".ngtypecheck.ts")
684
+ ) {
685
+ const originalFilename = sourceFile.fileName.slice(0, -15) + ".ts";
686
+ const originalSourceFile = builderProgram.getSourceFile(originalFilename);
687
+ if (originalSourceFile) {
688
+ affectedSourceFiles.add(originalSourceFile);
689
+ }
690
+ return true;
691
+ }
692
+ return false;
693
+ },
694
+ );
695
+ } catch {
696
+ logger.debug("getSemanticDiagnosticsOfNextAffectedFile 크래시 (무시) — 모든 소스를 affected로 처리");
697
+ for (const sourceFile of builderProgram.getSourceFiles()) {
698
+ if (!angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
699
+ affectedSourceFiles.add(sourceFile);
700
+ }
701
+ }
702
+ break;
703
+ }
704
+ if (!result) break;
705
+ if ("fileName" in result.affected) {
706
+ affectedSourceFiles.add(result.affected);
707
+ } else {
708
+ isGlobalChange = true;
709
+ for (const sourceFile of builderProgram.getSourceFiles()) {
710
+ if (!angularCompiler.ignoreForDiagnostics.has(sourceFile)) {
711
+ affectedSourceFiles.add(sourceFile);
712
+ }
713
+ }
714
+ }
715
+ }
716
+
717
+ // 리소스 의존성 기반 diagnosticCache 무효화
718
+ if (sourceFileCache != null && sourceFileCache.modifiedFiles.size > 0) {
719
+ const normalizedModifiedFiles = new Set<string>();
720
+ for (const f of sourceFileCache.modifiedFiles) {
721
+ normalizedModifiedFiles.add(pathx.posix(f));
722
+ }
723
+
724
+ for (const sourceFile of builderProgram.getSourceFiles()) {
725
+ if (angularCompiler.ignoreForEmit.has(sourceFile)) {
726
+ continue;
727
+ }
728
+ const resourceDependencies = angularCompiler.getResourceDependencies(sourceFile);
729
+ for (const resourceDep of resourceDependencies) {
730
+ if (normalizedModifiedFiles.has(pathx.posix(resourceDep))) {
731
+ this._diagnosticCache.delete(sourceFile);
732
+ affectedSourceFiles.add(sourceFile);
733
+ break;
734
+ }
735
+ }
736
+ }
737
+ }
738
+
739
+ // affectedPaths 변환
740
+ let affectedPaths: ReadonlySet<string> | undefined;
741
+ if (isGlobalChange) {
742
+ affectedPaths = undefined;
743
+ } else {
744
+ const paths = new Set<string>();
745
+ for (const sf of affectedSourceFiles) {
746
+ paths.add(pathx.posix(sf.fileName));
747
+ }
748
+ affectedPaths = paths;
749
+ }
750
+
751
+ logger.debug(`Angular affected 파일 탐색 완료 (${affectedSourceFiles.size}개)`);
752
+ return { affectedSourceFiles, affectedPaths };
753
+ }
754
+
755
+ private _extendHostForAngular(
756
+ host: ts.CompilerHost,
757
+ effectiveTransformStylesheet?: ISdTsCompilerOptions["transformStylesheet"],
758
+ ): void {
759
+ const hostAny = host as ts.CompilerHost & Record<string, unknown>;
760
+ const { externalStylesheets, sourceFileCache } = this._options;
761
+ const transformStylesheet = effectiveTransformStylesheet;
762
+
763
+ // readResource
764
+ hostAny["readResource"] = (fileName: string) => {
765
+ return host.readFile(fileName) ?? "";
766
+ };
767
+
768
+ // transformResource
769
+ if (transformStylesheet != null) {
770
+ hostAny["transformResource"] = async (
771
+ data: string,
772
+ context: { type: string; containingFile: string; resourceFile: string | null },
773
+ ) => {
774
+ if (context.type !== "style") {
775
+ return null;
776
+ }
777
+ if (data.trim().length === 0) {
778
+ return { content: "" };
779
+ }
780
+ const result = await transformStylesheet(
781
+ data,
782
+ context.containingFile,
783
+ context.resourceFile ?? undefined,
784
+ );
785
+ return typeof result === "string" ? { content: result } : null;
786
+ };
787
+ }
788
+
789
+ // getModifiedResourceFiles
790
+ const cache = this._sourceFileCache ?? sourceFileCache;
791
+ if (cache != null) {
792
+ hostAny["getModifiedResourceFiles"] = () => cache.modifiedFiles;
793
+ }
794
+
795
+ // resourceNameToFileName
796
+ hostAny["resourceNameToFileName"] = (
797
+ resourceName: string,
798
+ containingFile: string,
799
+ ): string | null => {
800
+ const resolvedPath = path.join(path.dirname(containingFile), resourceName);
801
+ if (!host.fileExists(resolvedPath)) {
802
+ return null;
803
+ }
804
+ if (externalStylesheets == null || hasTemplateExtension(resolvedPath)) {
805
+ return resolvedPath;
806
+ }
807
+ let externalId = externalStylesheets.get(resolvedPath);
808
+ if (externalId == null) {
809
+ externalId = createHash("sha256").update(resolvedPath).digest("hex");
810
+ externalStylesheets.set(resolvedPath, externalId);
811
+ }
812
+ return externalId + ".css";
813
+ };
814
+ }
815
+ }