@simplysm/sd-cli 14.0.10 → 14.0.12

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 (266) hide show
  1. package/README.md +58 -253
  2. package/dist/angular/client-transform-stylesheet.js +1 -1
  3. package/dist/angular/client-transform-stylesheet.js.map +1 -1
  4. package/dist/angular/vite-angular-plugin.d.ts +1 -1
  5. package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
  6. package/dist/angular/vite-angular-plugin.js +60 -34
  7. package/dist/angular/vite-angular-plugin.js.map +1 -1
  8. package/dist/angular/vite-postcss-inline-plugin.d.ts +1 -1
  9. package/dist/angular/vite-postcss-inline-plugin.js +1 -1
  10. package/dist/capacitor/capacitor.d.ts +14 -2
  11. package/dist/capacitor/capacitor.d.ts.map +1 -1
  12. package/dist/capacitor/capacitor.js +131 -17
  13. package/dist/capacitor/capacitor.js.map +1 -1
  14. package/dist/commands/build.d.ts +3 -10
  15. package/dist/commands/build.d.ts.map +1 -1
  16. package/dist/commands/build.js +3 -10
  17. package/dist/commands/build.js.map +1 -1
  18. package/dist/commands/check.js +3 -3
  19. package/dist/commands/check.js.map +1 -1
  20. package/dist/commands/dev.d.ts +3 -9
  21. package/dist/commands/dev.d.ts.map +1 -1
  22. package/dist/commands/dev.js +3 -9
  23. package/dist/commands/dev.js.map +1 -1
  24. package/dist/commands/device.d.ts +13 -0
  25. package/dist/commands/device.d.ts.map +1 -0
  26. package/dist/commands/device.js +53 -0
  27. package/dist/commands/device.js.map +1 -0
  28. package/dist/commands/publish.d.ts +1 -1
  29. package/dist/commands/publish.d.ts.map +1 -1
  30. package/dist/commands/publish.js +18 -26
  31. package/dist/commands/publish.js.map +1 -1
  32. package/dist/commands/replace-deps.d.ts +3 -3
  33. package/dist/commands/replace-deps.d.ts.map +1 -1
  34. package/dist/commands/replace-deps.js +1 -1
  35. package/dist/commands/typecheck.d.ts +4 -3
  36. package/dist/commands/typecheck.d.ts.map +1 -1
  37. package/dist/commands/typecheck.js +5 -11
  38. package/dist/commands/typecheck.js.map +1 -1
  39. package/dist/commands/watch.d.ts +9 -9
  40. package/dist/commands/watch.js +9 -9
  41. package/dist/electron/electron.d.ts.map +1 -1
  42. package/dist/electron/electron.js +42 -3
  43. package/dist/electron/electron.js.map +1 -1
  44. package/dist/engines/BaseEngine.d.ts +1 -1
  45. package/dist/engines/BaseEngine.d.ts.map +1 -1
  46. package/dist/engines/BaseEngine.js +3 -1
  47. package/dist/engines/BaseEngine.js.map +1 -1
  48. package/dist/engines/NgtscEngine.d.ts +7 -7
  49. package/dist/engines/NgtscEngine.d.ts.map +1 -1
  50. package/dist/engines/NgtscEngine.js +3 -3
  51. package/dist/engines/ServerEsbuildEngine.d.ts +7 -7
  52. package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
  53. package/dist/engines/ServerEsbuildEngine.js +3 -3
  54. package/dist/engines/TscEngine.d.ts +7 -7
  55. package/dist/engines/TscEngine.d.ts.map +1 -1
  56. package/dist/engines/TscEngine.js +3 -3
  57. package/dist/engines/ViteEngine.d.ts +1 -1
  58. package/dist/engines/ViteEngine.d.ts.map +1 -1
  59. package/dist/engines/ViteEngine.js +7 -12
  60. package/dist/engines/ViteEngine.js.map +1 -1
  61. package/dist/engines/index.d.ts +5 -5
  62. package/dist/engines/index.js +5 -5
  63. package/dist/engines/types.d.ts +20 -20
  64. package/dist/engines/types.d.ts.map +1 -1
  65. package/dist/infra/ResultCollector.d.ts +9 -9
  66. package/dist/infra/ResultCollector.js +8 -8
  67. package/dist/infra/SignalHandler.d.ts +7 -7
  68. package/dist/infra/SignalHandler.js +7 -7
  69. package/dist/infra/WorkerManager.d.ts +14 -14
  70. package/dist/infra/WorkerManager.js +14 -14
  71. package/dist/orchestrators/BuildOrchestrator.d.ts +25 -25
  72. package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
  73. package/dist/orchestrators/BuildOrchestrator.js +29 -29
  74. package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
  75. package/dist/orchestrators/DevWatchOrchestrator.d.ts +7 -7
  76. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  77. package/dist/orchestrators/DevWatchOrchestrator.js +35 -57
  78. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  79. package/dist/sd-cli-entry.d.ts +2 -2
  80. package/dist/sd-cli-entry.d.ts.map +1 -1
  81. package/dist/sd-cli-entry.js +45 -9
  82. package/dist/sd-cli-entry.js.map +1 -1
  83. package/dist/sd-cli.d.ts +3 -3
  84. package/dist/sd-cli.js +16 -16
  85. package/dist/sd-cli.js.map +1 -1
  86. package/dist/sd-config.types.d.ts +105 -105
  87. package/dist/sd-config.types.d.ts.map +1 -1
  88. package/dist/utils/angular-compiler.js +5 -5
  89. package/dist/utils/angular-compiler.js.map +1 -1
  90. package/dist/utils/build-env.d.ts +1 -1
  91. package/dist/utils/build-env.js +1 -1
  92. package/dist/utils/concurrency.d.ts +7 -7
  93. package/dist/utils/concurrency.js +7 -7
  94. package/dist/utils/copy-public.d.ts +9 -9
  95. package/dist/utils/copy-public.js +17 -17
  96. package/dist/utils/copy-public.js.map +1 -1
  97. package/dist/utils/copy-src.d.ts +9 -9
  98. package/dist/utils/copy-src.js +11 -11
  99. package/dist/utils/copy-src.js.map +1 -1
  100. package/dist/utils/engine-stop.d.ts +8 -9
  101. package/dist/utils/engine-stop.d.ts.map +1 -1
  102. package/dist/utils/engine-stop.js +9 -10
  103. package/dist/utils/engine-stop.js.map +1 -1
  104. package/dist/utils/esbuild-config.d.ts +23 -23
  105. package/dist/utils/esbuild-config.d.ts.map +1 -1
  106. package/dist/utils/esbuild-config.js +25 -25
  107. package/dist/utils/esbuild-config.js.map +1 -1
  108. package/dist/utils/lint-with-program.d.ts +15 -15
  109. package/dist/utils/lint-with-program.d.ts.map +1 -1
  110. package/dist/utils/lint-with-program.js +29 -29
  111. package/dist/utils/lint-with-program.js.map +1 -1
  112. package/dist/utils/ngtsc-build-core.d.ts +8 -8
  113. package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
  114. package/dist/utils/ngtsc-build-core.js +14 -14
  115. package/dist/utils/ngtsc-build-core.js.map +1 -1
  116. package/dist/utils/output-path-rewriter.d.ts +14 -14
  117. package/dist/utils/output-path-rewriter.js +18 -18
  118. package/dist/utils/output-path-rewriter.js.map +1 -1
  119. package/dist/utils/output-utils.d.ts +6 -6
  120. package/dist/utils/output-utils.js +11 -11
  121. package/dist/utils/output-utils.js.map +1 -1
  122. package/dist/utils/package-utils.d.ts +21 -21
  123. package/dist/utils/package-utils.d.ts.map +1 -1
  124. package/dist/utils/package-utils.js +56 -45
  125. package/dist/utils/package-utils.js.map +1 -1
  126. package/dist/utils/replace-deps.d.ts +25 -25
  127. package/dist/utils/replace-deps.d.ts.map +1 -1
  128. package/dist/utils/replace-deps.js +84 -65
  129. package/dist/utils/replace-deps.js.map +1 -1
  130. package/dist/utils/sd-config.d.ts +3 -3
  131. package/dist/utils/sd-config.js +3 -3
  132. package/dist/utils/tsc-build.d.ts +13 -13
  133. package/dist/utils/tsc-build.d.ts.map +1 -1
  134. package/dist/utils/tsc-build.js +9 -9
  135. package/dist/utils/tsc-build.js.map +1 -1
  136. package/dist/utils/tsconfig.d.ts +11 -9
  137. package/dist/utils/tsconfig.d.ts.map +1 -1
  138. package/dist/utils/tsconfig.js +11 -9
  139. package/dist/utils/tsconfig.js.map +1 -1
  140. package/dist/utils/typecheck-non-package.d.ts +5 -6
  141. package/dist/utils/typecheck-non-package.d.ts.map +1 -1
  142. package/dist/utils/typecheck-non-package.js +7 -8
  143. package/dist/utils/typecheck-non-package.js.map +1 -1
  144. package/dist/utils/typecheck-serialization.d.ts +8 -8
  145. package/dist/utils/typecheck-serialization.d.ts.map +1 -1
  146. package/dist/utils/typecheck-serialization.js +12 -16
  147. package/dist/utils/typecheck-serialization.js.map +1 -1
  148. package/dist/utils/vite-config.d.ts +8 -5
  149. package/dist/utils/vite-config.d.ts.map +1 -1
  150. package/dist/utils/vite-config.js +36 -29
  151. package/dist/utils/vite-config.js.map +1 -1
  152. package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
  153. package/dist/utils/vite-scope-watch-plugin.js +1 -1
  154. package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
  155. package/dist/utils/worker-events.d.ts +12 -12
  156. package/dist/utils/worker-events.d.ts.map +1 -1
  157. package/dist/utils/worker-events.js +10 -10
  158. package/dist/utils/worker-events.js.map +1 -1
  159. package/dist/utils/worker-utils.d.ts +12 -13
  160. package/dist/utils/worker-utils.d.ts.map +1 -1
  161. package/dist/utils/worker-utils.js +12 -13
  162. package/dist/utils/worker-utils.js.map +1 -1
  163. package/dist/vitest-plugin.d.ts.map +1 -1
  164. package/dist/vitest-plugin.js +5 -7
  165. package/dist/vitest-plugin.js.map +1 -1
  166. package/dist/workers/client.worker.d.ts +4 -2
  167. package/dist/workers/client.worker.d.ts.map +1 -1
  168. package/dist/workers/client.worker.js +209 -1
  169. package/dist/workers/client.worker.js.map +1 -1
  170. package/dist/workers/library-build.worker.d.ts +1 -1
  171. package/dist/workers/library-build.worker.d.ts.map +1 -1
  172. package/dist/workers/library-build.worker.js +7 -7
  173. package/dist/workers/library-build.worker.js.map +1 -1
  174. package/dist/workers/lint.worker.d.ts +2 -2
  175. package/dist/workers/lint.worker.js +2 -2
  176. package/dist/workers/ngtsc-build.worker.js +30 -30
  177. package/dist/workers/ngtsc-build.worker.js.map +1 -1
  178. package/dist/workers/server-build.worker.d.ts +17 -17
  179. package/dist/workers/server-build.worker.d.ts.map +1 -1
  180. package/dist/workers/server-build.worker.js +46 -46
  181. package/dist/workers/server-build.worker.js.map +1 -1
  182. package/dist/workers/server-runtime.worker.d.ts +7 -7
  183. package/dist/workers/server-runtime.worker.d.ts.map +1 -1
  184. package/dist/workers/server-runtime.worker.js +17 -17
  185. package/dist/workers/server-runtime.worker.js.map +1 -1
  186. package/docs/config.md +340 -0
  187. package/docs/publish-configuration-types.md +87 -0
  188. package/docs/pwa-configuration-types.md +55 -0
  189. package/docs/vitest-plugin.md +47 -0
  190. package/package.json +9 -7
  191. package/src/angular/client-transform-stylesheet.ts +1 -1
  192. package/src/angular/vite-angular-plugin.ts +70 -37
  193. package/src/angular/vite-postcss-inline-plugin.ts +1 -1
  194. package/src/capacitor/capacitor.ts +159 -23
  195. package/src/commands/build.ts +3 -10
  196. package/src/commands/check.ts +3 -3
  197. package/src/commands/dev.ts +3 -9
  198. package/src/commands/device.ts +65 -0
  199. package/src/commands/publish.ts +30 -26
  200. package/src/commands/replace-deps.ts +3 -3
  201. package/src/commands/typecheck.ts +7 -13
  202. package/src/commands/watch.ts +9 -9
  203. package/src/electron/electron.ts +49 -4
  204. package/src/engines/BaseEngine.ts +4 -1
  205. package/src/engines/NgtscEngine.ts +7 -7
  206. package/src/engines/ServerEsbuildEngine.ts +7 -7
  207. package/src/engines/TscEngine.ts +7 -7
  208. package/src/engines/ViteEngine.ts +8 -13
  209. package/src/engines/index.ts +5 -5
  210. package/src/engines/types.ts +20 -20
  211. package/src/infra/ResultCollector.ts +9 -9
  212. package/src/infra/SignalHandler.ts +7 -7
  213. package/src/infra/WorkerManager.ts +14 -14
  214. package/src/orchestrators/BuildOrchestrator.ts +37 -37
  215. package/src/orchestrators/DevWatchOrchestrator.ts +37 -61
  216. package/src/sd-cli-entry.ts +51 -9
  217. package/src/sd-cli.ts +16 -16
  218. package/src/sd-config.types.ts +107 -107
  219. package/src/utils/angular-compiler.ts +5 -5
  220. package/src/utils/build-env.ts +1 -1
  221. package/src/utils/concurrency.ts +7 -7
  222. package/src/utils/copy-public.ts +17 -17
  223. package/src/utils/copy-src.ts +11 -11
  224. package/src/utils/engine-stop.ts +9 -10
  225. package/src/utils/esbuild-config.ts +29 -29
  226. package/src/utils/lint-with-program.ts +34 -34
  227. package/src/utils/ngtsc-build-core.ts +17 -17
  228. package/src/utils/output-path-rewriter.ts +18 -18
  229. package/src/utils/output-utils.ts +11 -11
  230. package/src/utils/package-utils.ts +57 -45
  231. package/src/utils/replace-deps.ts +92 -67
  232. package/src/utils/sd-config.ts +3 -3
  233. package/src/utils/tsc-build.ts +18 -18
  234. package/src/utils/tsconfig.ts +11 -9
  235. package/src/utils/typecheck-non-package.ts +7 -8
  236. package/src/utils/typecheck-serialization.ts +13 -15
  237. package/src/utils/vite-config.ts +45 -35
  238. package/src/utils/vite-scope-watch-plugin.ts +6 -1
  239. package/src/utils/worker-events.ts +16 -16
  240. package/src/utils/worker-utils.ts +12 -13
  241. package/src/vitest-plugin.ts +5 -8
  242. package/src/workers/client.worker.ts +236 -2
  243. package/src/workers/library-build.worker.ts +8 -8
  244. package/src/workers/lint.worker.ts +2 -2
  245. package/src/workers/ngtsc-build.worker.ts +31 -31
  246. package/src/workers/server-build.worker.ts +60 -60
  247. package/src/workers/server-runtime.worker.ts +22 -22
  248. package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +1 -0
  249. package/tests/angular/vite-angular-plugin-hmr.spec.ts +78 -0
  250. package/tests/angular/vite-angular-plugin.spec.ts +67 -0
  251. package/tests/capacitor/capacitor-build.spec.ts +6 -4
  252. package/tests/capacitor/capacitor-icon.spec.ts +7 -5
  253. package/tests/capacitor/capacitor-init.spec.ts +120 -10
  254. package/tests/capacitor/capacitor-run.spec.ts +14 -17
  255. package/tests/capacitor/capacitor-workspace.spec.ts +5 -3
  256. package/tests/commands/check.spec.ts +2 -2
  257. package/tests/commands/device.spec.ts +147 -0
  258. package/tests/commands/publish.spec.ts +2 -2
  259. package/tests/commands/typecheck.spec.ts +8 -0
  260. package/tests/electron/electron.spec.ts +12 -10
  261. package/tests/engines/base-engine.spec.ts +37 -0
  262. package/tests/engines/vite-engine.spec.ts +115 -3
  263. package/tests/orchestrators/dev-watch-orchestrator.spec.ts +21 -93
  264. package/tests/utils/vite-config.spec.ts +144 -90
  265. package/tests/workers/client-worker.spec.ts +690 -0
  266. package/tests/workers/server-build-worker.spec.ts +3 -3
