@simplysm/sd-cli 13.0.100 → 14.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/build.js +29 -19
- package/dist/commands/build.js.map +1 -6
- package/dist/commands/check.d.ts +1 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +130 -115
- package/dist/commands/check.js.map +1 -6
- package/dist/commands/dev.d.ts +6 -7
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +24 -14
- package/dist/commands/dev.js.map +1 -6
- package/dist/commands/lint.d.ts +1 -1
- package/dist/commands/lint.js +158 -116
- package/dist/commands/lint.js.map +1 -6
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +637 -510
- package/dist/commands/publish.js.map +1 -6
- package/dist/commands/replace-deps.js +12 -12
- package/dist/commands/replace-deps.js.map +1 -6
- package/dist/commands/typecheck.d.ts +5 -30
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +144 -207
- package/dist/commands/typecheck.js.map +1 -6
- package/dist/commands/watch.d.ts +6 -4
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +25 -16
- package/dist/commands/watch.js.map +1 -6
- package/dist/engines/NgtscEngine.d.ts +47 -0
- package/dist/engines/NgtscEngine.d.ts.map +1 -0
- package/dist/engines/NgtscEngine.js +151 -0
- package/dist/engines/NgtscEngine.js.map +1 -0
- package/dist/engines/ServerEsbuildEngine.d.ts +47 -0
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -0
- package/dist/engines/ServerEsbuildEngine.js +159 -0
- package/dist/engines/ServerEsbuildEngine.js.map +1 -0
- package/dist/engines/TscEngine.d.ts +47 -0
- package/dist/engines/TscEngine.d.ts.map +1 -0
- package/dist/engines/TscEngine.js +153 -0
- package/dist/engines/TscEngine.js.map +1 -0
- package/dist/engines/ViteEngine.d.ts +49 -0
- package/dist/engines/ViteEngine.d.ts.map +1 -0
- package/dist/engines/ViteEngine.js +161 -0
- package/dist/engines/ViteEngine.js.map +1 -0
- package/dist/engines/index.d.ts +26 -0
- package/dist/engines/index.d.ts.map +1 -0
- package/dist/engines/index.js +30 -0
- package/dist/engines/index.js.map +1 -0
- package/dist/engines/types.d.ts +77 -0
- package/dist/engines/types.d.ts.map +1 -0
- package/dist/engines/types.js +2 -0
- package/dist/engines/types.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -6
- package/dist/infra/ResultCollector.d.ts +1 -1
- package/dist/infra/ResultCollector.d.ts.map +1 -1
- package/dist/infra/ResultCollector.js +30 -27
- package/dist/infra/ResultCollector.js.map +1 -6
- package/dist/infra/SignalHandler.js +45 -42
- package/dist/infra/SignalHandler.js.map +1 -6
- package/dist/infra/WorkerManager.js +56 -53
- package/dist/infra/WorkerManager.js.map +1 -6
- package/dist/orchestrators/BuildOrchestrator.d.ts +33 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +314 -309
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -6
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +60 -0
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -0
- package/dist/orchestrators/DevWatchOrchestrator.js +465 -0
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -0
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +190 -266
- package/dist/sd-cli-entry.js.map +1 -6
- package/dist/sd-cli.js +77 -49
- package/dist/sd-cli.js.map +1 -6
- package/dist/sd-config.types.d.ts +2 -0
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/sd-config.types.js +2 -1
- package/dist/sd-config.types.js.map +1 -6
- package/dist/utils/angular-build.d.ts +77 -0
- package/dist/utils/angular-build.d.ts.map +1 -0
- package/dist/utils/angular-build.js +84 -0
- package/dist/utils/angular-build.js.map +1 -0
- package/dist/utils/build-env.js +9 -9
- package/dist/utils/build-env.js.map +1 -6
- package/dist/utils/concurrency.d.ts +15 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +38 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/copy-public.js +104 -87
- package/dist/utils/copy-public.js.map +1 -6
- package/dist/utils/copy-src.js +49 -35
- package/dist/utils/copy-src.js.map +1 -6
- package/dist/utils/esbuild-config.d.ts +0 -29
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +151 -218
- package/dist/utils/esbuild-config.js.map +1 -6
- package/dist/utils/ngtsc-build-core.d.ts +49 -0
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -0
- package/dist/utils/ngtsc-build-core.js +250 -0
- package/dist/utils/ngtsc-build-core.js.map +1 -0
- package/dist/utils/output-path-rewriter.d.ts +23 -0
- package/dist/utils/output-path-rewriter.d.ts.map +1 -0
- package/dist/utils/output-path-rewriter.js +74 -0
- package/dist/utils/output-path-rewriter.js.map +1 -0
- package/dist/utils/output-utils.js +55 -40
- package/dist/utils/output-utils.js.map +1 -6
- package/dist/utils/package-utils.d.ts +8 -0
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +103 -73
- package/dist/utils/package-utils.js.map +1 -6
- package/dist/utils/rebuild-manager.js +41 -44
- package/dist/utils/rebuild-manager.js.map +1 -6
- package/dist/utils/replace-deps.js +283 -184
- package/dist/utils/replace-deps.js.map +1 -6
- package/dist/utils/scss-compiler.d.ts +10 -0
- package/dist/utils/scss-compiler.d.ts.map +1 -0
- package/dist/utils/scss-compiler.js +36 -0
- package/dist/utils/scss-compiler.js.map +1 -0
- package/dist/utils/sd-config.js +29 -19
- package/dist/utils/sd-config.js.map +1 -6
- package/dist/utils/tsc-build.d.ts +36 -0
- package/dist/utils/tsc-build.d.ts.map +1 -0
- package/dist/utils/tsc-build.js +130 -0
- package/dist/utils/tsc-build.js.map +1 -0
- package/dist/utils/tsconfig.d.ts +7 -26
- package/dist/utils/tsconfig.d.ts.map +1 -1
- package/dist/utils/tsconfig.js +39 -64
- package/dist/utils/tsconfig.js.map +1 -6
- package/dist/utils/typecheck-non-package.d.ts +18 -0
- package/dist/utils/typecheck-non-package.d.ts.map +1 -0
- package/dist/utils/typecheck-non-package.js +64 -0
- package/dist/utils/typecheck-non-package.js.map +1 -0
- package/dist/utils/typecheck-serialization.js +58 -40
- package/dist/utils/typecheck-serialization.js.map +1 -6
- package/dist/utils/worker-events.js +48 -40
- package/dist/utils/worker-events.js.map +1 -6
- package/dist/utils/worker-utils.js +48 -28
- package/dist/utils/worker-utils.js.map +1 -6
- package/dist/vitest-plugin.d.ts +9 -0
- package/dist/vitest-plugin.d.ts.map +1 -0
- package/dist/vitest-plugin.js +85 -0
- package/dist/vitest-plugin.js.map +1 -0
- package/dist/workers/library-build.worker.d.ts +54 -0
- package/dist/workers/library-build.worker.d.ts.map +1 -0
- package/dist/workers/library-build.worker.js +97 -0
- package/dist/workers/library-build.worker.js.map +1 -0
- package/dist/workers/lint.worker.js +9 -6
- package/dist/workers/lint.worker.js.map +1 -6
- package/dist/workers/ngtsc-build.worker.d.ts +23 -0
- package/dist/workers/ngtsc-build.worker.d.ts.map +1 -0
- package/dist/workers/ngtsc-build.worker.js +98 -0
- package/dist/workers/ngtsc-build.worker.js.map +1 -0
- package/dist/workers/{server.worker.d.ts → server-build.worker.d.ts} +39 -29
- package/dist/workers/server-build.worker.d.ts.map +1 -0
- package/dist/workers/server-build.worker.js +399 -0
- package/dist/workers/server-build.worker.js.map +1 -0
- package/dist/workers/server-runtime.worker.d.ts +3 -2
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +100 -95
- package/dist/workers/server-runtime.worker.js.map +1 -6
- package/dist/workers/vite-build.worker.d.ts +56 -0
- package/dist/workers/vite-build.worker.d.ts.map +1 -0
- package/dist/workers/vite-build.worker.js +167 -0
- package/dist/workers/vite-build.worker.js.map +1 -0
- package/package.json +10 -16
- package/src/commands/check.ts +21 -3
- package/src/commands/dev.ts +10 -8
- package/src/commands/lint.ts +1 -1
- package/src/commands/publish.ts +4 -0
- package/src/commands/typecheck.ts +89 -256
- package/src/commands/watch.ts +9 -8
- package/src/engines/NgtscEngine.ts +190 -0
- package/src/engines/ServerEsbuildEngine.ts +195 -0
- package/src/engines/TscEngine.ts +189 -0
- package/src/engines/ViteEngine.ts +203 -0
- package/src/engines/index.ts +49 -0
- package/src/engines/types.ts +79 -0
- package/src/index.ts +0 -3
- package/src/infra/ResultCollector.ts +1 -1
- package/src/orchestrators/BuildOrchestrator.ts +87 -157
- package/src/orchestrators/DevWatchOrchestrator.ts +573 -0
- package/src/sd-cli-entry.ts +13 -116
- package/src/sd-config.types.ts +2 -0
- package/src/utils/angular-build.ts +157 -0
- package/src/utils/concurrency.ts +43 -0
- package/src/utils/esbuild-config.ts +1 -122
- package/src/utils/ngtsc-build-core.ts +379 -0
- package/src/utils/output-path-rewriter.ts +82 -0
- package/src/utils/package-utils.ts +20 -0
- package/src/utils/scss-compiler.ts +58 -0
- package/src/utils/tsc-build.ts +175 -0
- package/src/utils/tsconfig.ts +27 -95
- package/src/utils/typecheck-non-package.ts +87 -0
- package/src/vitest-plugin.ts +118 -0
- package/src/workers/library-build.worker.ts +153 -0
- package/src/workers/ngtsc-build.worker.ts +146 -0
- package/src/workers/server-build.worker.ts +565 -0
- package/src/workers/server-runtime.worker.ts +17 -26
- package/src/workers/vite-build.worker.ts +252 -0
- package/tests/commands/check.spec.ts +276 -0
- package/tests/commands/dev.spec.ts +53 -0
- package/tests/commands/lint.spec.ts +243 -0
- package/tests/commands/publish.spec.ts +1159 -0
- package/tests/commands/typecheck.spec.ts +294 -0
- package/tests/commands/watch.spec.ts +53 -0
- package/tests/engines/engine-selection.spec.ts +247 -0
- package/tests/engines/ngtsc-engine.spec.ts +274 -0
- package/tests/engines/server-esbuild-engine.spec.ts +256 -0
- package/tests/engines/tsc-engine.spec.ts +213 -0
- package/tests/engines/vite-engine.spec.ts +358 -0
- package/tests/infra/result-collector.spec.ts +46 -0
- package/tests/infra/signal-handler.spec.ts +32 -0
- package/tests/infra/worker-manager.spec.ts +63 -0
- package/tests/orchestrators/build-orchestrator.spec.ts +772 -0
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +1173 -0
- package/tests/sd-cli-entry.spec.ts +49 -0
- package/tests/utils/angular-build.spec.ts +251 -0
- package/tests/utils/build-env.spec.ts +33 -0
- package/tests/utils/concurrency.spec.ts +65 -0
- package/tests/utils/copy-src.spec.ts +144 -0
- package/tests/utils/esbuild-config.spec.ts +186 -0
- package/tests/utils/external-modules.spec.ts +161 -0
- package/tests/utils/ngtsc-scss-refactor.spec.ts +66 -0
- package/tests/utils/output-path-rewriter.spec.ts +165 -0
- package/tests/utils/output-utils.spec.ts +104 -0
- package/tests/utils/package-utils.spec.ts +52 -0
- package/tests/utils/rebuild-manager.spec.ts +30 -27
- package/tests/utils/replace-deps.spec.ts +69 -0
- package/tests/utils/scss-compiler.spec.ts +131 -0
- package/tests/utils/sd-config.spec.ts +77 -0
- package/tests/utils/tsc-build.spec.ts +358 -0
- package/tests/utils/tsconfig-angular.spec.ts +71 -0
- package/tests/utils/typecheck-non-package.spec.ts +120 -0
- package/tests/utils/worker-events.spec.ts +155 -0
- package/tests/utils/worker-utils.spec.ts +43 -0
- package/tests/vitest-plugin-cwd.spec.ts +68 -0
- package/tests/vitest-plugin.spec.ts +103 -0
- package/tests/workers/library-build-worker.spec.ts +258 -0
- package/tests/workers/ngtsc-build-worker.spec.ts +187 -0
- package/tests/workers/server-build-worker.spec.ts +566 -0
- package/tests/workers/server-runtime-worker.spec.ts +251 -0
- package/README.md +0 -295
- package/dist/builders/BaseBuilder.d.ts +0 -88
- package/dist/builders/BaseBuilder.d.ts.map +0 -1
- package/dist/builders/BaseBuilder.js +0 -142
- package/dist/builders/BaseBuilder.js.map +0 -6
- package/dist/builders/DtsBuilder.d.ts +0 -22
- package/dist/builders/DtsBuilder.d.ts.map +0 -1
- package/dist/builders/DtsBuilder.js +0 -72
- package/dist/builders/DtsBuilder.js.map +0 -6
- package/dist/builders/LibraryBuilder.d.ts +0 -22
- package/dist/builders/LibraryBuilder.d.ts.map +0 -1
- package/dist/builders/LibraryBuilder.js +0 -85
- package/dist/builders/LibraryBuilder.js.map +0 -6
- package/dist/builders/types.d.ts +0 -55
- package/dist/builders/types.d.ts.map +0 -1
- package/dist/builders/types.js +0 -1
- package/dist/builders/types.js.map +0 -6
- package/dist/capacitor/capacitor.d.ts +0 -151
- package/dist/capacitor/capacitor.d.ts.map +0 -1
- package/dist/capacitor/capacitor.js +0 -694
- package/dist/capacitor/capacitor.js.map +0 -6
- package/dist/commands/device.d.ts +0 -22
- package/dist/commands/device.d.ts.map +0 -1
- package/dist/commands/device.js +0 -98
- package/dist/commands/device.js.map +0 -6
- package/dist/commands/init.d.ts +0 -14
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -72
- package/dist/commands/init.js.map +0 -6
- package/dist/electron/electron.d.ts +0 -84
- package/dist/electron/electron.d.ts.map +0 -1
- package/dist/electron/electron.js +0 -263
- package/dist/electron/electron.js.map +0 -6
- package/dist/orchestrators/DevOrchestrator.d.ts +0 -83
- package/dist/orchestrators/DevOrchestrator.d.ts.map +0 -1
- package/dist/orchestrators/DevOrchestrator.js +0 -540
- package/dist/orchestrators/DevOrchestrator.js.map +0 -6
- package/dist/orchestrators/WatchOrchestrator.d.ts +0 -57
- package/dist/orchestrators/WatchOrchestrator.d.ts.map +0 -1
- package/dist/orchestrators/WatchOrchestrator.js +0 -199
- package/dist/orchestrators/WatchOrchestrator.js.map +0 -6
- package/dist/utils/tailwind-config-deps.d.ts +0 -8
- package/dist/utils/tailwind-config-deps.d.ts.map +0 -1
- package/dist/utils/tailwind-config-deps.js +0 -82
- package/dist/utils/tailwind-config-deps.js.map +0 -6
- package/dist/utils/template.d.ts +0 -14
- package/dist/utils/template.d.ts.map +0 -1
- package/dist/utils/template.js +0 -33
- package/dist/utils/template.js.map +0 -6
- package/dist/utils/vite-config.d.ts +0 -35
- package/dist/utils/vite-config.d.ts.map +0 -1
- package/dist/utils/vite-config.js +0 -259
- package/dist/utils/vite-config.js.map +0 -6
- package/dist/workers/client.worker.d.ts +0 -83
- package/dist/workers/client.worker.d.ts.map +0 -1
- package/dist/workers/client.worker.js +0 -111
- package/dist/workers/client.worker.js.map +0 -6
- package/dist/workers/dts.worker.d.ts +0 -75
- package/dist/workers/dts.worker.d.ts.map +0 -1
- package/dist/workers/dts.worker.js +0 -270
- package/dist/workers/dts.worker.js.map +0 -6
- package/dist/workers/library.worker.d.ts +0 -75
- package/dist/workers/library.worker.d.ts.map +0 -1
- package/dist/workers/library.worker.js +0 -166
- package/dist/workers/library.worker.js.map +0 -6
- package/dist/workers/server.worker.d.ts.map +0 -1
- package/dist/workers/server.worker.js +0 -482
- package/dist/workers/server.worker.js.map +0 -6
- package/src/builders/BaseBuilder.ts +0 -218
- package/src/builders/DtsBuilder.ts +0 -92
- package/src/builders/LibraryBuilder.ts +0 -110
- package/src/builders/types.ts +0 -60
- package/src/capacitor/capacitor.ts +0 -931
- package/src/commands/device.ts +0 -140
- package/src/commands/init.ts +0 -113
- package/src/electron/electron.ts +0 -362
- package/src/orchestrators/DevOrchestrator.ts +0 -744
- package/src/orchestrators/WatchOrchestrator.ts +0 -277
- package/src/utils/tailwind-config-deps.ts +0 -98
- package/src/utils/template.ts +0 -56
- package/src/utils/vite-config.ts +0 -390
- package/src/workers/client.worker.ts +0 -250
- package/src/workers/dts.worker.ts +0 -453
- package/src/workers/library.worker.ts +0 -316
- package/src/workers/server.worker.ts +0 -734
- package/templates/init/.gitignore.hbs +0 -34
- package/templates/init/.npmrc.hbs +0 -1
- package/templates/init/.prettierignore +0 -1
- package/templates/init/.prettierrc.yaml +0 -12
- package/templates/init/eslint.config.ts +0 -15
- package/templates/init/mise.toml +0 -3
- package/templates/init/package.json.hbs +0 -32
- package/templates/init/packages/client-admin/index.html.hbs +0 -144
- package/templates/init/packages/client-admin/package.json.hbs +0 -27
- package/templates/init/packages/client-admin/public/assets/logo-landscape.png +0 -0
- package/templates/init/packages/client-admin/public/assets/logo.png +0 -0
- package/templates/init/packages/client-admin/public/favicon.ico +0 -0
- package/templates/init/packages/client-admin/src/App.tsx +0 -42
- package/templates/init/packages/client-admin/src/dev/DevDialog.tsx +0 -34
- package/templates/init/packages/client-admin/src/events/AuthChangeEvent.ts +0 -3
- package/templates/init/packages/client-admin/src/main.css +0 -4
- package/templates/init/packages/client-admin/src/main.tsx.hbs +0 -146
- package/templates/init/packages/client-admin/src/providers/AppServiceProvider.tsx.hbs +0 -103
- package/templates/init/packages/client-admin/src/providers/AppStructureProvider.tsx +0 -84
- package/templates/init/packages/client-admin/src/providers/AuthProvider.tsx.hbs +0 -96
- package/templates/init/packages/client-admin/src/providers/configureSharedData.ts.hbs +0 -67
- package/templates/init/packages/client-admin/src/views/auth/LoginView.tsx +0 -132
- package/templates/init/packages/client-admin/src/views/home/HomeView.tsx +0 -108
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +0 -243
- package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeSheet.tsx.hbs +0 -271
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +0 -146
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +0 -121
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +0 -52
- package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleSheet.tsx.hbs +0 -125
- package/templates/init/packages/client-admin/src/views/home/main/MainView.tsx.hbs +0 -13
- package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +0 -241
- package/templates/init/packages/client-admin/src/views/home/system/system-log/SystemLogSheet.tsx.hbs +0 -169
- package/templates/init/packages/client-admin/src/views/not-found/NotFoundView.tsx +0 -15
- package/templates/init/packages/client-admin/tailwind.config.ts +0 -10
- package/templates/init/packages/db-main/package.json.hbs +0 -13
- package/templates/init/packages/db-main/src/MainDbContext.ts +0 -22
- package/templates/init/packages/db-main/src/dataLogExt.ts +0 -127
- package/templates/init/packages/db-main/src/index.ts +0 -14
- package/templates/init/packages/db-main/src/tables/base/Employee.ts +0 -24
- package/templates/init/packages/db-main/src/tables/base/EmployeeConfig.ts +0 -13
- package/templates/init/packages/db-main/src/tables/base/Role.ts +0 -9
- package/templates/init/packages/db-main/src/tables/base/RolePermission.ts +0 -13
- package/templates/init/packages/db-main/src/tables/system/_DataLog.ts +0 -19
- package/templates/init/packages/db-main/src/tables/system/_Log.ts +0 -16
- package/templates/init/packages/server/package.json.hbs +0 -20
- package/templates/init/packages/server/public-dev/dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/templates/init/packages/server/src/index.ts +0 -4
- package/templates/init/packages/server/src/main.ts.hbs +0 -34
- package/templates/init/packages/server/src/services/AuthService.ts.hbs +0 -171
- package/templates/init/packages/server/src/services/DevService.ts.hbs +0 -94
- package/templates/init/packages/server/src/services/EmployeeService.ts.hbs +0 -122
- package/templates/init/packages/server/src/services/RoleService.ts.hbs +0 -59
- package/templates/init/pnpm-workspace.yaml +0 -15
- package/templates/init/sd.config.ts.hbs +0 -48
- package/templates/init/tsconfig.json.hbs +0 -39
- package/templates/init/vitest.config.ts +0 -36
- package/tests/capacitor-exclude.spec.ts +0 -78
- package/tests/capacitor.spec.ts +0 -49
- package/tests/copy-src.spec.ts +0 -50
- package/tests/electron-exclude.spec.ts +0 -61
- package/tests/get-compiler-options-for-package.spec.ts +0 -80
- package/tests/get-package-source-files.spec.ts +0 -139
- package/tests/get-types-from-package-json.spec.ts +0 -92
- package/tests/infra/ResultCollector.spec.ts +0 -30
- package/tests/infra/SignalHandler.spec.ts +0 -38
- package/tests/infra/WorkerManager.spec.ts +0 -63
- package/tests/load-ignore-patterns.spec.ts +0 -163
- package/tests/load-sd-config.spec.ts +0 -100
- package/tests/package-utils.spec.ts +0 -188
- package/tests/parse-root-tsconfig.spec.ts +0 -89
- package/tests/publish-config-narrowing.spec.ts +0 -20
- package/tests/replace-deps.spec.ts +0 -308
- package/tests/run-lint.spec.ts +0 -366
- package/tests/run-typecheck.spec.ts +0 -544
- package/tests/run-watch.spec.ts +0 -76
- package/tests/sd-cli.spec.ts +0 -265
- package/tests/sd-public-dev-plugin-mime.spec.ts +0 -19
- package/tests/tailwind-config-deps.spec.ts +0 -30
- package/tests/template.spec.ts +0 -70
- package/tests/vite-config-exclude.spec.ts +0 -35
- package/tests/vite-config-outdir.spec.ts +0 -38
- package/tests/write-changed-output-files.spec.ts +0 -97
|
@@ -0,0 +1,1173 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
// --- Mock factories (vi.mock is hoisted) ---
|
|
4
|
+
|
|
5
|
+
vi.mock("consola", () => ({
|
|
6
|
+
consola: {
|
|
7
|
+
withTag: vi.fn(() => ({
|
|
8
|
+
debug: vi.fn(),
|
|
9
|
+
info: vi.fn(),
|
|
10
|
+
warn: vi.fn(),
|
|
11
|
+
error: vi.fn(),
|
|
12
|
+
start: vi.fn(),
|
|
13
|
+
success: vi.fn(),
|
|
14
|
+
})),
|
|
15
|
+
},
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
vi.mock("../../src/utils/sd-config", () => ({
|
|
19
|
+
loadSdConfig: vi.fn(),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock("../../src/utils/build-env", () => ({
|
|
23
|
+
getVersion: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("../../src/utils/package-utils", () => ({
|
|
27
|
+
filterPackagesByTargets: vi.fn(),
|
|
28
|
+
validateTargets: vi.fn(),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock("../../src/utils/replace-deps", () => ({
|
|
32
|
+
watchReplaceDeps: vi.fn(),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock("../../src/utils/output-utils", () => ({
|
|
36
|
+
printErrors: vi.fn(),
|
|
37
|
+
printServers: vi.fn(),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
vi.mock("../../src/utils/rebuild-manager", () => ({
|
|
41
|
+
RebuildManager: vi.fn().mockImplementation(function (this: any) {
|
|
42
|
+
this.on = vi.fn();
|
|
43
|
+
this.registerBuild = vi.fn().mockReturnValue(vi.fn());
|
|
44
|
+
}),
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
vi.mock("../../src/infra/ResultCollector", () => ({
|
|
48
|
+
ResultCollector: vi.fn().mockImplementation(function (this: any) {
|
|
49
|
+
const store = new Map<string, any>();
|
|
50
|
+
this.add = vi.fn((result: any) => {
|
|
51
|
+
store.set(`${result.name}:${result.type}`, result);
|
|
52
|
+
});
|
|
53
|
+
this.get = vi.fn((key: string) => store.get(key));
|
|
54
|
+
this.toMap = vi.fn(() => new Map(store));
|
|
55
|
+
}),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
vi.mock("../../src/infra/SignalHandler", () => ({
|
|
59
|
+
SignalHandler: vi.fn().mockImplementation(function (this: any) {
|
|
60
|
+
this.waitForTermination = vi.fn().mockResolvedValue(undefined);
|
|
61
|
+
this.isTerminated = vi.fn().mockReturnValue(false);
|
|
62
|
+
this.requestTermination = vi.fn();
|
|
63
|
+
}),
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
vi.mock("../../src/utils/copy-src", () => ({
|
|
67
|
+
watchCopySrcFiles: vi.fn().mockResolvedValue({
|
|
68
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
69
|
+
}),
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
vi.mock("@simplysm/core-common", () => ({
|
|
73
|
+
err: {
|
|
74
|
+
message: vi.fn((e: any) => (e instanceof Error ? e.message : String(e))),
|
|
75
|
+
},
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Engine mock — tracks created engines and the package they were created for
|
|
79
|
+
const mockBuildEngines: Array<{
|
|
80
|
+
run: ReturnType<typeof vi.fn>;
|
|
81
|
+
startWatch: ReturnType<typeof vi.fn>;
|
|
82
|
+
stop: ReturnType<typeof vi.fn>;
|
|
83
|
+
_pkgName: string;
|
|
84
|
+
}> = [];
|
|
85
|
+
|
|
86
|
+
vi.mock("../../src/engines/index", () => ({
|
|
87
|
+
createBuildEngine: vi.fn((pkg: any, _options: any) => {
|
|
88
|
+
const engine = {
|
|
89
|
+
run: vi.fn().mockResolvedValue({
|
|
90
|
+
success: true,
|
|
91
|
+
js: { success: true, errors: [], warnings: [] },
|
|
92
|
+
dts: { success: true, errors: [], warnings: [], diagnostics: [] },
|
|
93
|
+
}),
|
|
94
|
+
startWatch: vi.fn().mockResolvedValue(undefined),
|
|
95
|
+
stop: vi.fn().mockResolvedValue(undefined),
|
|
96
|
+
_pkgName: pkg.name,
|
|
97
|
+
};
|
|
98
|
+
mockBuildEngines.push(engine);
|
|
99
|
+
return engine;
|
|
100
|
+
}),
|
|
101
|
+
}));
|
|
102
|
+
|
|
103
|
+
vi.mock("@simplysm/core-node", () => ({
|
|
104
|
+
FsWatcher: {
|
|
105
|
+
watch: vi.fn().mockResolvedValue({
|
|
106
|
+
onChange: vi.fn(),
|
|
107
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
Worker: {
|
|
111
|
+
create: vi.fn(),
|
|
112
|
+
},
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
vi.mock("child_process", () => ({
|
|
116
|
+
spawn: vi.fn(() => ({
|
|
117
|
+
on: vi.fn(),
|
|
118
|
+
kill: vi.fn(),
|
|
119
|
+
exitCode: 0,
|
|
120
|
+
})),
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
// --- Dynamic imports after mocking ---
|
|
124
|
+
|
|
125
|
+
const { DevWatchOrchestrator } = await import("../../src/orchestrators/DevWatchOrchestrator");
|
|
126
|
+
const { loadSdConfig } = await import("../../src/utils/sd-config");
|
|
127
|
+
const { filterPackagesByTargets, validateTargets } = await import("../../src/utils/package-utils");
|
|
128
|
+
const { watchReplaceDeps } = await import("../../src/utils/replace-deps");
|
|
129
|
+
const { printErrors, printServers: _printServers } = await import("../../src/utils/output-utils");
|
|
130
|
+
const { createBuildEngine } = await import("../../src/engines/index");
|
|
131
|
+
const { ResultCollector } = await import("../../src/infra/ResultCollector");
|
|
132
|
+
const { RebuildManager } = await import("../../src/utils/rebuild-manager");
|
|
133
|
+
const { watchCopySrcFiles } = await import("../../src/utils/copy-src");
|
|
134
|
+
const { FsWatcher, Worker } = await import("@simplysm/core-node");
|
|
135
|
+
const { spawn } = await import("child_process");
|
|
136
|
+
const { getVersion } = await import("../../src/utils/build-env");
|
|
137
|
+
|
|
138
|
+
import type { SdConfig } from "../../src/sd-config.types";
|
|
139
|
+
|
|
140
|
+
// --- Runtime worker mock helper ---
|
|
141
|
+
|
|
142
|
+
interface MockRuntimeProxy {
|
|
143
|
+
on: ReturnType<typeof vi.fn>;
|
|
144
|
+
start: ReturnType<typeof vi.fn>;
|
|
145
|
+
terminate: ReturnType<typeof vi.fn>;
|
|
146
|
+
emit: (event: string, data?: unknown) => void;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function createMockRuntimeProxy(): MockRuntimeProxy {
|
|
150
|
+
const handlers = new Map<string, (data: unknown) => void>();
|
|
151
|
+
return {
|
|
152
|
+
on: vi.fn((event: string, handler: (data: unknown) => void) => {
|
|
153
|
+
handlers.set(event, handler);
|
|
154
|
+
}),
|
|
155
|
+
start: vi.fn().mockResolvedValue(undefined),
|
|
156
|
+
terminate: vi.fn().mockResolvedValue(undefined),
|
|
157
|
+
emit(event: string, data?: unknown) {
|
|
158
|
+
handlers.get(event)?.(data);
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let mockRuntimeProxies: MockRuntimeProxy[];
|
|
164
|
+
|
|
165
|
+
// --- Helpers ---
|
|
166
|
+
|
|
167
|
+
function createConfig(overrides: Partial<SdConfig> = {}): SdConfig {
|
|
168
|
+
return { packages: {}, ...overrides };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function setupDefaults(config: SdConfig): void {
|
|
172
|
+
vi.mocked(loadSdConfig).mockResolvedValue(config);
|
|
173
|
+
vi.mocked(filterPackagesByTargets).mockImplementation((pkgs) => {
|
|
174
|
+
const result: Record<string, any> = {};
|
|
175
|
+
for (const [k, v] of Object.entries(pkgs)) {
|
|
176
|
+
if (v != null) result[k] = v;
|
|
177
|
+
}
|
|
178
|
+
return result;
|
|
179
|
+
});
|
|
180
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: vi.fn() });
|
|
181
|
+
vi.mocked(getVersion).mockResolvedValue("1.0.0");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// --- Tests ---
|
|
185
|
+
|
|
186
|
+
describe("DevWatchOrchestrator", () => {
|
|
187
|
+
beforeEach(() => {
|
|
188
|
+
vi.clearAllMocks();
|
|
189
|
+
mockBuildEngines.length = 0;
|
|
190
|
+
mockRuntimeProxies = [];
|
|
191
|
+
vi.spyOn(process, "cwd").mockReturnValue("/test-root");
|
|
192
|
+
vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
193
|
+
vi.mocked(Worker.create).mockImplementation(() => {
|
|
194
|
+
const proxy = createMockRuntimeProxy();
|
|
195
|
+
mockRuntimeProxies.push(proxy);
|
|
196
|
+
return proxy as any;
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe("Slice 1: watch mode", () => {
|
|
201
|
+
// --- Unit Tests ---
|
|
202
|
+
|
|
203
|
+
it("creates BuildEngine for library packages with correct output flags", async () => {
|
|
204
|
+
setupDefaults(createConfig({
|
|
205
|
+
packages: { "core-common": { target: "node" }, "core-browser": { target: "browser" } },
|
|
206
|
+
}));
|
|
207
|
+
|
|
208
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
209
|
+
await orchestrator.initialize();
|
|
210
|
+
|
|
211
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(2);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("does not create BuildEngine for server packages in watch mode", async () => {
|
|
215
|
+
setupDefaults(createConfig({
|
|
216
|
+
packages: { "service-server": { target: "server" } },
|
|
217
|
+
}));
|
|
218
|
+
|
|
219
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
220
|
+
await orchestrator.initialize();
|
|
221
|
+
|
|
222
|
+
expect(createBuildEngine).not.toHaveBeenCalled();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("outputs warning when no watchable packages exist", async () => {
|
|
226
|
+
setupDefaults(createConfig({
|
|
227
|
+
packages: { "sd-scripts": { target: "scripts" } },
|
|
228
|
+
}));
|
|
229
|
+
|
|
230
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
231
|
+
await orchestrator.initialize();
|
|
232
|
+
|
|
233
|
+
expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("No packages"));
|
|
234
|
+
expect(createBuildEngine).not.toHaveBeenCalled();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// --- Acceptance: Scenario 1 — watch 모드는 Library + Scripts만 대상 (server 제외) ---
|
|
238
|
+
it("processes Library and Scripts packages in watch mode, excludes server", async () => {
|
|
239
|
+
setupDefaults(createConfig({
|
|
240
|
+
packages: {
|
|
241
|
+
"core-common": { target: "node" },
|
|
242
|
+
"service-server": { target: "server" },
|
|
243
|
+
"sd-scripts": { target: "scripts", watch: { target: ["dist/**/*.js"], cmd: "node" } },
|
|
244
|
+
},
|
|
245
|
+
}));
|
|
246
|
+
|
|
247
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
248
|
+
await orchestrator.initialize();
|
|
249
|
+
await orchestrator.start();
|
|
250
|
+
|
|
251
|
+
// Library only = 1 engine (server excluded)
|
|
252
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(1);
|
|
253
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
254
|
+
expect.objectContaining({ name: "core-common" }),
|
|
255
|
+
expect.any(Object),
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
// Library engine: startWatch with js+dts
|
|
259
|
+
const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
|
|
260
|
+
expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
|
|
261
|
+
|
|
262
|
+
// Scripts+watch: spawn called for hook
|
|
263
|
+
expect(spawn).toHaveBeenCalled();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// --- Acceptance: Scenario 2 — 통합 Orchestrator가 공통 인프라를 사용한다 ---
|
|
267
|
+
it("creates ResultCollector, RebuildManager, SignalHandler on initialize", async () => {
|
|
268
|
+
setupDefaults(createConfig({
|
|
269
|
+
packages: { "core-common": { target: "node" } },
|
|
270
|
+
}));
|
|
271
|
+
|
|
272
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
273
|
+
await orchestrator.initialize();
|
|
274
|
+
|
|
275
|
+
expect(ResultCollector).toHaveBeenCalledTimes(1);
|
|
276
|
+
expect(RebuildManager).toHaveBeenCalledTimes(1);
|
|
277
|
+
|
|
278
|
+
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
279
|
+
expect(rebuildInstance.on).toHaveBeenCalledWith("batchComplete", expect.any(Function));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// --- Acceptance: Scenario 3 — Library 패키지 watch ---
|
|
283
|
+
it("starts library engine with js+dts output", async () => {
|
|
284
|
+
setupDefaults(createConfig({
|
|
285
|
+
packages: {
|
|
286
|
+
"core-common": { target: "node" },
|
|
287
|
+
"core-browser": { target: "browser" },
|
|
288
|
+
"storage": { target: "neutral" },
|
|
289
|
+
},
|
|
290
|
+
}));
|
|
291
|
+
|
|
292
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
293
|
+
await orchestrator.initialize();
|
|
294
|
+
await orchestrator.start();
|
|
295
|
+
|
|
296
|
+
for (const engine of mockBuildEngines) {
|
|
297
|
+
expect(engine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// --- Acceptance: Scenario 4 — Server 패키지는 watch에서 제외 ---
|
|
302
|
+
it("excludes server packages from watch mode entirely", async () => {
|
|
303
|
+
setupDefaults(createConfig({
|
|
304
|
+
packages: {
|
|
305
|
+
"core-common": { target: "node" },
|
|
306
|
+
"service-server": { target: "server" },
|
|
307
|
+
},
|
|
308
|
+
}));
|
|
309
|
+
|
|
310
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
311
|
+
await orchestrator.initialize();
|
|
312
|
+
await orchestrator.start();
|
|
313
|
+
|
|
314
|
+
// Only library engine created, no server engine
|
|
315
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(1);
|
|
316
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
317
|
+
expect.objectContaining({ name: "core-common" }),
|
|
318
|
+
expect.any(Object),
|
|
319
|
+
);
|
|
320
|
+
expect(Worker.create).not.toHaveBeenCalled();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// --- Acceptance: Scenario 5 — Scripts+watch 패키지 hook 실행 ---
|
|
324
|
+
it("runs initial hook and starts file watcher for scripts+watch packages", async () => {
|
|
325
|
+
setupDefaults(createConfig({
|
|
326
|
+
packages: {
|
|
327
|
+
"sd-scripts": {
|
|
328
|
+
target: "scripts",
|
|
329
|
+
watch: { target: ["dist/**/*.js"], cmd: "node", args: ["run-task.js"] },
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
}));
|
|
333
|
+
|
|
334
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
335
|
+
await orchestrator.initialize();
|
|
336
|
+
await orchestrator.start();
|
|
337
|
+
|
|
338
|
+
expect(spawn).toHaveBeenCalledWith("node", ["run-task.js"], expect.objectContaining({ shell: true }));
|
|
339
|
+
expect(FsWatcher.watch).toHaveBeenCalled();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Unit: file change re-runs hook
|
|
343
|
+
it("re-runs watch hook when watched files change", async () => {
|
|
344
|
+
let onChangeCallback: (...args: any[]) => void = () => {};
|
|
345
|
+
vi.mocked(FsWatcher.watch).mockResolvedValue({
|
|
346
|
+
onChange: vi.fn((_opts: any, cb: any) => { onChangeCallback = cb; }),
|
|
347
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
348
|
+
} as any);
|
|
349
|
+
|
|
350
|
+
setupDefaults(createConfig({
|
|
351
|
+
packages: {
|
|
352
|
+
"sd-scripts": {
|
|
353
|
+
target: "scripts",
|
|
354
|
+
watch: { target: ["dist/**/*.js"], cmd: "node", args: ["run-task.js"] },
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
}));
|
|
358
|
+
|
|
359
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
360
|
+
await orchestrator.initialize();
|
|
361
|
+
await orchestrator.start();
|
|
362
|
+
|
|
363
|
+
expect(spawn).toHaveBeenCalledTimes(1);
|
|
364
|
+
onChangeCallback();
|
|
365
|
+
expect(spawn).toHaveBeenCalledTimes(2);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// --- Acceptance: Scenario 6 — watch에서 copySrc 파일 감시 ---
|
|
369
|
+
it("starts copySrc watcher when library package has copySrc config", async () => {
|
|
370
|
+
setupDefaults(createConfig({
|
|
371
|
+
packages: {
|
|
372
|
+
"core-common": { target: "node", copySrc: ["**/*.json"] },
|
|
373
|
+
},
|
|
374
|
+
}));
|
|
375
|
+
|
|
376
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
377
|
+
await orchestrator.initialize();
|
|
378
|
+
await orchestrator.start();
|
|
379
|
+
|
|
380
|
+
expect(watchCopySrcFiles).toHaveBeenCalledWith(
|
|
381
|
+
expect.stringContaining("core-common"),
|
|
382
|
+
["**/*.json"],
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// Unit: no copySrc watcher when config absent
|
|
387
|
+
it("does not start copySrc watcher when config is absent", async () => {
|
|
388
|
+
setupDefaults(createConfig({
|
|
389
|
+
packages: { "core-common": { target: "node" } },
|
|
390
|
+
}));
|
|
391
|
+
|
|
392
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
393
|
+
await orchestrator.initialize();
|
|
394
|
+
await orchestrator.start();
|
|
395
|
+
|
|
396
|
+
expect(watchCopySrcFiles).not.toHaveBeenCalled();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// --- Acceptance: Library 패키지에 watch hook 설정 시 빌드 엔진 + hook 동시 실행 ---
|
|
400
|
+
it("runs both build engine and watch hook for library package with watch config", async () => {
|
|
401
|
+
setupDefaults(createConfig({
|
|
402
|
+
packages: {
|
|
403
|
+
"core-common": {
|
|
404
|
+
target: "node",
|
|
405
|
+
watch: { target: ["scripts/**/*.mjs"], cmd: "node", args: ["sync.mjs"] },
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
}));
|
|
409
|
+
|
|
410
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
411
|
+
await orchestrator.initialize();
|
|
412
|
+
await orchestrator.start();
|
|
413
|
+
|
|
414
|
+
// Build engine created and started
|
|
415
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(1);
|
|
416
|
+
const libraryEngine = mockBuildEngines.find((e) => e._pkgName === "core-common")!;
|
|
417
|
+
expect(libraryEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: true });
|
|
418
|
+
|
|
419
|
+
// Watch hook also executed
|
|
420
|
+
expect(spawn).toHaveBeenCalledWith("node", ["sync.mjs"], expect.objectContaining({ shell: true }));
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// --- Unit: Library 패키지에 watch hook 없으면 hook 미실행 ---
|
|
424
|
+
it("does not run watch hook for library package without watch config", async () => {
|
|
425
|
+
setupDefaults(createConfig({
|
|
426
|
+
packages: { "core-common": { target: "node" } },
|
|
427
|
+
}));
|
|
428
|
+
|
|
429
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
430
|
+
await orchestrator.initialize();
|
|
431
|
+
await orchestrator.start();
|
|
432
|
+
|
|
433
|
+
// Build engine created
|
|
434
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(1);
|
|
435
|
+
// No hook
|
|
436
|
+
expect(spawn).not.toHaveBeenCalled();
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// --- Acceptance: Scenario 7 — watch에서 replaceDeps 감시 ---
|
|
440
|
+
it("starts replaceDeps watcher when config exists", async () => {
|
|
441
|
+
const replaceDeps = { "@simplysm/*": "packages/*/src" };
|
|
442
|
+
setupDefaults(createConfig({
|
|
443
|
+
packages: { "core-common": { target: "node" } },
|
|
444
|
+
replaceDeps,
|
|
445
|
+
}));
|
|
446
|
+
|
|
447
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
448
|
+
await orchestrator.initialize();
|
|
449
|
+
|
|
450
|
+
expect(watchReplaceDeps).toHaveBeenCalledWith("/test-root", replaceDeps);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Unit: no replaceDeps watcher when config absent
|
|
454
|
+
it("does not start replaceDeps watcher when config is absent", async () => {
|
|
455
|
+
setupDefaults(createConfig({
|
|
456
|
+
packages: { "core-common": { target: "node" } },
|
|
457
|
+
}));
|
|
458
|
+
|
|
459
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
460
|
+
await orchestrator.initialize();
|
|
461
|
+
|
|
462
|
+
expect(watchReplaceDeps).not.toHaveBeenCalled();
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// --- Shutdown tests ---
|
|
466
|
+
|
|
467
|
+
it("stops all engines, closes watchers, and disposes replaceDeps on shutdown", async () => {
|
|
468
|
+
const mockDispose = vi.fn();
|
|
469
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
|
|
470
|
+
|
|
471
|
+
const mockCopySrcWatcher = { close: vi.fn().mockResolvedValue(undefined) };
|
|
472
|
+
vi.mocked(watchCopySrcFiles).mockResolvedValue(mockCopySrcWatcher as any);
|
|
473
|
+
|
|
474
|
+
setupDefaults(createConfig({
|
|
475
|
+
packages: {
|
|
476
|
+
"core-common": { target: "node", copySrc: ["**/*.json"] },
|
|
477
|
+
},
|
|
478
|
+
replaceDeps: { "@simplysm/*": "packages/*/src" },
|
|
479
|
+
}));
|
|
480
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose });
|
|
481
|
+
|
|
482
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
483
|
+
await orchestrator.initialize();
|
|
484
|
+
await orchestrator.start();
|
|
485
|
+
await orchestrator.shutdown();
|
|
486
|
+
|
|
487
|
+
for (const engine of mockBuildEngines) {
|
|
488
|
+
expect(engine.stop).toHaveBeenCalledOnce();
|
|
489
|
+
}
|
|
490
|
+
expect(mockCopySrcWatcher.close).toHaveBeenCalledOnce();
|
|
491
|
+
expect(mockDispose).toHaveBeenCalledOnce();
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// --- batchComplete handler ---
|
|
495
|
+
|
|
496
|
+
it("triggers printErrors on batchComplete", async () => {
|
|
497
|
+
setupDefaults(createConfig({
|
|
498
|
+
packages: { "core-common": { target: "node" } },
|
|
499
|
+
}));
|
|
500
|
+
|
|
501
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
502
|
+
await orchestrator.initialize();
|
|
503
|
+
|
|
504
|
+
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
505
|
+
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
506
|
+
const batchHandler = onCall?.[1] as (() => void) | undefined;
|
|
507
|
+
|
|
508
|
+
vi.mocked(printErrors).mockClear();
|
|
509
|
+
batchHandler?.();
|
|
510
|
+
|
|
511
|
+
expect(printErrors).toHaveBeenCalledOnce();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// --- awaitTermination ---
|
|
515
|
+
|
|
516
|
+
it("delegates awaitTermination to SignalHandler", async () => {
|
|
517
|
+
setupDefaults(createConfig({
|
|
518
|
+
packages: { "core-common": { target: "node" } },
|
|
519
|
+
}));
|
|
520
|
+
|
|
521
|
+
const { SignalHandler } = await import("../../src/infra/SignalHandler");
|
|
522
|
+
|
|
523
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: [], options: [] });
|
|
524
|
+
await orchestrator.initialize();
|
|
525
|
+
await orchestrator.awaitTermination();
|
|
526
|
+
|
|
527
|
+
const signalInstance = vi.mocked(SignalHandler).mock.instances[0];
|
|
528
|
+
expect(signalInstance.waitForTermination).toHaveBeenCalled();
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
// --- targets filter ---
|
|
532
|
+
|
|
533
|
+
it("passes targets to filterPackagesByTargets", async () => {
|
|
534
|
+
setupDefaults(createConfig({
|
|
535
|
+
packages: { "core-common": { target: "node" }, "storage": { target: "node" } },
|
|
536
|
+
}));
|
|
537
|
+
|
|
538
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["core-common"], options: [] });
|
|
539
|
+
await orchestrator.initialize();
|
|
540
|
+
|
|
541
|
+
expect(filterPackagesByTargets).toHaveBeenCalledWith(
|
|
542
|
+
expect.objectContaining({ "core-common": { target: "node" } }),
|
|
543
|
+
["core-common"],
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
describe("Slice 2: dev mode", () => {
|
|
549
|
+
// Helper: override engine mock to add build result to resultCollector
|
|
550
|
+
function setupEngineWithResult(status: "success" | "error" = "success"): void {
|
|
551
|
+
vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
|
|
552
|
+
const engine = {
|
|
553
|
+
run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
|
|
554
|
+
startWatch: vi.fn().mockImplementation(() => {
|
|
555
|
+
options.resultCollector?.add({
|
|
556
|
+
name: pkg.name, target: "server", type: "build", status,
|
|
557
|
+
});
|
|
558
|
+
}),
|
|
559
|
+
stop: vi.fn().mockResolvedValue(undefined),
|
|
560
|
+
_pkgName: pkg.name,
|
|
561
|
+
};
|
|
562
|
+
mockBuildEngines.push(engine);
|
|
563
|
+
return engine as any;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// --- Acceptance: dev 명령어가 통합 Orchestrator를 사용한다 ---
|
|
568
|
+
it("creates engine for server packages and starts runtime in dev mode", async () => {
|
|
569
|
+
setupDefaults(createConfig({
|
|
570
|
+
packages: { "service-server": { target: "server" } },
|
|
571
|
+
}));
|
|
572
|
+
setupEngineWithResult("success");
|
|
573
|
+
|
|
574
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
575
|
+
await orchestrator.initialize();
|
|
576
|
+
await orchestrator.start();
|
|
577
|
+
|
|
578
|
+
expect(createBuildEngine).toHaveBeenCalledOnce();
|
|
579
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
580
|
+
expect.objectContaining({ name: "service-server" }),
|
|
581
|
+
expect.any(Object),
|
|
582
|
+
);
|
|
583
|
+
expect(mockBuildEngines[0].startWatch).toHaveBeenCalledWith({ js: true, dts: false });
|
|
584
|
+
expect(Worker.create).toHaveBeenCalledTimes(1);
|
|
585
|
+
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
586
|
+
expect.objectContaining({ env: expect.objectContaining({ VER: "1.0.0", DEV: "true" }) }),
|
|
587
|
+
);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// --- Acceptance: Server 패키지 dev 모드 실행 ---
|
|
591
|
+
it("passes merged env (VER+DEV+config.env) to engine and runtime", async () => {
|
|
592
|
+
setupDefaults(createConfig({
|
|
593
|
+
packages: {
|
|
594
|
+
"service-server": { target: "server", env: { DB_HOST: "localhost" } },
|
|
595
|
+
},
|
|
596
|
+
}));
|
|
597
|
+
setupEngineWithResult("success");
|
|
598
|
+
|
|
599
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
600
|
+
await orchestrator.initialize();
|
|
601
|
+
await orchestrator.start();
|
|
602
|
+
|
|
603
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
604
|
+
expect.objectContaining({
|
|
605
|
+
config: expect.objectContaining({
|
|
606
|
+
env: expect.objectContaining({ VER: "1.0.0", DEV: "true", DB_HOST: "localhost" }),
|
|
607
|
+
}),
|
|
608
|
+
}),
|
|
609
|
+
expect.any(Object),
|
|
610
|
+
);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// --- Acceptance: Server 리빌드 실패 시 runtime 미재시작 ---
|
|
614
|
+
it("does not start runtime when build fails", async () => {
|
|
615
|
+
setupDefaults(createConfig({
|
|
616
|
+
packages: { "service-server": { target: "server" } },
|
|
617
|
+
}));
|
|
618
|
+
setupEngineWithResult("error");
|
|
619
|
+
|
|
620
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
621
|
+
await orchestrator.initialize();
|
|
622
|
+
await orchestrator.start();
|
|
623
|
+
|
|
624
|
+
expect(Worker.create).not.toHaveBeenCalled();
|
|
625
|
+
expect(printErrors).toHaveBeenCalled();
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// --- Acceptance: Library 패키지가 dev에서 제외된다 ---
|
|
629
|
+
it("excludes library packages from dev mode", async () => {
|
|
630
|
+
setupDefaults(createConfig({
|
|
631
|
+
packages: {
|
|
632
|
+
"core-common": { target: "node" },
|
|
633
|
+
"service-server": { target: "server" },
|
|
634
|
+
},
|
|
635
|
+
}));
|
|
636
|
+
setupEngineWithResult("success");
|
|
637
|
+
|
|
638
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
639
|
+
await orchestrator.initialize();
|
|
640
|
+
await orchestrator.start();
|
|
641
|
+
|
|
642
|
+
// Only server engine created
|
|
643
|
+
expect(createBuildEngine).toHaveBeenCalledOnce();
|
|
644
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
645
|
+
expect.objectContaining({ name: "service-server" }),
|
|
646
|
+
expect.any(Object),
|
|
647
|
+
);
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// --- Acceptance: client target 패키지를 분류한다 ---
|
|
651
|
+
it("creates engine for client packages in dev mode", async () => {
|
|
652
|
+
setupDefaults(createConfig({
|
|
653
|
+
packages: {
|
|
654
|
+
"service-server": { target: "server" },
|
|
655
|
+
"my-client": { target: "client", server: "service-server" },
|
|
656
|
+
},
|
|
657
|
+
}));
|
|
658
|
+
setupEngineWithResult("success");
|
|
659
|
+
|
|
660
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
661
|
+
await orchestrator.initialize();
|
|
662
|
+
|
|
663
|
+
// Server + Client engines created
|
|
664
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(2);
|
|
665
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
666
|
+
expect.objectContaining({ name: "my-client" }),
|
|
667
|
+
expect.any(Object),
|
|
668
|
+
);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// --- Acceptance: server가 string이고 해당 서버가 dev 대상 → 서버 연결 클라이언트 ---
|
|
672
|
+
it("maps client to server when config.server is a string and server is a dev target", async () => {
|
|
673
|
+
setupDefaults(createConfig({
|
|
674
|
+
packages: {
|
|
675
|
+
"service-server": { target: "server" },
|
|
676
|
+
"my-client": { target: "client", server: "service-server" },
|
|
677
|
+
},
|
|
678
|
+
}));
|
|
679
|
+
setupEngineWithResult("success");
|
|
680
|
+
|
|
681
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
682
|
+
await orchestrator.initialize();
|
|
683
|
+
|
|
684
|
+
// Client engine created (not skipped)
|
|
685
|
+
const clientEngineCall = vi.mocked(createBuildEngine).mock.calls.find(
|
|
686
|
+
(c: any[]) => c[0].name === "my-client",
|
|
687
|
+
);
|
|
688
|
+
expect(clientEngineCall).toBeDefined();
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// --- Acceptance: server가 string이지만 서버가 dev 대상 아닌 경우 → 경고 후 독립 전환 ---
|
|
692
|
+
it("warns and treats as standalone when config.server names a non-dev-target server", async () => {
|
|
693
|
+
setupDefaults(createConfig({
|
|
694
|
+
packages: {
|
|
695
|
+
"my-client": { target: "client", server: "non-existent-server" },
|
|
696
|
+
},
|
|
697
|
+
}));
|
|
698
|
+
setupEngineWithResult("success");
|
|
699
|
+
|
|
700
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
701
|
+
await orchestrator.initialize();
|
|
702
|
+
|
|
703
|
+
// Client engine still created
|
|
704
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
705
|
+
expect.objectContaining({ name: "my-client" }),
|
|
706
|
+
expect.any(Object),
|
|
707
|
+
);
|
|
708
|
+
// Warning about non-target server
|
|
709
|
+
expect(process.stdout.write).toHaveBeenCalledWith(
|
|
710
|
+
expect.stringContaining("non-existent-server"),
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// --- Acceptance: client 패키지가 0개 → 기존 동작 유지 ---
|
|
715
|
+
it("operates normally with server only when no client packages exist", async () => {
|
|
716
|
+
setupDefaults(createConfig({
|
|
717
|
+
packages: {
|
|
718
|
+
"service-server": { target: "server" },
|
|
719
|
+
},
|
|
720
|
+
}));
|
|
721
|
+
setupEngineWithResult("success");
|
|
722
|
+
|
|
723
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
724
|
+
await orchestrator.initialize();
|
|
725
|
+
await orchestrator.start();
|
|
726
|
+
|
|
727
|
+
expect(createBuildEngine).toHaveBeenCalledOnce();
|
|
728
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
729
|
+
expect.objectContaining({ name: "service-server" }),
|
|
730
|
+
expect.any(Object),
|
|
731
|
+
);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// --- Unit: server가 number인 경우 독립 클라이언트 ---
|
|
735
|
+
it("creates engine for standalone client with config.server as number", async () => {
|
|
736
|
+
setupDefaults(createConfig({
|
|
737
|
+
packages: {
|
|
738
|
+
"standalone": { target: "client", server: 4200 },
|
|
739
|
+
},
|
|
740
|
+
}));
|
|
741
|
+
setupEngineWithResult("success");
|
|
742
|
+
|
|
743
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
744
|
+
await orchestrator.initialize();
|
|
745
|
+
|
|
746
|
+
expect(createBuildEngine).toHaveBeenCalledWith(
|
|
747
|
+
expect.objectContaining({ name: "standalone" }),
|
|
748
|
+
expect.any(Object),
|
|
749
|
+
);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
// --- Unit: client only (no server) still initializes ---
|
|
753
|
+
it("initializes when only client packages exist (no server)", async () => {
|
|
754
|
+
setupDefaults(createConfig({
|
|
755
|
+
packages: {
|
|
756
|
+
"my-client": { target: "client", server: 4200 },
|
|
757
|
+
},
|
|
758
|
+
}));
|
|
759
|
+
setupEngineWithResult("success");
|
|
760
|
+
|
|
761
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
762
|
+
await orchestrator.initialize();
|
|
763
|
+
|
|
764
|
+
expect(createBuildEngine).toHaveBeenCalledOnce();
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
// --- Acceptance: Server 리빌드 시 runtime 재시작 ---
|
|
768
|
+
it("restarts runtime on batchComplete with success", async () => {
|
|
769
|
+
setupDefaults(createConfig({
|
|
770
|
+
packages: { "service-server": { target: "server" } },
|
|
771
|
+
}));
|
|
772
|
+
setupEngineWithResult("success");
|
|
773
|
+
|
|
774
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
775
|
+
await orchestrator.initialize();
|
|
776
|
+
await orchestrator.start();
|
|
777
|
+
|
|
778
|
+
// Get batchComplete handler
|
|
779
|
+
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
780
|
+
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
781
|
+
const batchHandler = onCall?.[1] as (() => void) | undefined;
|
|
782
|
+
|
|
783
|
+
batchHandler?.();
|
|
784
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
785
|
+
|
|
786
|
+
// Old runtime terminated, new one created
|
|
787
|
+
expect(mockRuntimeProxies).toHaveLength(2);
|
|
788
|
+
expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
// --- Acceptance: SIGINT/SIGTERM 시 graceful shutdown (dev mode) ---
|
|
792
|
+
it("stops engines and terminates runtime workers on shutdown", async () => {
|
|
793
|
+
setupDefaults(createConfig({
|
|
794
|
+
packages: { "service-server": { target: "server" } },
|
|
795
|
+
}));
|
|
796
|
+
setupEngineWithResult("success");
|
|
797
|
+
|
|
798
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
799
|
+
await orchestrator.initialize();
|
|
800
|
+
await orchestrator.start();
|
|
801
|
+
await orchestrator.shutdown();
|
|
802
|
+
|
|
803
|
+
expect(mockBuildEngines[0].stop).toHaveBeenCalledOnce();
|
|
804
|
+
expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
|
|
805
|
+
expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("Shutting down"));
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// --- Acceptance: dev에서 replaceDeps 감시 ---
|
|
809
|
+
it("starts replaceDeps watcher in dev mode", async () => {
|
|
810
|
+
const replaceDeps = { "@simplysm/*": "packages/*/src" };
|
|
811
|
+
setupDefaults(createConfig({
|
|
812
|
+
packages: { "service-server": { target: "server" } },
|
|
813
|
+
replaceDeps,
|
|
814
|
+
}));
|
|
815
|
+
setupEngineWithResult("success");
|
|
816
|
+
|
|
817
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
818
|
+
await orchestrator.initialize();
|
|
819
|
+
|
|
820
|
+
expect(watchReplaceDeps).toHaveBeenCalledWith("/test-root", replaceDeps);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// Unit: outputs warning when no server packages
|
|
824
|
+
it("outputs warning when no server packages in dev mode", async () => {
|
|
825
|
+
setupDefaults(createConfig({
|
|
826
|
+
packages: { "core-common": { target: "node" } },
|
|
827
|
+
}));
|
|
828
|
+
|
|
829
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
830
|
+
await orchestrator.initialize();
|
|
831
|
+
|
|
832
|
+
expect(process.stdout.write).toHaveBeenCalledWith(expect.stringContaining("No"));
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
// Unit: multiple server packages
|
|
836
|
+
it("creates engines and runtimes for multiple server packages", async () => {
|
|
837
|
+
setupDefaults(createConfig({
|
|
838
|
+
packages: {
|
|
839
|
+
"service-server": { target: "server" },
|
|
840
|
+
"api-server": { target: "server" },
|
|
841
|
+
},
|
|
842
|
+
}));
|
|
843
|
+
setupEngineWithResult("success");
|
|
844
|
+
|
|
845
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
846
|
+
await orchestrator.initialize();
|
|
847
|
+
await orchestrator.start();
|
|
848
|
+
|
|
849
|
+
expect(createBuildEngine).toHaveBeenCalledTimes(2);
|
|
850
|
+
expect(Worker.create).toHaveBeenCalledTimes(2);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
// Unit: skips start when no packages
|
|
854
|
+
it("skips start when no server packages found", async () => {
|
|
855
|
+
setupDefaults(createConfig({
|
|
856
|
+
packages: { "core-common": { target: "node" } },
|
|
857
|
+
}));
|
|
858
|
+
|
|
859
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
860
|
+
await orchestrator.initialize();
|
|
861
|
+
await orchestrator.start();
|
|
862
|
+
|
|
863
|
+
expect(createBuildEngine).not.toHaveBeenCalled();
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
// Unit: disposes replaceDeps on dev shutdown
|
|
867
|
+
it("disposes replaceDeps watcher on dev shutdown", async () => {
|
|
868
|
+
const mockDispose = vi.fn();
|
|
869
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose } as any);
|
|
870
|
+
|
|
871
|
+
setupDefaults(createConfig({
|
|
872
|
+
packages: { "service-server": { target: "server" } },
|
|
873
|
+
replaceDeps: { "@simplysm/*": "packages/*/src" },
|
|
874
|
+
}));
|
|
875
|
+
vi.mocked(watchReplaceDeps).mockResolvedValue({ entries: [], dispose: mockDispose } as any);
|
|
876
|
+
setupEngineWithResult("success");
|
|
877
|
+
|
|
878
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
879
|
+
await orchestrator.initialize();
|
|
880
|
+
await orchestrator.start();
|
|
881
|
+
await orchestrator.shutdown();
|
|
882
|
+
|
|
883
|
+
expect(mockDispose).toHaveBeenCalledOnce();
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
describe("Slice 4: dev mode client integration", () => {
|
|
888
|
+
// Helper: engine mock with ViteEngine-like port behavior for clients
|
|
889
|
+
function setupEngineWithClientPort(
|
|
890
|
+
serverStatus: "success" | "error" = "success",
|
|
891
|
+
clientPort: number | null = 54321,
|
|
892
|
+
): void {
|
|
893
|
+
vi.mocked(createBuildEngine).mockImplementation((pkg: any, options: any) => {
|
|
894
|
+
const isClient = pkg.config.target === "client";
|
|
895
|
+
const engine: any = {
|
|
896
|
+
run: vi.fn().mockResolvedValue({ success: true, js: { success: true, errors: [], warnings: [] }, dts: { success: true, errors: [], warnings: [], diagnostics: [] } }),
|
|
897
|
+
startWatch: vi.fn().mockImplementation(() => {
|
|
898
|
+
if (isClient) {
|
|
899
|
+
// Client: simulate Vite serverReady
|
|
900
|
+
if (clientPort != null) {
|
|
901
|
+
engine.port = clientPort;
|
|
902
|
+
}
|
|
903
|
+
} else {
|
|
904
|
+
// Server: report build result
|
|
905
|
+
options.resultCollector?.add({
|
|
906
|
+
name: pkg.name, target: "server", type: "build", status: serverStatus,
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}),
|
|
910
|
+
stop: vi.fn().mockResolvedValue(undefined),
|
|
911
|
+
_pkgName: pkg.name,
|
|
912
|
+
port: undefined,
|
|
913
|
+
};
|
|
914
|
+
mockBuildEngines.push(engine);
|
|
915
|
+
return engine;
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// --- Acceptance: 서버 연결 클라이언트의 Vite dev server 시작 ---
|
|
920
|
+
it("starts client engines with startWatch({ js: true, dts: false })", async () => {
|
|
921
|
+
setupDefaults(createConfig({
|
|
922
|
+
packages: {
|
|
923
|
+
"service-server": { target: "server" },
|
|
924
|
+
"my-client": { target: "client", server: "service-server" },
|
|
925
|
+
},
|
|
926
|
+
}));
|
|
927
|
+
setupEngineWithClientPort("success");
|
|
928
|
+
|
|
929
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
930
|
+
await orchestrator.initialize();
|
|
931
|
+
await orchestrator.start();
|
|
932
|
+
|
|
933
|
+
const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
|
|
934
|
+
expect(clientEngine.startWatch).toHaveBeenCalledWith({ js: true, dts: false });
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
// --- Acceptance: 서버 연결 클라이언트 ready 대기 후 서버 시작 → clientPorts 전달 ---
|
|
938
|
+
it("passes clientPorts to server runtime for connected clients", async () => {
|
|
939
|
+
setupDefaults(createConfig({
|
|
940
|
+
packages: {
|
|
941
|
+
"service-server": { target: "server" },
|
|
942
|
+
"my-client": { target: "client", server: "service-server" },
|
|
943
|
+
},
|
|
944
|
+
}));
|
|
945
|
+
setupEngineWithClientPort("success", 54321);
|
|
946
|
+
|
|
947
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
948
|
+
await orchestrator.initialize();
|
|
949
|
+
await orchestrator.start();
|
|
950
|
+
|
|
951
|
+
expect(Worker.create).toHaveBeenCalled();
|
|
952
|
+
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
953
|
+
expect.objectContaining({
|
|
954
|
+
clientPorts: { "my-client": 54321 },
|
|
955
|
+
}),
|
|
956
|
+
);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
// --- Acceptance: 독립 클라이언트는 서버 시작 대기에 영향 없음 ---
|
|
960
|
+
it("does not include standalone client ports in server runtime clientPorts", async () => {
|
|
961
|
+
setupDefaults(createConfig({
|
|
962
|
+
packages: {
|
|
963
|
+
"service-server": { target: "server" },
|
|
964
|
+
"standalone": { target: "client", server: 4200 },
|
|
965
|
+
},
|
|
966
|
+
}));
|
|
967
|
+
setupEngineWithClientPort("success", 4200);
|
|
968
|
+
|
|
969
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
970
|
+
await orchestrator.initialize();
|
|
971
|
+
await orchestrator.start();
|
|
972
|
+
|
|
973
|
+
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
974
|
+
expect.objectContaining({
|
|
975
|
+
clientPorts: {},
|
|
976
|
+
}),
|
|
977
|
+
);
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
// --- Acceptance: 독립 클라이언트 URL 출력 (ResultCollector 등록) ---
|
|
981
|
+
it("registers standalone client as running server in ResultCollector", async () => {
|
|
982
|
+
setupDefaults(createConfig({
|
|
983
|
+
packages: {
|
|
984
|
+
"standalone": { target: "client", server: 4200 },
|
|
985
|
+
},
|
|
986
|
+
}));
|
|
987
|
+
setupEngineWithClientPort("success", 4200);
|
|
988
|
+
|
|
989
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
990
|
+
await orchestrator.initialize();
|
|
991
|
+
await orchestrator.start();
|
|
992
|
+
|
|
993
|
+
const resultCollector = vi.mocked(ResultCollector).mock.instances[0];
|
|
994
|
+
expect(resultCollector.add).toHaveBeenCalledWith(
|
|
995
|
+
expect.objectContaining({
|
|
996
|
+
name: "standalone",
|
|
997
|
+
target: "client",
|
|
998
|
+
type: "server",
|
|
999
|
+
status: "running",
|
|
1000
|
+
port: 4200,
|
|
1001
|
+
}),
|
|
1002
|
+
);
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
// --- Acceptance: 클라이언트 Vite 서버 실패 시 프록시 없이 서버 시작 ---
|
|
1006
|
+
it("starts server without proxy when client port is unavailable", async () => {
|
|
1007
|
+
setupDefaults(createConfig({
|
|
1008
|
+
packages: {
|
|
1009
|
+
"service-server": { target: "server" },
|
|
1010
|
+
"my-client": { target: "client", server: "service-server" },
|
|
1011
|
+
},
|
|
1012
|
+
}));
|
|
1013
|
+
setupEngineWithClientPort("success", null);
|
|
1014
|
+
|
|
1015
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
1016
|
+
await orchestrator.initialize();
|
|
1017
|
+
await orchestrator.start();
|
|
1018
|
+
|
|
1019
|
+
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
1020
|
+
expect.objectContaining({
|
|
1021
|
+
clientPorts: {},
|
|
1022
|
+
}),
|
|
1023
|
+
);
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// --- Acceptance: printServers에 serverClientsMap 전달 ---
|
|
1027
|
+
it("passes serverClientsMap to printServers", async () => {
|
|
1028
|
+
setupDefaults(createConfig({
|
|
1029
|
+
packages: {
|
|
1030
|
+
"service-server": { target: "server" },
|
|
1031
|
+
"my-client": { target: "client", server: "service-server" },
|
|
1032
|
+
},
|
|
1033
|
+
}));
|
|
1034
|
+
setupEngineWithClientPort("success", 54321);
|
|
1035
|
+
|
|
1036
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
1037
|
+
await orchestrator.initialize();
|
|
1038
|
+
await orchestrator.start();
|
|
1039
|
+
|
|
1040
|
+
expect(_printServers).toHaveBeenCalledWith(
|
|
1041
|
+
expect.any(Map),
|
|
1042
|
+
expect.any(Map),
|
|
1043
|
+
);
|
|
1044
|
+
const serverClientsMap = vi.mocked(_printServers).mock.calls[0][1] as Map<string, string[]>;
|
|
1045
|
+
expect(serverClientsMap.get("service-server")).toEqual(["my-client"]);
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
// --- Acceptance: 서버 rebuild 시 프록시 재등록 ---
|
|
1049
|
+
it("passes clientPorts on server rebuild restart", async () => {
|
|
1050
|
+
setupDefaults(createConfig({
|
|
1051
|
+
packages: {
|
|
1052
|
+
"service-server": { target: "server" },
|
|
1053
|
+
"my-client": { target: "client", server: "service-server" },
|
|
1054
|
+
},
|
|
1055
|
+
}));
|
|
1056
|
+
setupEngineWithClientPort("success", 54321);
|
|
1057
|
+
|
|
1058
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
1059
|
+
await orchestrator.initialize();
|
|
1060
|
+
await orchestrator.start();
|
|
1061
|
+
|
|
1062
|
+
// Trigger batchComplete
|
|
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 (() => void) | undefined;
|
|
1066
|
+
batchHandler?.();
|
|
1067
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
1068
|
+
|
|
1069
|
+
// Second runtime start should also have clientPorts
|
|
1070
|
+
expect(mockRuntimeProxies).toHaveLength(2);
|
|
1071
|
+
expect(mockRuntimeProxies[1].start).toHaveBeenCalledWith(
|
|
1072
|
+
expect.objectContaining({
|
|
1073
|
+
clientPorts: { "my-client": 54321 },
|
|
1074
|
+
}),
|
|
1075
|
+
);
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// --- Unit: shutdown stops client engines ---
|
|
1079
|
+
it("stops client engines on shutdown", async () => {
|
|
1080
|
+
setupDefaults(createConfig({
|
|
1081
|
+
packages: {
|
|
1082
|
+
"service-server": { target: "server" },
|
|
1083
|
+
"my-client": { target: "client", server: "service-server" },
|
|
1084
|
+
},
|
|
1085
|
+
}));
|
|
1086
|
+
setupEngineWithClientPort("success", 54321);
|
|
1087
|
+
|
|
1088
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
1089
|
+
await orchestrator.initialize();
|
|
1090
|
+
await orchestrator.start();
|
|
1091
|
+
await orchestrator.shutdown();
|
|
1092
|
+
|
|
1093
|
+
const clientEngine = mockBuildEngines.find((e) => e._pkgName === "my-client")!;
|
|
1094
|
+
expect(clientEngine.stop).toHaveBeenCalledOnce();
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
// --- Unit: Client dev server에 env를 주입하지 않음 ---
|
|
1098
|
+
it("does not pass env to client engine config", async () => {
|
|
1099
|
+
setupDefaults(createConfig({
|
|
1100
|
+
packages: {
|
|
1101
|
+
"my-client": { target: "client", server: 4200, env: { API_URL: "http://example.com" } },
|
|
1102
|
+
},
|
|
1103
|
+
}));
|
|
1104
|
+
setupEngineWithClientPort("success", 4200);
|
|
1105
|
+
|
|
1106
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: [], options: [] });
|
|
1107
|
+
await orchestrator.initialize();
|
|
1108
|
+
|
|
1109
|
+
// Client engine should not have VER/DEV in config
|
|
1110
|
+
const clientCall = vi.mocked(createBuildEngine).mock.calls.find(
|
|
1111
|
+
(c: any[]) => c[0].name === "my-client",
|
|
1112
|
+
);
|
|
1113
|
+
const clientConfig = clientCall?.[0] as any;
|
|
1114
|
+
expect(clientConfig.config.env).toEqual({ API_URL: "http://example.com" });
|
|
1115
|
+
expect(clientConfig.config.env?.VER).toBeUndefined();
|
|
1116
|
+
expect(clientConfig.config.env?.DEV).toBeUndefined();
|
|
1117
|
+
});
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
describe("target validation", () => {
|
|
1121
|
+
it("calls validateTargets during watch initialize", async () => {
|
|
1122
|
+
setupDefaults(createConfig({
|
|
1123
|
+
packages: { "core-common": { target: "node" } },
|
|
1124
|
+
}));
|
|
1125
|
+
|
|
1126
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["core-common"], options: [] });
|
|
1127
|
+
await orchestrator.initialize();
|
|
1128
|
+
|
|
1129
|
+
expect(validateTargets).toHaveBeenCalledWith(
|
|
1130
|
+
["core-common"],
|
|
1131
|
+
expect.objectContaining({ "core-common": { target: "node" } }),
|
|
1132
|
+
);
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
it("calls validateTargets during dev initialize", async () => {
|
|
1136
|
+
setupDefaults(createConfig({
|
|
1137
|
+
packages: { "service-server": { target: "server" } },
|
|
1138
|
+
}));
|
|
1139
|
+
|
|
1140
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: ["service-server"], options: [] });
|
|
1141
|
+
await orchestrator.initialize();
|
|
1142
|
+
|
|
1143
|
+
expect(validateTargets).toHaveBeenCalledWith(
|
|
1144
|
+
["service-server"],
|
|
1145
|
+
expect.objectContaining({ "service-server": { target: "server" } }),
|
|
1146
|
+
);
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
it("throws when validateTargets throws for unknown target in watch", async () => {
|
|
1150
|
+
setupDefaults(createConfig({
|
|
1151
|
+
packages: { "core-common": { target: "node" } },
|
|
1152
|
+
}));
|
|
1153
|
+
vi.mocked(validateTargets).mockImplementation(() => {
|
|
1154
|
+
throw new Error("Unknown target: nonexistent");
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1157
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "watch", targets: ["nonexistent"], options: [] });
|
|
1158
|
+
await expect(orchestrator.initialize()).rejects.toThrow("Unknown target: nonexistent");
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1161
|
+
it("throws when validateTargets throws for unknown target in dev", async () => {
|
|
1162
|
+
setupDefaults(createConfig({
|
|
1163
|
+
packages: { "service-server": { target: "server" } },
|
|
1164
|
+
}));
|
|
1165
|
+
vi.mocked(validateTargets).mockImplementation(() => {
|
|
1166
|
+
throw new Error("Unknown target: nonexistent");
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
const orchestrator = new DevWatchOrchestrator({ mode: "dev", targets: ["nonexistent"], options: [] });
|
|
1170
|
+
await expect(orchestrator.initialize()).rejects.toThrow("Unknown target: nonexistent");
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1173
|
+
});
|