@simplysm/sd-cli 14.0.8 → 14.0.10

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 (254) hide show
  1. package/dist/angular/client-transform-stylesheet.d.ts.map +1 -1
  2. package/dist/angular/client-transform-stylesheet.js.map +1 -1
  3. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  4. package/dist/angular/vite-angular-plugin.js +15 -8
  5. package/dist/angular/vite-angular-plugin.js.map +1 -1
  6. package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
  7. package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
  8. package/dist/capacitor/capacitor.d.ts.map +1 -1
  9. package/dist/capacitor/capacitor.js +41 -41
  10. package/dist/capacitor/capacitor.js.map +1 -1
  11. package/dist/commands/build.d.ts.map +1 -1
  12. package/dist/commands/build.js.map +1 -1
  13. package/dist/commands/check.d.ts.map +1 -1
  14. package/dist/commands/check.js.map +1 -1
  15. package/dist/commands/dev.d.ts.map +1 -1
  16. package/dist/commands/dev.js.map +1 -1
  17. package/dist/commands/lint.d.ts.map +1 -1
  18. package/dist/commands/lint.js.map +1 -1
  19. package/dist/commands/publish.d.ts.map +1 -1
  20. package/dist/commands/publish.js.map +1 -1
  21. package/dist/commands/replace-deps.d.ts.map +1 -1
  22. package/dist/commands/replace-deps.js.map +1 -1
  23. package/dist/commands/typecheck.d.ts.map +1 -1
  24. package/dist/commands/typecheck.js +12 -12
  25. package/dist/commands/typecheck.js.map +1 -1
  26. package/dist/commands/watch.d.ts.map +1 -1
  27. package/dist/commands/watch.js.map +1 -1
  28. package/dist/electron/electron.d.ts.map +1 -1
  29. package/dist/electron/electron.js +26 -27
  30. package/dist/electron/electron.js.map +1 -1
  31. package/dist/engines/BaseEngine.d.ts +1 -5
  32. package/dist/engines/BaseEngine.d.ts.map +1 -1
  33. package/dist/engines/BaseEngine.js +7 -16
  34. package/dist/engines/BaseEngine.js.map +1 -1
  35. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  36. package/dist/engines/NgtscEngine.js +10 -11
  37. package/dist/engines/NgtscEngine.js.map +1 -1
  38. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
  39. package/dist/engines/ServerEsbuildEngine.js +10 -11
  40. package/dist/engines/ServerEsbuildEngine.js.map +1 -1
  41. package/dist/engines/TscEngine.d.ts.map +1 -1
  42. package/dist/engines/TscEngine.js +10 -11
  43. package/dist/engines/TscEngine.js.map +1 -1
  44. package/dist/engines/ViteEngine.d.ts.map +1 -1
  45. package/dist/engines/ViteEngine.js +3 -13
  46. package/dist/engines/ViteEngine.js.map +1 -1
  47. package/dist/engines/index.d.ts.map +1 -1
  48. package/dist/engines/index.js.map +1 -1
  49. package/dist/engines/types.d.ts +3 -6
  50. package/dist/engines/types.d.ts.map +1 -1
  51. package/dist/engines/types.js.map +1 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/infra/ResultCollector.d.ts +1 -1
  55. package/dist/infra/ResultCollector.d.ts.map +1 -1
  56. package/dist/infra/ResultCollector.js.map +1 -1
  57. package/dist/infra/SignalHandler.d.ts.map +1 -1
  58. package/dist/infra/SignalHandler.js.map +1 -1
  59. package/dist/infra/WorkerManager.d.ts.map +1 -1
  60. package/dist/infra/WorkerManager.js.map +1 -1
  61. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  62. package/dist/orchestrators/BuildOrchestrator.js +30 -61
  63. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  64. package/dist/orchestrators/DevWatchOrchestrator.d.ts +2 -0
  65. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  66. package/dist/orchestrators/DevWatchOrchestrator.js +40 -44
  67. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  68. package/dist/sd-cli-entry.d.ts.map +1 -1
  69. package/dist/sd-cli-entry.js +2 -13
  70. package/dist/sd-cli-entry.js.map +1 -1
  71. package/dist/sd-cli.d.ts.map +1 -1
  72. package/dist/sd-cli.js.map +1 -1
  73. package/dist/sd-config.types.d.ts.map +1 -1
  74. package/dist/sd-config.types.js.map +1 -1
  75. package/dist/utils/SdCliReporter.d.ts +18 -0
  76. package/dist/utils/SdCliReporter.d.ts.map +1 -0
  77. package/dist/utils/SdCliReporter.js +144 -0
  78. package/dist/utils/SdCliReporter.js.map +1 -0
  79. package/dist/utils/angular-build.d.ts.map +1 -1
  80. package/dist/utils/angular-build.js.map +1 -1
  81. package/dist/utils/angular-compiler.d.ts.map +1 -1
  82. package/dist/utils/angular-compiler.js +11 -4
  83. package/dist/utils/angular-compiler.js.map +1 -1
  84. package/dist/utils/build-env.d.ts.map +1 -1
  85. package/dist/utils/build-env.js +2 -1
  86. package/dist/utils/build-env.js.map +1 -1
  87. package/dist/utils/concurrency.d.ts.map +1 -1
  88. package/dist/utils/concurrency.js.map +1 -1
  89. package/dist/utils/copy-public.d.ts.map +1 -1
  90. package/dist/utils/copy-public.js +21 -21
  91. package/dist/utils/copy-public.js.map +1 -1
  92. package/dist/utils/copy-src.d.ts.map +1 -1
  93. package/dist/utils/copy-src.js +12 -12
  94. package/dist/utils/copy-src.js.map +1 -1
  95. package/dist/utils/diagnostic-utils.d.ts.map +1 -1
  96. package/dist/utils/diagnostic-utils.js +3 -2
  97. package/dist/utils/diagnostic-utils.js.map +1 -1
  98. package/dist/utils/engine-stop.d.ts.map +1 -1
  99. package/dist/utils/engine-stop.js.map +1 -1
  100. package/dist/utils/esbuild-config.d.ts.map +1 -1
  101. package/dist/utils/esbuild-config.js +2 -0
  102. package/dist/utils/esbuild-config.js.map +1 -1
  103. package/dist/utils/generate-pwa-icons.d.ts.map +1 -1
  104. package/dist/utils/generate-pwa-icons.js.map +1 -1
  105. package/dist/utils/hmr-candidates.d.ts.map +1 -1
  106. package/dist/utils/hmr-candidates.js.map +1 -1
  107. package/dist/utils/lint-utils.d.ts.map +1 -1
  108. package/dist/utils/lint-utils.js.map +1 -1
  109. package/dist/utils/lint-with-program.d.ts.map +1 -1
  110. package/dist/utils/lint-with-program.js +7 -3
  111. package/dist/utils/lint-with-program.js.map +1 -1
  112. package/dist/utils/ngtsc-build-core.d.ts +2 -10
  113. package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
  114. package/dist/utils/ngtsc-build-core.js +16 -15
  115. package/dist/utils/ngtsc-build-core.js.map +1 -1
  116. package/dist/utils/orchestrator-utils.d.ts.map +1 -1
  117. package/dist/utils/orchestrator-utils.js.map +1 -1
  118. package/dist/utils/output-path-rewriter.d.ts.map +1 -1
  119. package/dist/utils/output-path-rewriter.js +7 -7
  120. package/dist/utils/output-path-rewriter.js.map +1 -1
  121. package/dist/utils/output-utils.d.ts.map +1 -1
  122. package/dist/utils/output-utils.js +1 -1
  123. package/dist/utils/output-utils.js.map +1 -1
  124. package/dist/utils/package-utils.d.ts +4 -0
  125. package/dist/utils/package-utils.d.ts.map +1 -1
  126. package/dist/utils/package-utils.js +34 -13
  127. package/dist/utils/package-utils.js.map +1 -1
  128. package/dist/utils/rebuild-manager.d.ts +1 -1
  129. package/dist/utils/rebuild-manager.d.ts.map +1 -1
  130. package/dist/utils/rebuild-manager.js +3 -1
  131. package/dist/utils/rebuild-manager.js.map +1 -1
  132. package/dist/utils/replace-deps.d.ts.map +1 -1
  133. package/dist/utils/replace-deps.js +10 -10
  134. package/dist/utils/replace-deps.js.map +1 -1
  135. package/dist/utils/scss-compiler.d.ts.map +1 -1
  136. package/dist/utils/scss-compiler.js.map +1 -1
  137. package/dist/utils/sd-config.d.ts.map +1 -1
  138. package/dist/utils/sd-config.js +2 -3
  139. package/dist/utils/sd-config.js.map +1 -1
  140. package/dist/utils/tsc-build.d.ts +3 -1
  141. package/dist/utils/tsc-build.d.ts.map +1 -1
  142. package/dist/utils/tsc-build.js +7 -4
  143. package/dist/utils/tsc-build.js.map +1 -1
  144. package/dist/utils/tsconfig.d.ts.map +1 -1
  145. package/dist/utils/tsconfig.js.map +1 -1
  146. package/dist/utils/typecheck-non-package.d.ts.map +1 -1
  147. package/dist/utils/typecheck-non-package.js +10 -5
  148. package/dist/utils/typecheck-non-package.js.map +1 -1
  149. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  150. package/dist/utils/typecheck-serialization.js.map +1 -1
  151. package/dist/utils/vite-config.d.ts.map +1 -1
  152. package/dist/utils/vite-config.js +2 -1
  153. package/dist/utils/vite-config.js.map +1 -1
  154. package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
  155. package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
  156. package/dist/utils/worker-events.d.ts +1 -1
  157. package/dist/utils/worker-events.d.ts.map +1 -1
  158. package/dist/utils/worker-events.js +1 -0
  159. package/dist/utils/worker-events.js.map +1 -1
  160. package/dist/utils/worker-utils.d.ts +1 -1
  161. package/dist/utils/worker-utils.d.ts.map +1 -1
  162. package/dist/utils/worker-utils.js +3 -1
  163. package/dist/utils/worker-utils.js.map +1 -1
  164. package/dist/vitest-plugin.d.ts.map +1 -1
  165. package/dist/vitest-plugin.js.map +1 -1
  166. package/dist/workers/client.worker.d.ts.map +1 -1
  167. package/dist/workers/client.worker.js +4 -0
  168. package/dist/workers/client.worker.js.map +1 -1
  169. package/dist/workers/library-build.worker.d.ts +2 -10
  170. package/dist/workers/library-build.worker.d.ts.map +1 -1
  171. package/dist/workers/library-build.worker.js +38 -14
  172. package/dist/workers/library-build.worker.js.map +1 -1
  173. package/dist/workers/lint.worker.d.ts.map +1 -1
  174. package/dist/workers/lint.worker.js.map +1 -1
  175. package/dist/workers/ngtsc-build.worker.d.ts.map +1 -1
  176. package/dist/workers/ngtsc-build.worker.js +40 -14
  177. package/dist/workers/ngtsc-build.worker.js.map +1 -1
  178. package/dist/workers/server-build.worker.d.ts +2 -10
  179. package/dist/workers/server-build.worker.d.ts.map +1 -1
  180. package/dist/workers/server-build.worker.js +28 -19
  181. package/dist/workers/server-build.worker.js.map +1 -1
  182. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  183. package/dist/workers/server-runtime.worker.js.map +1 -1
  184. package/package.json +4 -4
  185. package/src/angular/vite-angular-plugin.ts +18 -9
  186. package/src/capacitor/capacitor.ts +41 -41
  187. package/src/commands/typecheck.ts +12 -12
  188. package/src/electron/electron.ts +26 -27
  189. package/src/engines/BaseEngine.ts +8 -19
  190. package/src/engines/NgtscEngine.ts +11 -11
  191. package/src/engines/ServerEsbuildEngine.ts +11 -11
  192. package/src/engines/TscEngine.ts +11 -11
  193. package/src/engines/ViteEngine.ts +3 -14
  194. package/src/engines/types.ts +3 -6
  195. package/src/infra/ResultCollector.ts +1 -1
  196. package/src/orchestrators/BuildOrchestrator.ts +31 -62
  197. package/src/orchestrators/DevWatchOrchestrator.ts +41 -44
  198. package/src/sd-cli-entry.ts +2 -12
  199. package/src/utils/SdCliReporter.ts +177 -0
  200. package/src/utils/angular-compiler.ts +11 -4
  201. package/src/utils/build-env.ts +2 -1
  202. package/src/utils/copy-public.ts +21 -21
  203. package/src/utils/copy-src.ts +12 -12
  204. package/src/utils/diagnostic-utils.ts +3 -2
  205. package/src/utils/esbuild-config.ts +2 -0
  206. package/src/utils/lint-with-program.ts +7 -3
  207. package/src/utils/ngtsc-build-core.ts +18 -18
  208. package/src/utils/output-path-rewriter.ts +7 -7
  209. package/src/utils/output-utils.ts +1 -1
  210. package/src/utils/package-utils.ts +37 -13
  211. package/src/utils/rebuild-manager.ts +4 -2
  212. package/src/utils/replace-deps.ts +10 -10
  213. package/src/utils/sd-config.ts +2 -3
  214. package/src/utils/tsc-build.ts +9 -4
  215. package/src/utils/typecheck-non-package.ts +10 -5
  216. package/src/utils/vite-config.ts +2 -1
  217. package/src/utils/worker-events.ts +2 -1
  218. package/src/utils/worker-utils.ts +3 -1
  219. package/src/workers/client.worker.ts +4 -0
  220. package/src/workers/library-build.worker.ts +48 -18
  221. package/src/workers/ngtsc-build.worker.ts +48 -13
  222. package/src/workers/server-build.worker.ts +30 -23
  223. package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +11 -7
  224. package/tests/angular/vite-angular-plugin-lint.spec.ts +6 -1
  225. package/tests/capacitor/capacitor-build.spec.ts +5 -0
  226. package/tests/capacitor/capacitor-icon.spec.ts +5 -0
  227. package/tests/capacitor/capacitor-init.spec.ts +5 -0
  228. package/tests/capacitor/capacitor-run.spec.ts +5 -0
  229. package/tests/capacitor/capacitor-workspace.spec.ts +5 -0
  230. package/tests/commands/typecheck.spec.ts +20 -31
  231. package/tests/electron/electron.spec.ts +5 -0
  232. package/tests/engines/base-engine.spec.ts +15 -21
  233. package/tests/engines/engine-lint-integration.spec.ts +5 -10
  234. package/tests/engines/ngtsc-engine.spec.ts +27 -41
  235. package/tests/engines/server-esbuild-engine.spec.ts +18 -29
  236. package/tests/engines/tsc-engine.spec.ts +14 -23
  237. package/tests/engines/vite-engine.spec.ts +10 -15
  238. package/tests/infra/result-collector.spec.ts +2 -2
  239. package/tests/orchestrators/build-orchestrator.spec.ts +19 -29
  240. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +110 -95
  241. package/tests/utils/copy-src.spec.ts +25 -19
  242. package/tests/utils/diagnostic-utils.spec.ts +72 -0
  243. package/tests/utils/ngtsc-build-core-angular-compiler.spec.ts +2 -3
  244. package/tests/utils/output-path-rewriter.spec.ts +7 -6
  245. package/tests/utils/output-utils.spec.ts +5 -5
  246. package/tests/utils/rebuild-manager.spec.ts +1 -1
  247. package/tests/utils/sd-config.spec.ts +4 -0
  248. package/tests/utils/tsc-build.spec.ts +23 -5
  249. package/tests/workers/library-build-worker.spec.ts +113 -20
  250. package/tests/workers/ngtsc-build-lint.spec.ts +3 -6
  251. package/tests/workers/ngtsc-build-worker.spec.ts +17 -19
  252. package/tests/workers/server-build-lint.spec.ts +4 -1
  253. package/tests/workers/server-build-worker.spec.ts +19 -22
  254. package/tests/angular/migration-cleanup.spec.ts +0 -59