@@ -1373,8 +1373,8 @@ describe("DevWatchOrchestrator", () => {
1373
1373
  });
1374
1374
  }
1375
1375
 
1376
- // Acceptance: Scenario "Capacitor 디바이스 실행"
1377
- it("runs Capacitor.create + initialize + run(devServerUrl) after ViteDevServer starts", async () => {
1376
+ // Acceptance: Scenario "Capacitor 초기화만 수행 (run 미호출)"
1377
+ it("runs Capacitor.create + initialize but NOT run in dev mode", async () => {
1378
1378
  setupDefaults(createConfig({
1379
1379
  packages: {
1380
1380
  "my-client": {
@@ -1396,11 +1396,11 @@ describe("DevWatchOrchestrator", () => {
1396
1396
  undefined,
1397
1397
  );
1398
1398
  expect(mockCapacitorInstance.initialize).toHaveBeenCalled();
1399
- expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://localhost:4200");
1399
+ expect(mockCapacitorInstance.run).not.toHaveBeenCalled();
1400
1400
  });
1401
1401
 
1402
- // Acceptance: Scenario "Electron 데스크톱 실행"
1403
- it("runs Electron.create + initialize + run(devServerUrl) after ViteDevServer starts", async () => {
1402
+ // Acceptance: Scenario "dev 모드에서 Electron 미처리"
1403
+ it("does not run Electron in dev mode even when electron config exists", async () => {
1404
1404
  setupDefaults(createConfig({
1405
1405
  packages: {
1406
1406
  "my-client": {
@@ -1416,17 +1416,13 @@ describe("DevWatchOrchestrator", () => {
1416
1416
  await orchestrator.initialize();
1417
1417
  await orchestrator.start();
1418
1418
 
1419
- expect(Electron.create).toHaveBeenCalledWith(
1420
- expect.stringContaining("my-client"),
1421
- { appId: "com.test.electron" },
1422
- undefined,
1423
- );
1424
- expect(mockElectronInstance.initialize).toHaveBeenCalled();
1425
- expect(mockElectronInstance.run).toHaveBeenCalledWith("http://localhost:4200");
1419
+ expect(Electron.create).not.toHaveBeenCalled();
1420
+ expect(mockElectronInstance.initialize).not.toHaveBeenCalled();
1421
+ expect(mockElectronInstance.run).not.toHaveBeenCalled();
1426
1422
  });
1427
1423
 
1428
- // Acceptance: Scenario "Capacitor + Electron 동시 실행"
1429
- it("runs both Capacitor.run and Electron.run when both are configured", async () => {
1424
+ // Acceptance: Scenario "Capacitor + Electron 동시 설정 시 Capacitor만 초기화"
1425
+ it("initializes Capacitor but ignores Electron when both are configured", async () => {
1430
1426
  setupDefaults(createConfig({
1431
1427
  packages: {
1432
1428
  "my-client": {
@@ -1444,9 +1440,9 @@ describe("DevWatchOrchestrator", () => {
1444
1440
  await orchestrator.start();
1445
1441
 
1446
1442
  expect(Capacitor.create).toHaveBeenCalled();
1447
- expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://localhost:4200");
1448
- expect(Electron.create).toHaveBeenCalled();
1449
- expect(mockElectronInstance.run).toHaveBeenCalledWith("http://localhost:4200");
1443
+ expect(mockCapacitorInstance.initialize).toHaveBeenCalled();
1444
+ expect(mockCapacitorInstance.run).not.toHaveBeenCalled();
1445
+ expect(Electron.create).not.toHaveBeenCalled();
1450
1446
  });
1451
1447
 
1452
1448
  // Acceptance: Scenario "네이티브 설정 없는 dev 모드"
@@ -1466,11 +1462,9 @@ describe("DevWatchOrchestrator", () => {
1466
1462
  expect(Electron.create).not.toHaveBeenCalled();
1467
1463
  });
1468
1464
 
1469
- // Acceptance: Scenario "dev 모드 종료 시 네��티브 프로세스 정리"
1470
- // Electron.run() blocks until SIGINT/SIGTERM — cleanup is handled by its internal signal handler.
1471
- // Capacitor.run() returns after deployment no cleanup needed.
1472
- // This scenario verifies that shutdown() completes without error when native apps are running.
1473
- it("shutdown completes when native apps were launched", async () => {
1465
+ // Acceptance: Scenario "dev 모드 종료 시 Capacitor 초기화 후 정상 종료"
1466
+ // Capacitor.initialize() 호출되므로 별도 정리 불필요.
1467
+ it("shutdown completes when Capacitor was initialized", async () => {
1474
1468
  setupDefaults(createConfig({
1475
1469
  packages: {
1476
1470
  "my-client": {
@@ -1493,8 +1487,8 @@ describe("DevWatchOrchestrator", () => {
1493
1487
  }
1494
1488
  });
1495
1489
 
1496
- // Unit: native run error does not crash orchestrator
1497
- it("logs error but does not crash when Capacitor.run throws", async () => {
1490
+ // Unit: Capacitor.initialize 에러는 로그만 남기고 크래시하지 않음
1491
+ it("logs error but does not crash when Capacitor.initialize throws", async () => {
1498
1492
  setupDefaults(createConfig({
1499
1493
  packages: {
1500
1494
  "my-client": {
@@ -1505,81 +1499,15 @@ describe("DevWatchOrchestrator", () => {
1505
1499
  },
1506
1500
  }));
1507
1501
  setupEngineWithPort(4200);
1508
- mockCapacitorInstance.run.mockRejectedValue(new Error("device not found"));
1502
+ mockCapacitorInstance.initialize.mockRejectedValue(new Error("init failed"));
1509
1503
 
1510
1504
  const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1511
1505
  await orchestrator.initialize();
1512
1506
  // Should not throw
1513
1507
  await orchestrator.start();
1514
1508
 
1515
- expect(mockCapacitorInstance.run).toHaveBeenCalled();
1516
- });
1517
-
1518
- // Unit: Electron.run is fire-and-forget (does not block dev mode start)
1519
- it("does not await Electron.run (fire-and-forget)", async () => {
1520
- let electronRunResolved = false;
1521
- mockElectronInstance.run.mockImplementation(() => {
1522
- return new Promise<void>((resolve) => {
1523
- // Simulate long-running Electron process — never resolves during test
1524
- setTimeout(() => {
1525
- electronRunResolved = true;
1526
- resolve();
1527
- }, 10_000);
1528
- });
1529
- });
1530
-
1531
- setupDefaults(createConfig({
1532
- packages: {
1533
- "my-client": {
1534
- target: "client",
1535
- server: 4200,
1536
- electron: { appId: "com.test.electron" },
1537
- },
1538
- },
1539
- }));
1540
- setupEngineWithPort(4200);
1541
-
1542
- const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1543
- await orchestrator.initialize();
1544
- await orchestrator.start();
1545
-
1546
- // start() returned without waiting for Electron.run to resolve
1547
- expect(electronRunResolved).toBe(false);
1548
- expect(mockElectronInstance.run).toHaveBeenCalled();
1549
- });
1550
-
1551
- // Unit: DESIGN-004 — Electron run failure registers error in ResultCollector
1552
- it("registers error in ResultCollector when Electron.run fails", async () => {
1553
- mockElectronInstance.run.mockRejectedValue(new Error("electron crashed"));
1554
-
1555
- setupDefaults(createConfig({
1556
- packages: {
1557
- "my-client": {
1558
- target: "client",
1559
- server: 4200,
1560
- electron: { appId: "com.test.electron" },
1561
- },
1562
- },
1563
- }));
1564
- setupEngineWithPort(4200);
1565
-
1566
- const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
1567
- await orchestrator.initialize();
1568
- await orchestrator.start();
1569
-
1570
- // Wait a tick for the fire-and-forget async to settle
1571
- await new Promise((resolve) => setTimeout(resolve, 50));
1572
-
1573
- const resultCollector = vi.mocked(ResultCollector).mock.instances[0];
1574
- expect(resultCollector.add).toHaveBeenCalledWith(
1575
- expect.objectContaining({
1576
- name: "my-client",
1577
- target: "client",
1578
- type: "build",
1579
- status: "error",
1580
- message: expect.stringContaining("electron crashed"),
1581
- }),
1582
- );
1509
+ expect(mockCapacitorInstance.initialize).toHaveBeenCalled();
1510
+ expect(mockCapacitorInstance.run).not.toHaveBeenCalled();
1583
1511
  });
1584
1512
  });
1585
1513
 
@@ -221,8 +221,8 @@ describe("createClientViteConfig", () => {
221
221
 
222
222
  // --- legacyModule (Feature 1.1) ---
223
223
 
224
- // Acceptance: Scenario "legacyModule 활성화한다"
225
- it("enables inlineDynamicImports and import.meta plugin when legacyModule is true", async () => {
224
+ // Acceptance: Scenario "legacyModule 활성화 시 inlineDynamicImports만 설정한다"
225
+ it("enables inlineDynamicImports without import.meta plugin when legacyModule is true", async () => {
226
226
  const config = await createClientViteConfig({
227
227
  ...createDefaultOptions(),
228
228
  legacyModule: true,
@@ -230,26 +230,22 @@ describe("createClientViteConfig", () => {
230
230
 
231
231
  // inlineDynamicImports가 활성화된다
232
232
  expect((config.build as any)?.rollupOptions?.output?.inlineDynamicImports).toBe(true);
233
- // import.meta 치환 플러그인이 활성화된다
233
+ // import.meta 치환 플러그인이 없다 (esbuild target이 자동 치환)
234
234
  const plugins = config.plugins as Array<{ name: string }>;
235
235
  const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
236
- expect(legacyPlugin).toBeDefined();
236
+ expect(legacyPlugin).toBeUndefined();
237
237
  });
238
238
 
239
- // Acceptance: Scenario "legacyModule 설정하지 않는다"
240
- it("does not set inlineDynamicImports or import.meta plugin when legacyModule is not set", async () => {
239
+ // Acceptance: Scenario "legacyModule 미설정 시 코드 분할이 기본 동작한다"
240
+ it("does not set inlineDynamicImports when legacyModule is not set", async () => {
241
241
  const config = await createClientViteConfig(createDefaultOptions());
242
242
 
243
243
  // 코드 분할이 기본 동작한다
244
244
  expect(config.build?.rollupOptions).toBeUndefined();
245
- // import.meta가 그대로 유지된다
246
- const plugins = config.plugins as Array<{ name: string }>;
247
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
248
- expect(legacyPlugin).toBeUndefined();
249
245
  });
250
246
 
251
- // Acceptance: Scenario "기존 splitting 옵션을 legacyModule로 마이그레이션한다"
252
- it("legacyModule: true provides inlineDynamicImports plus import.meta plugin (splitting replacement)", async () => {
247
+ // Acceptance: Scenario "legacyModule: true는 inlineDynamicImports를 활성화한다"
248
+ it("legacyModule: true provides inlineDynamicImports (splitting replacement)", async () => {
253
249
  const config = await createClientViteConfig({
254
250
  ...createDefaultOptions(),
255
251
  legacyModule: true,
@@ -257,95 +253,32 @@ describe("createClientViteConfig", () => {
257
253
 
258
254
  // 기존 splitting: false와 동일한 inlineDynamicImports 동작
259
255
  expect((config.build as any)?.rollupOptions?.output?.inlineDynamicImports).toBe(true);
260
- // 추가로 import.meta 치환이 활성화된다
261
- const plugins = config.plugins as Array<{ name: string }>;
262
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
263
- expect(legacyPlugin).toBeDefined();
264
- });
265
-
266
- // Unit: enforce: "post" 설정 확인 (D4)
267
- it("sd-legacy-import-meta plugin has enforce: post", async () => {
268
- const config = await createClientViteConfig({
269
- ...createDefaultOptions(),
270
- legacyModule: true,
271
- });
272
-
273
- const plugins = config.plugins as Array<{ name: string; enforce?: string }>;
274
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
275
- expect(legacyPlugin?.enforce).toBe("post");
276
256
  });
277
257
 
278
- // Acceptance: Scenario "사용자 코드의 import.meta.url을 치환한다"
279
- it("sd-legacy-import-meta plugin replaces import.meta.url with module URL", async () => {
280
- const config = await createClientViteConfig({
281
- ...createDefaultOptions(),
282
- legacyModule: true,
283
- });
284
-
285
- const plugins = config.plugins as Array<{ name: string; enforce?: string; transform?: Function }>;
286
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
287
- expect(legacyPlugin).toBeDefined();
288
-
289
- const code = 'const url = import.meta.url;';
290
- const id = "/packages/my-client/src/app.ts";
291
- const result = legacyPlugin!.transform!(code, id);
258
+ // --- legacyModule esbuild.supported override (Feature 1.4) ---
292
259
 
293
- expect(result).toBeDefined();
294
- expect(result.code).toContain(JSON.stringify(id));
295
- expect(result.code).not.toContain("import.meta");
296
- });
297
-
298
- // Acceptance: Scenario "Vite가 주입한 import.meta.hot을 치환한다"
299
- it("sd-legacy-import-meta plugin replaces import.meta.hot injected by Vite", async () => {
260
+ // Acceptance: Scenario "legacyModule: true일 때 esbuild.supported에 import-meta/dynamic-import false 설정"
261
+ it("sets esbuild.supported to disable import-meta and dynamic-import when legacyModule is true", async () => {
300
262
  const config = await createClientViteConfig({
301
263
  ...createDefaultOptions(),
302
264
  legacyModule: true,
303
265
  });
304
266
 
305
- const plugins = config.plugins as Array<{ name: string; transform?: Function }>;
306
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
307
-
308
- const code = 'import.meta.hot = createHotContext("/src/app.ts");';
309
- const id = "/packages/my-client/src/app.ts";
310
- const result = legacyPlugin!.transform!(code, id);
311
-
312
- expect(result).toBeDefined();
313
- expect(result.code).not.toContain("import.meta");
314
- });
315
-
316
- // Acceptance: Scenario "/@vite/client의 import.meta를 치환한다"
317
- it("sd-legacy-import-meta plugin replaces import.meta in /@vite/client", async () => {
318
- const config = await createClientViteConfig({
319
- ...createDefaultOptions(),
320
- legacyModule: true,
321
- });
322
-
323
- const plugins = config.plugins as Array<{ name: string; transform?: Function }>;
324
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
325
-
326
- const code = 'const base = import.meta.url;';
327
- const id = "/@vite/client";
328
- const result = legacyPlugin!.transform!(code, id);
329
-
330
- expect(result).toBeDefined();
331
- expect(result.code).not.toContain("import.meta");
267
+ const esbuildOpts = config.esbuild as Record<string, unknown> | undefined;
268
+ expect(esbuildOpts?.["supported"]).toEqual(
269
+ expect.objectContaining({
270
+ "import-meta": false,
271
+ "dynamic-import": false,
272
+ }),
273
+ );
332
274
  });
333
275
 
334
- // Acceptance: Scenario "import.meta가 없는 모듈은 변환하지 않는다"
335
- it("sd-legacy-import-meta plugin returns undefined for modules without import.meta", async () => {
336
- const config = await createClientViteConfig({
337
- ...createDefaultOptions(),
338
- legacyModule: true,
339
- });
340
-
341
- const plugins = config.plugins as Array<{ name: string; transform?: Function }>;
342
- const legacyPlugin = plugins.find((p) => p.name === "sd-legacy-import-meta");
343
-
344
- const code = 'const x = 1 + 2;';
345
- const id = "/packages/my-client/src/utils.ts";
346
- const result = legacyPlugin!.transform!(code, id);
276
+ // Acceptance: Scenario "legacyModule 미설정 esbuild.supported 변경 없음"
277
+ it("does not set esbuild.supported when legacyModule is not specified", async () => {
278
+ const config = await createClientViteConfig(createDefaultOptions());
347
279
 
348
- expect(result).toBeUndefined();
280
+ const esbuildOpts = config.esbuild as Record<string, unknown> | undefined;
281
+ expect(esbuildOpts?.["supported"]).toBeUndefined();
349
282
  });
350
283
 
351
284
  // --- PWA (Feature 5.2) ---
@@ -505,6 +438,127 @@ describe("createClientViteConfig", () => {
505
438
  );
506
439
  });
507
440
 
441
+ // --- watch option (Feature 1.2: legacy dev mode) ---
442
+
443
+ // Acceptance: Scenario "watch: true 시 build.watch 설정 및 emptyOutDir: false"
444
+ it("sets build.watch and emptyOutDir: false when watch is true in build mode", async () => {
445
+ const config = await createClientViteConfig({
446
+ ...createDefaultOptions(),
447
+ mode: "build",
448
+ watch: true,
449
+ });
450
+
451
+ expect(config.build?.watch).toEqual({});
452
+ expect(config.build?.emptyOutDir).toBe(false);
453
+ expect(config.logLevel).toBeUndefined();
454
+ });
455
+
456
+ // Acceptance: Scenario "watch: true + replaceDeps 시 sdScopeWatchPlugin 포함"
457
+ it("includes sdScopeWatchPlugin when watch is true with replaceDeps in build mode", async () => {
458
+ const { sdScopeWatchPlugin } = await import("../../src/utils/vite-scope-watch-plugin");
459
+
460
+ const config = await createClientViteConfig({
461
+ ...createDefaultOptions(),
462
+ mode: "build",
463
+ watch: true,
464
+ replaceDeps: [{ packageName: "@scope/core", sourcePath: "/packages/core" }],
465
+ });
466
+
467
+ const plugins = config.plugins as Array<{ name: string }>;
468
+ const scopePlugin = plugins.find((p) => p.name === "sd-scope-watch-plugin");
469
+ expect(scopePlugin).toBeDefined();
470
+ expect(sdScopeWatchPlugin).toHaveBeenCalled();
471
+ });
472
+
473
+ // Acceptance: Scenario "watch 미설정 시 기존 build 동작 유지"
474
+ it("sets emptyOutDir: true and logLevel: silent when watch is not set in build mode", async () => {
475
+ const config = await createClientViteConfig(createDefaultOptions());
476
+
477
+ expect(config.build?.emptyOutDir).toBe(true);
478
+ expect(config.logLevel).toBe("silent");
479
+ expect(config.build?.watch).toBeUndefined();
480
+ });
481
+
482
+ // Unit: watch: true without replaceDeps does not add sdScopeWatchPlugin
483
+ it("does not add sdScopeWatchPlugin in watch mode without replaceDeps", async () => {
484
+ const config = await createClientViteConfig({
485
+ ...createDefaultOptions(),
486
+ mode: "build",
487
+ watch: true,
488
+ });
489
+
490
+ const plugins = config.plugins as Array<{ name: string }>;
491
+ const scopePlugin = plugins.find((p) => p.name === "sd-scope-watch-plugin");
492
+ expect(scopePlugin).toBeUndefined();
493
+ });
494
+
495
+ // Unit: watch: true still sets outDir
496
+ it("sets outDir in watch mode", async () => {
497
+ const config = await createClientViteConfig({
498
+ ...createDefaultOptions(),
499
+ mode: "build",
500
+ watch: true,
501
+ });
502
+
503
+ expect(config.build?.outDir).toContain("my-client");
504
+ expect(config.build?.outDir).toMatch(/dist$/);
505
+ });
506
+
507
+ // --- exclude (Feature 1.1: vite-exclude-passthrough) ---
508
+
509
+ // Acceptance: Scenario "exclude에 패키지를 지정하면 pre-bundling에서 제외된다"
510
+ it("sets optimizeDeps.exclude when exclude is provided", async () => {
511
+ const config = await createClientViteConfig({
512
+ ...createDefaultOptions(),
513
+ mode: "dev",
514
+ exclude: ["jeep-sqlite"],
515
+ });
516
+
517
+ expect(config.optimizeDeps?.exclude).toEqual(["jeep-sqlite"]);
518
+ });
519
+
520
+ // Acceptance: Scenario "exclude 미설정 시 기존 동작과 동일하다"
521
+ it("does not set optimizeDeps.exclude when exclude is not provided", async () => {
522
+ const config = await createClientViteConfig({
523
+ ...createDefaultOptions(),
524
+ mode: "dev",
525
+ });
526
+
527
+ expect(config.optimizeDeps?.exclude).toBeUndefined();
528
+ });
529
+
530
+ // Acceptance: Scenario "exclude와 replaceDeps가 모두 있으면 둘 다 제외된다"
531
+ it("sets optimizeDeps.exclude from exclude while sdScopeWatchPlugin handles replaceDeps", async () => {
532
+ const { sdScopeWatchPlugin } = await import("../../src/utils/vite-scope-watch-plugin");
533
+
534
+ const config = await createClientViteConfig({
535
+ ...createDefaultOptions(),
536
+ mode: "dev",
537
+ exclude: ["jeep-sqlite"],
538
+ replaceDeps: [{ packageName: "@scope/core", sourcePath: "/packages/core" }],
539
+ });
540
+
541
+ // Base config에 exclude 설정
542
+ expect(config.optimizeDeps?.exclude).toEqual(["jeep-sqlite"]);
543
+ // sdScopeWatchPlugin도 호출됨 (replaceDeps용 exclude는 plugin이 처리)
544
+ expect(sdScopeWatchPlugin).toHaveBeenCalled();
545
+ });
546
+
547
+ // Acceptance: Scenario "exclude만 있고 replaceDeps가 없으면 exclude만 제외된다"
548
+ it("sets optimizeDeps.exclude from exclude when no replaceDeps", async () => {
549
+ const config = await createClientViteConfig({
550
+ ...createDefaultOptions(),
551
+ mode: "dev",
552
+ exclude: ["jeep-sqlite"],
553
+ });
554
+
555
+ expect(config.optimizeDeps?.exclude).toEqual(["jeep-sqlite"]);
556
+ // sdScopeWatchPlugin은 호출되지 않음
557
+ const plugins = config.plugins as Array<{ name: string }>;
558
+ const scopePlugin = plugins.find((p) => p.name === "sd-scope-watch-plugin");
559
+ expect(scopePlugin).toBeUndefined();
560
+ });
561
+
508
562
  // Acceptance: Scenario "pwa 필드 미설정 시 기본값"
509
563
  it("uses default manifest values from pkgName when pwa is undefined", async () => {
510
564
  await createClientViteConfig(createDefaultOptions());