@@ -29,8 +29,7 @@ vi.mock("../../src/utils/package-utils", async (importOriginal) => {
29
29
  ...actual,
30
30
  filterPackagesByTargets: vi.fn(),
31
31
  validateTargets: vi.fn(),
32
- discoverWorkspacePackages: vi.fn(),
33
- mergeTestsPackagesIntoConfig: vi.fn(),
32
+ buildPathMapFromConfig: vi.fn(),
34
33
  };
35
34
  });
36
35
 
@@ -94,8 +93,7 @@ vi.mock("../../src/engines/index", () => ({
94
93
  const engine = {
95
94
  run: vi.fn().mockResolvedValue({
96
95
  success: true,
97
- js: { success: true, errors: [], warnings: [] },
98
- dts: { success: true, errors: [], warnings: [], diagnostics: [] },
96
+ build: { success: true, errors: [], warnings: [], diagnostics: [] },
99
97
  }),
100
98
  startWatch: vi.fn().mockResolvedValue(undefined),
101
99
  stop: vi.fn().mockResolvedValue(undefined),
@@ -116,6 +114,12 @@ vi.mock("@simplysm/core-node", () => ({
116
114
  Worker: {
117
115
  create: vi.fn(),
118
116
  },
117
+ pathx: {
118
+ posix: vi.fn((p: string) => p.replace(/\\/g, "/")),
119
+ posixResolve: vi.fn((...args: string[]) => args.join("/").replace(/\\/g, "/")),
120
+ isChildPath: vi.fn((child: string, parent: string) => child.startsWith(parent + "/")),
121
+ filterByTargets: vi.fn((files: string[], targets: string[]) => targets.length === 0 ? files : files),
122
+ },
119
123
  }));
120
124
 
121
125
  vi.mock("child_process", () => ({
@@ -156,7 +160,7 @@ vi.mock("../../src/electron/electron", () => ({
156
160
 
157
161
  const { DevWatchOrchestrator } = await import("../../src/orchestrators/DevWatchOrchestrator");
158
162
  const { loadSdConfig } = await import("../../src/utils/sd-config");
159
- const { filterPackagesByTargets, validateTargets, discoverWorkspacePackages, mergeTestsPackagesIntoConfig } = await import("../../src/utils/package-utils");
163
+ const { filterPackagesByTargets, validateTargets, buildPathMapFromConfig } = await import("../../src/utils/package-utils");
160
164
  const { watchReplaceDeps } = await import("../../src/utils/replace-deps");
161
165
  const { printErrors, printServers: _printServers } = await import("../../src/utils/output-utils");
162
166
  const { createBuildEngine } = await import("../../src/engines/index");
@@ -171,7 +175,7 @@ const { Capacitor } = await import("../../src/capacitor/capacitor");
171
175
  const { Electron } = await import("../../src/electron/electron");
172
176
 
173
177
  import type { SdConfig } from "../../src/sd-config.types";
174
- import nodePath from "path";
178
+
175
179
 
176
180
  // --- Runtime worker mock helper ---
177
181
 
@@ -204,16 +208,14 @@ function createConfig(overrides: Partial<SdConfig> = {}): SdConfig {
204
208
  return { packages: {}, ...overrides };
205
209
  }
206
210
 
207
- function setupDefaults(config: SdConfig, workspacePackages?: Map<string, string>): void {
211
+ function setupDefaults(config: SdConfig): void {
208
212
  vi.mocked(loadSdConfig).mockResolvedValue(config);
209
- vi.mocked(discoverWorkspacePackages).mockReturnValue(workspacePackages ?? new Map());
210
- vi.mocked(mergeTestsPackagesIntoConfig).mockImplementation((configPackages, _wp) => {
211
- // Default: no tests packages, just build pathMap from config
213
+ vi.mocked(buildPathMapFromConfig).mockImplementation((configPackages) => {
212
214
  const pathMap = new Map<string, string>();
213
215
  for (const name of Object.keys(configPackages)) {
214
216
  pathMap.set(name, `packages/${name}`);
215
217
  }
216
- return { merged: configPackages, pathMap };
218
+ return pathMap;
217
219
  });
218
220
  vi.mocked(filterPackagesByTargets).mockImplementation((pkgs) => {
219
221
  const result: Record<string, any> = {};
@@ -255,8 +257,7 @@ describe("DevWatchOrchestrator", () => {
255
257
  const engine = {
256
258
  run: vi.fn().mockResolvedValue({
257
259
  success: true,
258
- js: { success: true, errors: [], warnings: [] },
259
- dts: { success: true, errors: [], warnings: [], diagnostics: [] },
260
+ build: { success: true, errors: [], warnings: [], diagnostics: [] },
260
261
  }),
261
262
  startWatch: vi.fn().mockResolvedValue(undefined),
262
263
  stop: vi.fn().mockResolvedValue(undefined),
@@ -326,7 +327,7 @@ describe("DevWatchOrchestrator", () => {
326
327
 
327
328
  // Library engine: startWatch with js+dts
328
329
  const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
329
- expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: true });
330
+ expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: false });
330
331
 
331
332
  // Scripts+watch: spawn called for hook
332
333
  expect(spawn).toHaveBeenCalled();
@@ -363,7 +364,7 @@ describe("DevWatchOrchestrator", () => {
363
364
  await orchestrator.start();
364
365
 
365
366
  for (const engine of mockBuildEngines) {
366
- expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: true });
367
+ expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: false });
367
368
  }
368
369
  });
369
370
 
@@ -483,7 +484,7 @@ describe("DevWatchOrchestrator", () => {
483
484
  // Build engine created and started
484
485
  expect(createBuildEngine).toHaveBeenCalledTimes(1);
485
486
  const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
486
- expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: true });
487
+ expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: false });
487
488
 
488
489
  // Watch hook also executed
489
490
  expect(spawn).toHaveBeenCalledWith("node", ["sync.mjs"], expect.objectContaining({ shell: true }));
@@ -572,10 +573,10 @@ describe("DevWatchOrchestrator", () => {
572
573
 
573
574
  const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
574
575
  const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
575
- const batchHandler = onCall?.[1] as (() => void) | undefined;
576
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
576
577
 
577
578
  vi.mocked(printErrors).mockClear();
578
- batchHandler?.();
579
+ batchHandler?.(["test:build"]);
579
580
 
580
581
  expect(printErrors).toHaveBeenCalledOnce();
581
582
  });
@@ -619,7 +620,7 @@ describe("DevWatchOrchestrator", () => {
619
620
  function setupEngineWithResult(status: "success" | "error" = "success"): void {
620
621
  vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
621
622
  const engine = {
622
- run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
623
+ run: vi.fn().mockResolvedValue({ success: true, build: { success: true, errors: [], warnings: [], diagnostics: [] } }),
623
624
  startWatch: vi.fn().mockImplementation(() => {
624
625
  options.resultCollector?.add({
625
626
  name: pkg.name, target: "server", type: "build", status,
@@ -649,11 +650,9 @@ describe("DevWatchOrchestrator", () => {
649
650
  expect.objectContaining({ name: "service-server" }),
650
651
  expect.any(Object),
651
652
  );
652
- expect(mockBuildEngines[0].startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
653
- expect(Worker.create).toHaveBeenCalledTimes(1);
654
- expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
655
- expect.objectContaining({ env: expect.objectContaining({ VER: "1.0.0", DEV: "true" }) }),
656
- );
653
+ expect(mockBuildEngines[0].startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
654
+ // Runtime is NOT started during initial build; it starts via batchComplete
655
+ expect(Worker.create).not.toHaveBeenCalled();
657
656
  });
658
657
 
659
658
  // --- Acceptance: Server 패키지 dev 모드 실행 ---
@@ -833,8 +832,8 @@ describe("DevWatchOrchestrator", () => {
833
832
  expect(createBuildEngine).toHaveBeenCalledOnce();
834
833
  });
835
834
 
836
- // --- Acceptance: Server 리빌드 시 runtime 재시작 ---
837
- it("restarts runtime on batchComplete with success", async () => {
835
+ // --- Acceptance: Server batchComplete 시 runtime 시작 ---
836
+ it("starts runtime on batchComplete with success", async () => {
838
837
  setupDefaults(createConfig({
839
838
  packages: { "service-server": { target: "server" } },
840
839
  }));
@@ -844,17 +843,20 @@ describe("DevWatchOrchestrator", () => {
844
843
  await orchestrator.initialize();
845
844
  await orchestrator.start();
846
845
 
846
+ // No runtime started initially
847
+ expect(Worker.create).not.toHaveBeenCalled();
848
+
847
849
  // Get batchComplete handler
848
850
  const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
849
851
  const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
850
- const batchHandler = onCall?.[1] as (() => void) | undefined;
852
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
851
853
 
852
- batchHandler?.();
853
- await new Promise((r) => setTimeout(r, 0));
854
+ batchHandler?.(["service-server:build"]);
855
+ await new Promise((r) => setTimeout(r, 200));
854
856
 
855
- // Old runtime terminated, new one created
856
- expect(mockRuntimeProxies).toHaveLength(2);
857
- expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
857
+ // First runtime created via batchComplete
858
+ expect(mockRuntimeProxies).toHaveLength(1);
859
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalled();
858
860
  });
859
861
 
860
862
  // --- Acceptance: SIGINT/SIGTERM 시 graceful shutdown (dev mode) ---
@@ -867,6 +869,14 @@ describe("DevWatchOrchestrator", () => {
867
869
  const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
868
870
  await orchestrator.initialize();
869
871
  await orchestrator.start();
872
+
873
+ // Trigger batchComplete to create runtime first
874
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
875
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
876
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
877
+ batchHandler?.(["service-server:build"]);
878
+ await new Promise((r) => setTimeout(r, 200));
879
+
870
880
  await orchestrator.shutdown();
871
881
 
872
882
  expect(mockBuildEngines[0].stop).toHaveBeenCalledOnce();
@@ -901,7 +911,7 @@ describe("DevWatchOrchestrator", () => {
901
911
  });
902
912
 
903
913
  // Unit: multiple server packages
904
- it("creates engines and runtimes for multiple server packages", async () => {
914
+ it("creates engines for multiple server packages (runtimes start via batchComplete)", async () => {
905
915
  setupDefaults(createConfig({
906
916
  packages: {
907
917
  "service-server": { target: "server" },
@@ -915,7 +925,8 @@ describe("DevWatchOrchestrator", () => {
915
925
  await orchestrator.start();
916
926
 
917
927
  expect(createBuildEngine).toHaveBeenCalledTimes(2);
918
- expect(Worker.create).toHaveBeenCalledTimes(2);
928
+ // Runtimes are NOT started during initial build
929
+ expect(Worker.create).not.toHaveBeenCalled();
919
930
  });
920
931
 
921
932
  // Unit: skips start when no packages
@@ -961,7 +972,7 @@ describe("DevWatchOrchestrator", () => {
961
972
  vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
962
973
  const isClient = pkg.config.target === "client";
963
974
  const engine: any = {
964
- run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
975
+ run: vi.fn().mockResolvedValue({ success: true, build: { success: true, errors: [], warnings: [], diagnostics: [] } }),
965
976
  startWatch: vi.fn().mockImplementation(() => {
966
977
  if (isClient) {
967
978
  // Client: simulate Vite serverReady
@@ -999,10 +1010,10 @@ describe("DevWatchOrchestrator", () => {
999
1010
  await orchestrator.start();
1000
1011
 
1001
1012
  const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
1002
- expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
1013
+ expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
1003
1014
  });
1004
1015
 
1005
- // --- Acceptance: 서버 연결 클라이언트 ready 대기 후 서버 시작 → clientPorts 전달 ---
1016
+ // --- Acceptance: batchComplete 서버 시작 → clientPorts 전달 ---
1006
1017
  it("passes clientPorts to server runtime for connected clients", async () => {
1007
1018
  setupDefaults(createConfig({
1008
1019
  packages: {
@@ -1016,6 +1027,16 @@ describe("DevWatchOrchestrator", () => {
1016
1027
  await orchestrator.initialize();
1017
1028
  await orchestrator.start();
1018
1029
 
1030
+ // Runtime starts via batchComplete, not initial start
1031
+ expect(Worker.create).not.toHaveBeenCalled();
1032
+
1033
+ // Trigger batchComplete
1034
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
1035
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
1036
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
1037
+ batchHandler?.(["service-server:build"]);
1038
+ await new Promise((r) => setTimeout(r, 200));
1039
+
1019
1040
  expect(Worker.create).toHaveBeenCalled();
1020
1041
  expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
1021
1042
  expect.objectContaining({
@@ -1038,6 +1059,13 @@ describe("DevWatchOrchestrator", () => {
1038
1059
  await orchestrator.initialize();
1039
1060
  await orchestrator.start();
1040
1061
 
1062
+ // Trigger batchComplete to start server runtime
1063
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
1064
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
1065
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
1066
+ batchHandler?.(["service-server:build"]);
1067
+ await new Promise((r) => setTimeout(r, 200));
1068
+
1041
1069
  expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
1042
1070
  expect.objectContaining({
1043
1071
  clientPorts: {},
@@ -1084,6 +1112,13 @@ describe("DevWatchOrchestrator", () => {
1084
1112
  await orchestrator.initialize();
1085
1113
  await orchestrator.start();
1086
1114
 
1115
+ // Trigger batchComplete to start server runtime
1116
+ const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
1117
+ const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
1118
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
1119
+ batchHandler?.(["service-server:build"]);
1120
+ await new Promise((r) => setTimeout(r, 200));
1121
+
1087
1122
  expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
1088
1123
  expect.objectContaining({
1089
1124
  clientPorts: {},
@@ -1127,15 +1162,28 @@ describe("DevWatchOrchestrator", () => {
1127
1162
  await orchestrator.initialize();
1128
1163
  await orchestrator.start();
1129
1164
 
1130
- // Trigger batchComplete
1165
+ // Trigger first batchComplete to start runtime
1131
1166
  const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
1132
1167
  const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
1133
- const batchHandler = onCall?.[1] as (() => void) | undefined;
1134
- batchHandler?.();
1135
- await new Promise((r) => setTimeout(r, 0));
1168
+ const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
1169
+ batchHandler?.(["service-server:build"]);
1170
+ await new Promise((r) => setTimeout(r, 200));
1171
+
1172
+ // First runtime created with clientPorts
1173
+ expect(mockRuntimeProxies).toHaveLength(1);
1174
+ expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
1175
+ expect.objectContaining({
1176
+ clientPorts: { "my-client": 54321 },
1177
+ }),
1178
+ );
1179
+
1180
+ // Trigger second batchComplete (rebuild)
1181
+ batchHandler?.(["service-server:build"]);
1182
+ await new Promise((r) => setTimeout(r, 200));
1136
1183
 
1137
1184
  // Second runtime start should also have clientPorts
1138
1185
  expect(mockRuntimeProxies).toHaveLength(2);
1186
+ expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
1139
1187
  expect(mockRuntimeProxies[1].start).toHaveBeenCalledWith(
1140
1188
  expect.objectContaining({
1141
1189
  clientPorts: { "my-client": 54321 },
@@ -1188,59 +1236,30 @@ describe("DevWatchOrchestrator", () => {
1188
1236
  });
1189
1237
  });
1190
1238
 
1191
- describe("tests packages inclusion", () => {
1192
- // Acceptance: tests 패키지는 tests/ 경로로 해석된다
1193
- it("resolves tests package dir to tests/ path in watch mode", async () => {
1194
- const wsPackages = new Map([
1195
- ["core-common", "packages/core-common"],
1196
- ["orm", "tests/orm"],
1197
- ]);
1198
- const mergedPackages = {
1199
- "core-common": { target: "node" },
1200
- "orm": { target: "node" },
1201
- };
1202
- const pathMap = new Map([
1203
- ["core-common", "packages/core-common"],
1204
- ["orm", "tests/orm"],
1205
- ]);
1206
-
1239
+ describe("tests packages exclusion", () => {
1240
+ // tests 패키지는 watch/dev에서 제외되고, sd.config.ts 패키지만 처리된다
1241
+ it("only processes sd.config.ts packages, excludes tests packages", async () => {
1207
1242
  setupDefaults(createConfig({
1208
1243
  packages: { "core-common": { target: "node" } },
1209
- }), wsPackages);
1210
-
1211
- vi.mocked(mergeTestsPackagesIntoConfig).mockReturnValue({
1212
- merged: mergedPackages as any,
1213
- pathMap,
1214
- });
1244
+ }));
1215
1245
 
1216
1246
  const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
1217
1247
  await orchestrator.initialize();
1218
1248
  await orchestrator.start();
1219
1249
 
1220
- // Both packages should have BuildEngines created
1221
- expect(createBuildEngine).toHaveBeenCalledTimes(2);
1222
-
1223
- // tests/orm package should have correct dir
1224
- const ormCall = vi.mocked(createBuildEngine).mock.calls.find(
1225
- (c: any[]) => c[0].name === "orm",
1226
- );
1227
- expect(ormCall).toBeDefined();
1228
- expect(ormCall![0]).toEqual(expect.objectContaining({
1229
- name: "orm",
1230
- dir: nodePath.join("/test-root", "tests", "orm"),
1231
- }));
1250
+ // Only config package should have BuildEngine created
1251
+ expect(createBuildEngine).toHaveBeenCalledTimes(1);
1232
1252
 
1233
- // packages/core-common should retain packages/ path
1234
1253
  const coreCall = vi.mocked(createBuildEngine).mock.calls.find(
1235
1254
  (c: any[]) => c[0].name === "core-common",
1236
1255
  );
1237
1256
  expect(coreCall![0]).toEqual(expect.objectContaining({
1238
1257
  name: "core-common",
1239
- dir: nodePath.join("/test-root", "packages", "core-common"),
1258
+ dir: "/test-root/packages/core-common",
1240
1259
  }));
1241
1260
  });
1242
1261
 
1243
- // Acceptance: sd.config.ts 패키지는 기존 packages/ 경로 유지
1262
+ // sd.config.ts 패키지는 기존 packages/ 경로 유지
1244
1263
  it("preserves packages/ path for sd.config.ts packages", async () => {
1245
1264
  setupDefaults(createConfig({
1246
1265
  packages: { "core-common": { target: "neutral" } },
@@ -1253,24 +1272,21 @@ describe("DevWatchOrchestrator", () => {
1253
1272
  (c: any[]) => c[0].name === "core-common",
1254
1273
  );
1255
1274
  expect(coreCall![0]).toEqual(expect.objectContaining({
1256
- dir: nodePath.join("/test-root", "packages", "core-common"),
1275
+ dir: "/test-root/packages/core-common",
1257
1276
  }));
1258
1277
  });
1259
1278
 
1260
- // Acceptance: mergeTestsPackagesIntoConfig is called with correct args
1261
- it("calls mergeTestsPackagesIntoConfig with sdConfig.packages and discovered workspace packages", async () => {
1262
- const wsPackages = new Map([["core-common", "packages/core-common"]]);
1279
+ // buildPathMapFromConfig이 올바른 인자로 호출된다
1280
+ it("calls buildPathMapFromConfig with sdConfig.packages", async () => {
1263
1281
  setupDefaults(createConfig({
1264
1282
  packages: { "core-common": { target: "node" } },
1265
- }), wsPackages);
1283
+ }));
1266
1284
 
1267
1285
  const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
1268
1286
  await orchestrator.initialize();
1269
1287
 
1270
- expect(discoverWorkspacePackages).toHaveBeenCalledWith("/test-root");
1271
- expect(mergeTestsPackagesIntoConfig).toHaveBeenCalledWith(
1288
+ expect(buildPathMapFromConfig).toHaveBeenCalledWith(
1272
1289
  { "core-common": { target: "node" } },
1273
- wsPackages,
1274
1290
  );
1275
1291
  });
1276
1292
  });
@@ -1336,8 +1352,7 @@ describe("DevWatchOrchestrator", () => {
1336
1352
  const engine = {
1337
1353
  run: vi.fn().mockResolvedValue({
1338
1354
  success: true,
1339
- js: { success: true, errors: [], warnings: [] },
1340
- dts: { success: true, errors: [], warnings: [], diagnostics: [] },
1355
+ build: { success: true, errors: [], warnings: [], diagnostics: [] },
1341
1356
  }),
1342
1357
  startWatch: vi.fn().mockImplementation(() => {
1343
1358
  // Simulate server ready with port
@@ -1571,8 +1586,8 @@ describe("DevWatchOrchestrator", () => {
1571
1586
  //#region Slice 4: watch/dev lint 활성화 (Feature 3.2)
1572
1587
 
1573
1588
  describe("lint activation", () => {
1574
- // Scenario: watch 초기 빌드에서 lint 실행된다
1575
- it("passes lint:true to startWatch for library engines in watch mode", async () => {
1589
+ // Scenario: watch 초기 빌드에서 lint 비활성화 (별도 실행으로 분리됨)
1590
+ it("passes lint:false to startWatch for library engines in watch mode", async () => {
1576
1591
  setupDefaults(createConfig({
1577
1592
  packages: {
1578
1593
  "core-common": { target: "node" },
@@ -1585,12 +1600,12 @@ describe("DevWatchOrchestrator", () => {
1585
1600
  await orchestrator.start();
1586
1601
 
1587
1602
  for (const engine of mockBuildEngines) {
1588
- expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: true });
1603
+ expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true, lint: false });
1589
1604
  }
1590
1605
  });
1591
1606
 
1592
- // Scenario: dev 초기 빌드에서 server/client lint 실행된다
1593
- it("passes lint:true to startWatch for server and client engines in dev mode", async () => {
1607
+ // Scenario: dev 초기 빌드에서 server/client lint 비활성화
1608
+ it("passes lint:false to startWatch for server and client engines in dev mode", async () => {
1594
1609
  setupDefaults(createConfig({
1595
1610
  packages: {
1596
1611
  "demo-server": { target: "server" },
@@ -1605,8 +1620,8 @@ describe("DevWatchOrchestrator", () => {
1605
1620
  const serverEngine = mockBuildEngines.find((e) => e._pkgName === "demo-server")!;
1606
1621
  const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
1607
1622
 
1608
- expect(serverEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
1609
- expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: true });
1623
+ expect(serverEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
1624
+ expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
1610
1625
  });
1611
1626
  });
1612
1627
 
@@ -4,6 +4,8 @@ import path from "path";
4
4
  const mockOnChange = vi.fn();
5
5
  const mockWatcherClose = vi.fn();
6
6
 
7
+ const toPosix = (p: string) => p.replace(/\\/g, "/");
8
+
7
9
  vi.mock("@simplysm/core-node", () => ({
8
10
  fsx: {
9
11
  glob: vi.fn(),
@@ -11,6 +13,10 @@ vi.mock("@simplysm/core-node", () => ({
11
13
  copy: vi.fn(),
12
14
  rm: vi.fn(),
13
15
  },
16
+ pathx: {
17
+ posix: (p: string) => p.replace(/\\/g, "/"),
18
+ posixResolve: (...args: string[]) => path.resolve(...args).replace(/\\/g, "/"),
19
+ },
14
20
  FsWatcher: {
15
21
  watch: vi.fn(() => Promise.resolve({
16
22
  onChange: mockOnChange,
@@ -22,9 +28,9 @@ vi.mock("@simplysm/core-node", () => ({
22
28
  const { fsx, FsWatcher } = await import("@simplysm/core-node");
23
29
  const { copySrcFiles, watchCopySrcFiles } = await import("../../src/utils/copy-src");
24
30
 
25
- const pkgDir = path.resolve("/workspace/packages/my-pkg");
26
- const srcDir = path.join(pkgDir, "src");
27
- const distDir = path.join(pkgDir, "dist");
31
+ const pkgDir = toPosix(path.resolve("/workspace/packages/my-pkg"));
32
+ const srcDir = toPosix(path.join(pkgDir, "src"));
33
+ const distDir = toPosix(path.join(pkgDir, "dist"));
28
34
 
29
35
  describe("copySrcFiles", () => {
30
36
  beforeEach(() => {
@@ -37,23 +43,23 @@ describe("copySrcFiles", () => {
37
43
 
38
44
  it("copies files matching glob patterns preserving relative paths", async () => {
39
45
  vi.mocked(fsx.glob).mockResolvedValue([
40
- path.join(srcDir, "styles", "app.css"),
46
+ toPosix(path.join(srcDir, "styles", "app.css")),
41
47
  ]);
42
48
 
43
49
  await copySrcFiles(pkgDir, ["**/*.css"]);
44
50
 
45
51
  expect(fsx.glob).toHaveBeenCalledWith("**/*.css", { cwd: srcDir, absolute: true });
46
- expect(fsx.mkdir).toHaveBeenCalledWith(path.join(distDir, "styles"));
52
+ expect(fsx.mkdir).toHaveBeenCalledWith(toPosix(path.join(distDir, "styles")));
47
53
  expect(fsx.copy).toHaveBeenCalledWith(
48
- path.join(srcDir, "styles", "app.css"),
49
- path.join(distDir, "styles", "app.css"),
54
+ toPosix(path.join(srcDir, "styles", "app.css")),
55
+ toPosix(path.join(distDir, "styles", "app.css")),
50
56
  );
51
57
  });
52
58
 
53
59
  it("handles multiple patterns", async () => {
54
60
  vi.mocked(fsx.glob)
55
- .mockResolvedValueOnce([path.join(srcDir, "a.css")])
56
- .mockResolvedValueOnce([path.join(srcDir, "b.json")]);
61
+ .mockResolvedValueOnce([toPosix(path.join(srcDir, "a.css"))])
62
+ .mockResolvedValueOnce([toPosix(path.join(srcDir, "b.json"))]);
57
63
 
58
64
  await copySrcFiles(pkgDir, ["**/*.css", "**/*.json"]);
59
65
 
@@ -87,7 +93,7 @@ describe("watchCopySrcFiles", () => {
87
93
  const watcher = await watchCopySrcFiles(pkgDir, ["**/*.css"]);
88
94
 
89
95
  expect(fsx.glob).toHaveBeenCalledWith("**/*.css", { cwd: srcDir, absolute: true });
90
- expect(FsWatcher.watch).toHaveBeenCalledWith([path.join(srcDir, "**/*.css")]);
96
+ expect(FsWatcher.watch).toHaveBeenCalledWith([toPosix(path.join(srcDir, "**/*.css"))]);
91
97
  expect(mockOnChange).toHaveBeenCalledWith({ delay: 300 }, expect.any(Function));
92
98
  expect(watcher).toBeDefined();
93
99
  });
@@ -101,13 +107,13 @@ describe("watchCopySrcFiles", () => {
101
107
  ) => Promise<void>;
102
108
 
103
109
  await onChangeCallback([
104
- { event: "change", path: path.join(srcDir, "styles", "app.css") },
110
+ { event: "change", path: toPosix(path.join(srcDir, "styles", "app.css")) },
105
111
  ]);
106
112
 
107
- expect(fsx.mkdir).toHaveBeenCalledWith(path.join(distDir, "styles"));
113
+ expect(fsx.mkdir).toHaveBeenCalledWith(toPosix(path.join(distDir, "styles")));
108
114
  expect(fsx.copy).toHaveBeenCalledWith(
109
- path.join(srcDir, "styles", "app.css"),
110
- path.join(distDir, "styles", "app.css"),
115
+ toPosix(path.join(srcDir, "styles", "app.css")),
116
+ toPosix(path.join(distDir, "styles", "app.css")),
111
117
  );
112
118
  });
113
119
 
@@ -119,10 +125,10 @@ describe("watchCopySrcFiles", () => {
119
125
  ) => Promise<void>;
120
126
 
121
127
  await onChangeCallback([
122
- { event: "unlink", path: path.join(srcDir, "styles", "old.css") },
128
+ { event: "unlink", path: toPosix(path.join(srcDir, "styles", "old.css")) },
123
129
  ]);
124
130
 
125
- expect(fsx.rm).toHaveBeenCalledWith(path.join(distDir, "styles", "old.css"));
131
+ expect(fsx.rm).toHaveBeenCalledWith(toPosix(path.join(distDir, "styles", "old.css")));
126
132
  });
127
133
 
128
134
  it("handles add event same as change event", async () => {
@@ -133,12 +139,12 @@ describe("watchCopySrcFiles", () => {
133
139
  ) => Promise<void>;
134
140
 
135
141
  await onChangeCallback([
136
- { event: "add", path: path.join(srcDir, "new.css") },
142
+ { event: "add", path: toPosix(path.join(srcDir, "new.css")) },
137
143
  ]);
138
144
 
139
145
  expect(fsx.copy).toHaveBeenCalledWith(
140
- path.join(srcDir, "new.css"),
141
- path.join(distDir, "new.css"),
146
+ toPosix(path.join(srcDir, "new.css")),
147
+ toPosix(path.join(distDir, "new.css")),
142
148
  );
143
149
  });
144
150
  });
@@ -0,0 +1,72 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isWorkspaceDiagnostic, formatDiagnosticError } from "../../src/utils/diagnostic-utils";
3
+
4
+ describe("isWorkspaceDiagnostic", () => {
5
+ it("includes diagnostic when file is within cwd", () => {
6
+ const diag = {
7
+ file: { fileName: "/workspace/src/index.ts" },
8
+ };
9
+ expect(isWorkspaceDiagnostic(diag as any, "/workspace")).toBe(true);
10
+ });
11
+
12
+ it("excludes diagnostic when file is in node_modules", () => {
13
+ const diag = {
14
+ file: { fileName: "/workspace/node_modules/dep/index.ts" },
15
+ };
16
+ expect(isWorkspaceDiagnostic(diag as any, "/workspace")).toBe(false);
17
+ });
18
+
19
+ it("includes diagnostic without file info (global diagnostic)", () => {
20
+ const diag = { file: null };
21
+ expect(isWorkspaceDiagnostic(diag as any, "/workspace")).toBe(true);
22
+ });
23
+
24
+ it("excludes diagnostic from outside cwd", () => {
25
+ const diag = {
26
+ file: { fileName: "/other-workspace/src/index.ts" },
27
+ };
28
+ expect(isWorkspaceDiagnostic(diag as any, "/workspace")).toBe(false);
29
+ });
30
+
31
+ // Key scenario: Windows backslash paths are normalized via posix()
32
+ it("normalizes Windows backslash paths for comparison", () => {
33
+ const diag = {
34
+ file: { fileName: "D:\\workspace\\src\\index.ts" },
35
+ };
36
+ expect(isWorkspaceDiagnostic(diag as any, "D:\\workspace")).toBe(true);
37
+ });
38
+
39
+ it("handles trailing slash in cwd", () => {
40
+ const diag = {
41
+ file: { fileName: "/workspace/src/index.ts" },
42
+ };
43
+ expect(isWorkspaceDiagnostic(diag as any, "/workspace/")).toBe(true);
44
+ });
45
+ });
46
+
47
+ describe("formatDiagnosticError", () => {
48
+ it("formats diagnostic with file info", () => {
49
+ const diag = {
50
+ file: {
51
+ fileName: "/workspace/src/index.ts",
52
+ getLineAndCharacterOfPosition: () => ({ line: 5, character: 10 }),
53
+ },
54
+ start: 100,
55
+ code: 2345,
56
+ messageText: "Type error",
57
+ };
58
+ const result = formatDiagnosticError(diag as any);
59
+ expect(result).toBe("/workspace/src/index.ts:6:11: TS2345: Type error");
60
+ });
61
+
62
+ it("formats diagnostic without file info", () => {
63
+ const diag = {
64
+ file: null,
65
+ start: null,
66
+ code: 1001,
67
+ messageText: "Global error",
68
+ };
69
+ const result = formatDiagnosticError(diag as any);
70
+ expect(result).toBe("TS1001: Global error");
71
+ });
72
+ });
@@ -119,9 +119,8 @@ describe("runNgtscBuild가 AngularCompiler를 사용한다", () => {
119
119
  expect(ngtscProgramSpy).not.toHaveBeenCalled();
120
120
 
121
121
  // 결과 구조 확인
122
- expect(result.js).toHaveProperty("success");
123
- expect(result.dts).toHaveProperty("success");
124
- expect(result.dts).toHaveProperty("diagnostics");
122
+ expect(result.build).toHaveProperty("success");
123
+ expect(result.build).toHaveProperty("diagnostics");
125
124
  });
126
125
 
127
126
  it("AngularCompiler 생성자에 transformStylesheet가 전달된다", async () => {