@simplysm/sd-cli 14.0.15 → 14.0.17
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/README.md +4 -3
- package/dist/angular/client-transform-stylesheet.d.ts +2 -0
- package/dist/angular/client-transform-stylesheet.d.ts.map +1 -1
- package/dist/angular/client-transform-stylesheet.js +88 -2
- package/dist/angular/client-transform-stylesheet.js.map +1 -1
- package/dist/angular/vite-angular-plugin.d.ts +7 -0
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +78 -16
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +24 -13
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +8 -9
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/device.d.ts +1 -1
- package/dist/commands/device.d.ts.map +1 -1
- package/dist/commands/device.js +61 -12
- package/dist/commands/device.js.map +1 -1
- package/dist/commands/lint.d.ts +0 -1
- package/dist/commands/lint.d.ts.map +1 -1
- package/dist/commands/lint.js +2 -3
- package/dist/commands/lint.js.map +1 -1
- package/dist/commands/publish.js +3 -3
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/replace-deps.js.map +1 -1
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +1 -2
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/electron/electron.d.ts +3 -2
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +54 -31
- package/dist/electron/electron.js.map +1 -1
- package/dist/engines/BaseEngine.js +1 -1
- package/dist/engines/BaseEngine.js.map +1 -1
- package/dist/engines/NgtscEngine.d.ts.map +1 -1
- package/dist/engines/NgtscEngine.js +0 -1
- package/dist/engines/NgtscEngine.js.map +1 -1
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
- package/dist/engines/ServerEsbuildEngine.js +0 -1
- package/dist/engines/ServerEsbuildEngine.js.map +1 -1
- package/dist/engines/TscEngine.d.ts.map +1 -1
- package/dist/engines/TscEngine.js +0 -1
- package/dist/engines/TscEngine.js.map +1 -1
- package/dist/engines/ViteEngine.d.ts.map +1 -1
- package/dist/engines/ViteEngine.js +10 -1
- package/dist/engines/ViteEngine.js.map +1 -1
- package/dist/engines/index.d.ts +0 -10
- package/dist/engines/index.d.ts.map +1 -1
- package/dist/engines/index.js +0 -5
- package/dist/engines/index.js.map +1 -1
- package/dist/engines/types.d.ts +0 -1
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/infra/SignalHandler.d.ts +1 -6
- package/dist/infra/SignalHandler.d.ts.map +1 -1
- package/dist/infra/SignalHandler.js +4 -13
- package/dist/infra/SignalHandler.js.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +7 -12
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +18 -11
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +0 -1
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +13 -16
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.js +1 -1
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +12 -2
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/angular-compiler.d.ts.map +1 -1
- package/dist/utils/angular-compiler.js +20 -13
- package/dist/utils/angular-compiler.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +1 -1
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +1 -4
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
- package/dist/utils/ngtsc-build-core.js +3 -0
- package/dist/utils/ngtsc-build-core.js.map +1 -1
- package/dist/utils/orchestrator-utils.js +1 -1
- package/dist/utils/orchestrator-utils.js.map +1 -1
- package/dist/utils/tsc-build.d.ts +5 -0
- package/dist/utils/tsc-build.d.ts.map +1 -1
- package/dist/utils/tsc-build.js +2 -1
- package/dist/utils/tsc-build.js.map +1 -1
- package/dist/utils/vite-config.d.ts +1 -1
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +22 -53
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-pwa-plugin.d.ts +9 -0
- package/dist/utils/vite-pwa-plugin.d.ts.map +1 -0
- package/dist/utils/vite-pwa-plugin.js +139 -0
- package/dist/utils/vite-pwa-plugin.js.map +1 -0
- package/dist/utils/worker-utils.d.ts +2 -5
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js +5 -11
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +9 -3
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/library-build.worker.d.ts.map +1 -1
- package/dist/workers/library-build.worker.js +6 -2
- package/dist/workers/library-build.worker.js.map +1 -1
- package/dist/workers/ngtsc-build.worker.js +2 -2
- package/dist/workers/ngtsc-build.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +6 -2
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.js +4 -4
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/docs/config.md +30 -2
- package/docs/pwa-configuration-types.md +1 -1
- package/package.json +8 -10
- package/src/angular/client-transform-stylesheet.ts +104 -2
- package/src/angular/vite-angular-plugin.ts +92 -31
- package/src/capacitor/capacitor.ts +25 -26
- package/src/commands/check.ts +8 -11
- package/src/commands/device.ts +71 -17
- package/src/commands/lint.ts +2 -3
- package/src/commands/publish.ts +3 -3
- package/src/commands/replace-deps.ts +1 -1
- package/src/commands/typecheck.ts +1 -2
- package/src/electron/electron.ts +62 -43
- package/src/engines/BaseEngine.ts +1 -1
- package/src/engines/NgtscEngine.ts +0 -1
- package/src/engines/ServerEsbuildEngine.ts +0 -1
- package/src/engines/TscEngine.ts +0 -1
- package/src/engines/ViteEngine.ts +9 -1
- package/src/engines/index.ts +0 -10
- package/src/engines/types.ts +0 -1
- package/src/infra/SignalHandler.ts +4 -14
- package/src/orchestrators/BuildOrchestrator.ts +7 -9
- package/src/orchestrators/DevWatchOrchestrator.ts +22 -10
- package/src/sd-cli-entry.ts +17 -24
- package/src/sd-cli.ts +1 -1
- package/src/sd-config.types.ts +13 -2
- package/src/utils/angular-compiler.ts +21 -21
- package/src/utils/esbuild-config.ts +2 -5
- package/src/utils/ngtsc-build-core.ts +7 -0
- package/src/utils/orchestrator-utils.ts +1 -1
- package/src/utils/tsc-build.ts +7 -0
- package/src/utils/vite-config.ts +23 -55
- package/src/utils/vite-pwa-plugin.ts +168 -0
- package/src/utils/worker-utils.ts +5 -11
- package/src/workers/client.worker.ts +11 -3
- package/src/workers/library-build.worker.ts +6 -2
- package/src/workers/ngtsc-build.worker.ts +2 -2
- package/src/workers/server-build.worker.ts +7 -2
- package/src/workers/server-runtime.worker.ts +4 -4
- package/tests/angular/client-transform-stylesheet.spec.ts +43 -0
- package/tests/angular/find-affected-by-scss.spec.ts +37 -0
- package/tests/angular/fixtures/basic-app/scss/_colors.scss +1 -0
- package/tests/angular/fixtures/basic-app/scss/_variables.scss +3 -0
- package/tests/angular/fixtures/basic-app/src/styled.component.ts +14 -0
- package/tests/angular/linker-disk-cache.spec.ts +158 -0
- package/tests/angular/scss-disk-cache.spec.ts +162 -0
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +15 -15
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +9 -9
- package/tests/angular/vite-angular-plugin-lint.spec.ts +4 -4
- package/tests/angular/vite-angular-plugin-scss-hmr.spec.ts +87 -0
- package/tests/angular/vite-angular-plugin.spec.ts +15 -15
- package/tests/capacitor/capacitor-icon.spec.ts +2 -4
- package/tests/capacitor/capacitor-init.spec.ts +2 -4
- package/tests/capacitor/capacitor-workspace.spec.ts +2 -4
- package/tests/commands/device.spec.ts +108 -8
- package/tests/commands/publish.spec.ts +2 -2
- package/tests/commands/typecheck.spec.ts +1 -1
- package/tests/electron/electron.spec.ts +24 -17
- package/tests/engines/ngtsc-engine.spec.ts +0 -3
- package/tests/engines/server-esbuild-engine.spec.ts +0 -3
- package/tests/engines/tsc-engine.spec.ts +1 -2
- package/tests/engines/vite-engine.spec.ts +0 -2
- package/tests/infra/signal-handler.spec.ts +1 -12
- package/tests/orchestrators/build-orchestrator.spec.ts +1 -7
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +24 -66
- package/tests/utils/angular-compiler.spec.ts +1396 -32
- package/tests/utils/esbuild-config.spec.ts +4 -7
- package/tests/utils/{ngtsc-build-core-angular-compiler.spec.ts → ngtsc-build-core.spec.ts} +142 -11
- package/tests/utils/orchestrator-utils.spec.ts +2 -2
- package/tests/utils/sd-config.spec.ts +2 -2
- package/tests/utils/tsc-build.spec.ts +4 -1
- package/tests/utils/vite-config.spec.ts +130 -261
- package/tests/utils/vite-pwa-plugin.acc.spec.ts +143 -0
- package/tests/utils/vite-pwa-plugin.spec.ts +350 -0
- package/tests/utils/worker-utils.spec.ts +8 -7
- package/tests/workers/client-worker.spec.ts +50 -1
- package/tests/workers/dev-port-file.verify.md +6 -0
- package/tests/workers/library-build-lint.spec.ts +1 -1
- package/tests/workers/library-build-worker.spec.ts +1 -1
- package/tests/workers/ngtsc-build-lint.spec.ts +1 -1
- package/tests/workers/server-build-lint.spec.ts +1 -1
- package/tests/workers/server-build-worker.spec.ts +1 -1
- package/tests/workers/server-runtime-worker.spec.ts +8 -1
- package/dist/infra/WorkerManager.d.ts +0 -40
- package/dist/infra/WorkerManager.d.ts.map +0 -1
- package/dist/infra/WorkerManager.js +0 -59
- package/dist/infra/WorkerManager.js.map +0 -1
- package/dist/utils/SdCliReporter.d.ts +0 -18
- package/dist/utils/SdCliReporter.d.ts.map +0 -1
- package/dist/utils/SdCliReporter.js +0 -144
- package/dist/utils/SdCliReporter.js.map +0 -1
- package/src/infra/WorkerManager.ts +0 -65
- package/src/utils/SdCliReporter.ts +0 -177
- package/tests/angular/scss-compiler-async.spec.ts +0 -54
- package/tests/commands/dev.spec.ts +0 -53
- package/tests/commands/watch.spec.ts +0 -53
- package/tests/infra/worker-manager.spec.ts +0 -63
- package/tests/utils/angular-compiler-emit.spec.ts +0 -570
- package/tests/utils/angular-compiler-init.spec.ts +0 -705
- package/tests/utils/angular-compiler-update.spec.ts +0 -293
- package/tests/utils/build-env.spec.ts +0 -33
- package/tests/utils/ngtsc-build-core-transform-stylesheet.spec.ts +0 -124
- package/tests/utils/ngtsc-scss-refactor.spec.ts +0 -47
|
@@ -31,6 +31,27 @@ vi.mock("../../src/utils/sd-config", () => ({
|
|
|
31
31
|
loadSdConfig: vi.fn(),
|
|
32
32
|
}));
|
|
33
33
|
|
|
34
|
+
// fs mock (포트 파일 읽기용)
|
|
35
|
+
const mockReadFileSync = vi.fn();
|
|
36
|
+
const mockExistsSync = vi.fn().mockReturnValue(false);
|
|
37
|
+
vi.mock("node:fs", () => ({
|
|
38
|
+
default: {
|
|
39
|
+
readFileSync: (...args: any[]) => mockReadFileSync(...args),
|
|
40
|
+
existsSync: (...args: any[]) => mockExistsSync(...args),
|
|
41
|
+
},
|
|
42
|
+
readFileSync: (...args: any[]) => mockReadFileSync(...args),
|
|
43
|
+
existsSync: (...args: any[]) => mockExistsSync(...args),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// http mock (헬스체크용)
|
|
47
|
+
const mockHttpGet = vi.fn();
|
|
48
|
+
vi.mock("node:http", () => ({
|
|
49
|
+
default: {
|
|
50
|
+
get: (...args: any[]) => mockHttpGet(...args),
|
|
51
|
+
},
|
|
52
|
+
get: (...args: any[]) => mockHttpGet(...args),
|
|
53
|
+
}));
|
|
54
|
+
|
|
34
55
|
const { Capacitor } = await import("../../src/capacitor/capacitor");
|
|
35
56
|
const { Electron } = await import("../../src/electron/electron");
|
|
36
57
|
const { loadSdConfig } = await import("../../src/utils/sd-config");
|
|
@@ -55,14 +76,14 @@ describe("runDevice", () => {
|
|
|
55
76
|
},
|
|
56
77
|
});
|
|
57
78
|
|
|
58
|
-
await runDevice({
|
|
79
|
+
await runDevice({ target: "client-pda", options: [] });
|
|
59
80
|
|
|
60
81
|
expect(Capacitor.create).toHaveBeenCalledWith(
|
|
61
82
|
expect.stringContaining("client-pda"),
|
|
62
83
|
{ appId: "com.test.app", appName: "TestApp" },
|
|
63
84
|
undefined,
|
|
64
85
|
);
|
|
65
|
-
expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://localhost:40480");
|
|
86
|
+
expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://localhost:40480/client-pda/");
|
|
66
87
|
});
|
|
67
88
|
|
|
68
89
|
// Acceptance: Scenario "device 명령어로 Electron 앱 실행"
|
|
@@ -77,14 +98,14 @@ describe("runDevice", () => {
|
|
|
77
98
|
},
|
|
78
99
|
});
|
|
79
100
|
|
|
80
|
-
await runDevice({
|
|
101
|
+
await runDevice({ target: "my-client", options: [] });
|
|
81
102
|
|
|
82
103
|
expect(Electron.create).toHaveBeenCalledWith(
|
|
83
104
|
expect.stringContaining("my-client"),
|
|
84
105
|
{ appId: "com.test.electron" },
|
|
85
106
|
undefined,
|
|
86
107
|
);
|
|
87
|
-
expect(mockElectronInstance.run).toHaveBeenCalledWith("http://localhost:4200");
|
|
108
|
+
expect(mockElectronInstance.run).toHaveBeenCalledWith("http://localhost:4200/my-client/");
|
|
88
109
|
});
|
|
89
110
|
|
|
90
111
|
// Acceptance: Scenario "device 명령어에 URL 옵션 지정"
|
|
@@ -99,7 +120,7 @@ describe("runDevice", () => {
|
|
|
99
120
|
},
|
|
100
121
|
});
|
|
101
122
|
|
|
102
|
-
await runDevice({
|
|
123
|
+
await runDevice({ target: "client-pda", url: "http://192.168.1.100:4200", options: [] });
|
|
103
124
|
|
|
104
125
|
expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://192.168.1.100:4200");
|
|
105
126
|
});
|
|
@@ -117,7 +138,7 @@ describe("runDevice", () => {
|
|
|
117
138
|
},
|
|
118
139
|
});
|
|
119
140
|
|
|
120
|
-
await runDevice({
|
|
141
|
+
await runDevice({ target: "my-client", options: [] });
|
|
121
142
|
|
|
122
143
|
expect(Electron.create).toHaveBeenCalled();
|
|
123
144
|
expect(Capacitor.create).not.toHaveBeenCalled();
|
|
@@ -131,7 +152,7 @@ describe("runDevice", () => {
|
|
|
131
152
|
},
|
|
132
153
|
});
|
|
133
154
|
|
|
134
|
-
await expect(runDevice({
|
|
155
|
+
await expect(runDevice({ target: "nonexistent", options: [] })).rejects.toThrow();
|
|
135
156
|
});
|
|
136
157
|
|
|
137
158
|
// Unit: client가 아닌 패키지 에러
|
|
@@ -142,6 +163,85 @@ describe("runDevice", () => {
|
|
|
142
163
|
},
|
|
143
164
|
});
|
|
144
165
|
|
|
145
|
-
await expect(runDevice({
|
|
166
|
+
await expect(runDevice({ target: "my-server", options: [] })).rejects.toThrow();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Acceptance: Scenario "dev 서버 실행 중 device 실행 시 URL 자동 생성"
|
|
170
|
+
it("auto-detects URL from .dev-port when server is a string", async () => {
|
|
171
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
172
|
+
packages: {
|
|
173
|
+
"client-devtool": {
|
|
174
|
+
target: "client",
|
|
175
|
+
server: "server",
|
|
176
|
+
electron: { appId: "com.test.electron" },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
mockReadFileSync.mockReturnValue("5173");
|
|
182
|
+
|
|
183
|
+
// HTTP 헬스체크 성공 mock
|
|
184
|
+
mockHttpGet.mockImplementation((_url: string, cb: Function) => {
|
|
185
|
+
const res = { resume: vi.fn() };
|
|
186
|
+
cb(res);
|
|
187
|
+
return { on: vi.fn(), setTimeout: vi.fn() };
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await runDevice({ target: "client-devtool", options: [] });
|
|
191
|
+
|
|
192
|
+
expect(mockElectronInstance.run).toHaveBeenCalledWith(
|
|
193
|
+
"http://localhost:5173/client-devtool/",
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Acceptance: Scenario "dev 서버 미실행 시 에러"
|
|
198
|
+
it("throws when .dev-port file does not exist and server is a string", async () => {
|
|
199
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
200
|
+
packages: {
|
|
201
|
+
"client-devtool": {
|
|
202
|
+
target: "client",
|
|
203
|
+
server: "server",
|
|
204
|
+
electron: { appId: "com.test.electron" },
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
mockReadFileSync.mockImplementation(() => {
|
|
210
|
+
throw new Error("ENOENT");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await expect(runDevice({ target: "client-devtool", options: [] })).rejects.toThrow(
|
|
214
|
+
"dev 서버가 실행 중이 아닙니다",
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Acceptance: Scenario "stale 포트 파일 존재 시 헬스체크 실패 에러"
|
|
219
|
+
it("throws when .dev-port exists but health check fails", async () => {
|
|
220
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
221
|
+
packages: {
|
|
222
|
+
"client-devtool": {
|
|
223
|
+
target: "client",
|
|
224
|
+
server: "server",
|
|
225
|
+
electron: { appId: "com.test.electron" },
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
mockReadFileSync.mockReturnValue("5173");
|
|
231
|
+
|
|
232
|
+
// HTTP 헬스체크 실패 mock
|
|
233
|
+
mockHttpGet.mockImplementation((_url: string, _cb: Function) => {
|
|
234
|
+
const req = {
|
|
235
|
+
on: vi.fn((event: string, handler: Function) => {
|
|
236
|
+
if (event === "error") handler(new Error("ECONNREFUSED"));
|
|
237
|
+
}),
|
|
238
|
+
setTimeout: vi.fn(),
|
|
239
|
+
};
|
|
240
|
+
return req;
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
await expect(runDevice({ target: "client-devtool", options: [] })).rejects.toThrow(
|
|
244
|
+
"dev 서버가 응답하지 않습니다",
|
|
245
|
+
);
|
|
146
246
|
});
|
|
147
247
|
});
|
|
@@ -446,7 +446,7 @@ describe("runPublish", () => {
|
|
|
446
446
|
(c: unknown[]) => c[0] === "claude",
|
|
447
447
|
);
|
|
448
448
|
expect(claudeCalls).toHaveLength(1);
|
|
449
|
-
expect((claudeCalls[0][1] as string[])).toContain("/sd-commit
|
|
449
|
+
expect((claudeCalls[0][1] as string[])).toContain("/sd-commit");
|
|
450
450
|
});
|
|
451
451
|
|
|
452
452
|
it("aborts when auto-commit claude command fails", async () => {
|
|
@@ -1105,7 +1105,7 @@ describe("runPublish", () => {
|
|
|
1105
1105
|
});
|
|
1106
1106
|
|
|
1107
1107
|
expect(mocks.loadSdConfig).toHaveBeenCalledWith(
|
|
1108
|
-
expect.objectContaining({
|
|
1108
|
+
expect.objectContaining({ opt: ["production"] }),
|
|
1109
1109
|
);
|
|
1110
1110
|
});
|
|
1111
1111
|
|
|
@@ -299,7 +299,7 @@ describe("executeTypecheck", () => {
|
|
|
299
299
|
await executeTypecheck({ targets: [], options: ["key=value"] });
|
|
300
300
|
|
|
301
301
|
expect(mocks.loadSdConfig).toHaveBeenCalledWith(
|
|
302
|
-
expect.objectContaining({
|
|
302
|
+
expect.objectContaining({ opt: ["key=value"] }),
|
|
303
303
|
);
|
|
304
304
|
});
|
|
305
305
|
|
|
@@ -7,6 +7,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
7
7
|
const mockFsxExists = vi.fn();
|
|
8
8
|
const mockFsxReadJson = vi.fn();
|
|
9
9
|
const mockFsxWriteJson = vi.fn().mockResolvedValue(undefined);
|
|
10
|
+
const mockFsxWrite = vi.fn().mockResolvedValue(undefined);
|
|
10
11
|
const mockFsxMkdir = vi.fn().mockResolvedValue(undefined);
|
|
11
12
|
const mockFsxCopy = vi.fn().mockResolvedValue(undefined);
|
|
12
13
|
const mockFsxReaddir = vi.fn();
|
|
@@ -17,6 +18,7 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
17
18
|
exists: mockFsxExists,
|
|
18
19
|
readJson: mockFsxReadJson,
|
|
19
20
|
writeJson: mockFsxWriteJson,
|
|
21
|
+
write: mockFsxWrite,
|
|
20
22
|
mkdir: mockFsxMkdir,
|
|
21
23
|
copy: mockFsxCopy,
|
|
22
24
|
readdir: mockFsxReaddir,
|
|
@@ -96,6 +98,11 @@ function setupDefaultMocks() {
|
|
|
96
98
|
"better-sqlite3": "^11.0.0",
|
|
97
99
|
"sharp": "^0.34.0",
|
|
98
100
|
},
|
|
101
|
+
devDependencies: {
|
|
102
|
+
"electron": "^35.0.0",
|
|
103
|
+
"@electron/rebuild": "^4.0.0",
|
|
104
|
+
"electron-builder": "^26.0.0",
|
|
105
|
+
},
|
|
99
106
|
});
|
|
100
107
|
mockFsxReaddir.mockResolvedValue(["index.html", "assets", "electron"]);
|
|
101
108
|
// Default: glob returns one exe file matching the builder output
|
|
@@ -169,7 +176,7 @@ describe("Electron", () => {
|
|
|
169
176
|
//#region Rule: Electron 프로젝트를 초기화한다
|
|
170
177
|
|
|
171
178
|
describe("인수 테스트: 초기화", () => {
|
|
172
|
-
it("package.json 생성 +
|
|
179
|
+
it("package.json 생성 + pnpm install + electron-rebuild를 실행한다", async () => {
|
|
173
180
|
const { Electron } = await import("../../src/electron/electron.js");
|
|
174
181
|
|
|
175
182
|
const electron = await Electron.create(PKG_PATH, {
|
|
@@ -181,13 +188,13 @@ describe("Electron", () => {
|
|
|
181
188
|
|
|
182
189
|
expect(findElectronPackageJson()).toBeDefined();
|
|
183
190
|
|
|
184
|
-
const
|
|
191
|
+
const spawnCalls = mockCpxSpawn.mock.calls;
|
|
185
192
|
expect(
|
|
186
|
-
|
|
193
|
+
spawnCalls.find((c) => c[0] === "pnpm" && (c[1] as string[]).includes("install")),
|
|
187
194
|
).toBeDefined();
|
|
188
195
|
expect(
|
|
189
|
-
|
|
190
|
-
(c) =>
|
|
196
|
+
spawnCalls.find(
|
|
197
|
+
(c) => c[0] === "pnpm" && (c[1] as string[]).includes("electron-rebuild"),
|
|
191
198
|
),
|
|
192
199
|
).toBeDefined();
|
|
193
200
|
});
|
|
@@ -199,7 +206,7 @@ describe("Electron", () => {
|
|
|
199
206
|
await electron.initialize();
|
|
200
207
|
|
|
201
208
|
const rebuildCall = mockCpxSpawn.mock.calls.find(
|
|
202
|
-
(c) =>
|
|
209
|
+
(c) => c[0] === "pnpm" && (c[1] as string[]).includes("electron-rebuild"),
|
|
203
210
|
);
|
|
204
211
|
expect(rebuildCall).toBeUndefined();
|
|
205
212
|
});
|
|
@@ -455,9 +462,9 @@ describe("Electron", () => {
|
|
|
455
462
|
const electronKill = vi.fn();
|
|
456
463
|
let resolveElectron: () => void = () => {};
|
|
457
464
|
|
|
458
|
-
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
459
|
-
|
|
460
|
-
|
|
465
|
+
mockCpxSpawn.mockImplementation((cmd: string, args: string[]) => {
|
|
466
|
+
// pnpm exec electron . → Electron 프로세스
|
|
467
|
+
if (cmd === "pnpm" && args[0] === "exec" && args[1] === "electron" && args[2] === ".") {
|
|
461
468
|
const p = new Promise<void>((resolve) => {
|
|
462
469
|
resolveElectron = resolve;
|
|
463
470
|
}) as any;
|
|
@@ -537,8 +544,8 @@ describe("Electron", () => {
|
|
|
537
544
|
describe("단위: run() 플러그인 동작", () => {
|
|
538
545
|
it("passes custom env and ELECTRON_DEV_URL via esbuild banner", async () => {
|
|
539
546
|
let resolveElectron: () => void = () => {};
|
|
540
|
-
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
541
|
-
if (
|
|
547
|
+
mockCpxSpawn.mockImplementation((cmd: string, args: string[]) => {
|
|
548
|
+
if (cmd === "pnpm" && args[0] === "exec" && args[1] === "electron" && args[2] === ".") {
|
|
542
549
|
const p = new Promise<void>((resolve) => {
|
|
543
550
|
resolveElectron = resolve;
|
|
544
551
|
}) as any;
|
|
@@ -570,8 +577,8 @@ describe("Electron", () => {
|
|
|
570
577
|
|
|
571
578
|
it("calls initialize() before starting esbuild context", async () => {
|
|
572
579
|
let resolveElectron: () => void = () => {};
|
|
573
|
-
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
574
|
-
if (
|
|
580
|
+
mockCpxSpawn.mockImplementation((cmd: string, args: string[]) => {
|
|
581
|
+
if (cmd === "pnpm" && args[0] === "exec" && args[1] === "electron" && args[2] === ".") {
|
|
575
582
|
const p = new Promise<void>((resolve) => {
|
|
576
583
|
resolveElectron = resolve;
|
|
577
584
|
}) as any;
|
|
@@ -589,11 +596,11 @@ describe("Electron", () => {
|
|
|
589
596
|
resolveElectron();
|
|
590
597
|
await runPromise;
|
|
591
598
|
|
|
592
|
-
// initialize calls
|
|
593
|
-
const
|
|
594
|
-
(c: any[]) => c[0] === "
|
|
599
|
+
// initialize calls pnpm install
|
|
600
|
+
const pnpmInstallCall = mockCpxSpawn.mock.calls.find(
|
|
601
|
+
(c: any[]) => c[0] === "pnpm" && (c[1] as string[]).includes("install"),
|
|
595
602
|
);
|
|
596
|
-
expect(
|
|
603
|
+
expect(pnpmInstallCall).toBeDefined();
|
|
597
604
|
}, 10_000);
|
|
598
605
|
});
|
|
599
606
|
|
|
@@ -78,7 +78,6 @@ describe("NgtscEngine", () => {
|
|
|
78
78
|
output: { js: true, dts: true },
|
|
79
79
|
}),
|
|
80
80
|
);
|
|
81
|
-
expect(result.success).toBe(true);
|
|
82
81
|
expect(result.build.success).toBe(true);
|
|
83
82
|
expect(result.build.diagnostics).toEqual([]);
|
|
84
83
|
await engine.stop();
|
|
@@ -133,7 +132,6 @@ describe("NgtscEngine", () => {
|
|
|
133
132
|
const engine = new NgtscEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
134
133
|
const result = await engine.run({ js: true, dts: true });
|
|
135
134
|
|
|
136
|
-
expect(result.success).toBe(false);
|
|
137
135
|
expect(result.build.success).toBe(false);
|
|
138
136
|
expect(result.build.errors).toContain("TS2322: Type error");
|
|
139
137
|
await engine.stop();
|
|
@@ -148,7 +146,6 @@ describe("NgtscEngine", () => {
|
|
|
148
146
|
const engine = new NgtscEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
149
147
|
const result = await engine.run({ js: true, dts: true });
|
|
150
148
|
|
|
151
|
-
expect(result.success).toBe(false);
|
|
152
149
|
expect(result.build.success).toBe(false);
|
|
153
150
|
expect(result.build.errors).toEqual(["ngtsc compilation error"]);
|
|
154
151
|
await engine.stop();
|
|
@@ -89,7 +89,6 @@ describe("ServerEsbuildEngine", () => {
|
|
|
89
89
|
packageManager: "mise",
|
|
90
90
|
}),
|
|
91
91
|
);
|
|
92
|
-
expect(result.success).toBe(true);
|
|
93
92
|
expect(result.build.success).toBe(true);
|
|
94
93
|
await engine.stop();
|
|
95
94
|
});
|
|
@@ -104,7 +103,6 @@ describe("ServerEsbuildEngine", () => {
|
|
|
104
103
|
const engine = new ServerEsbuildEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
105
104
|
const result = await engine.run({ js: true, dts: false });
|
|
106
105
|
|
|
107
|
-
expect(result.success).toBe(false);
|
|
108
106
|
expect(result.build.warnings).toEqual(["warn1"]);
|
|
109
107
|
expect(result.build.success).toBe(false);
|
|
110
108
|
expect(result.build.diagnostics).toEqual([{ code: 2345, category: 1 }]);
|
|
@@ -121,7 +119,6 @@ describe("ServerEsbuildEngine", () => {
|
|
|
121
119
|
const engine = new ServerEsbuildEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
122
120
|
const result = await engine.run({ js: true, dts: false });
|
|
123
121
|
|
|
124
|
-
expect(result.success).toBe(false);
|
|
125
122
|
expect(result.build.success).toBe(false);
|
|
126
123
|
expect(result.build.errors).toEqual(["esbuild error"]);
|
|
127
124
|
await engine.stop();
|
|
@@ -75,7 +75,6 @@ describe("TscEngine", () => {
|
|
|
75
75
|
output: { js: true, dts: true },
|
|
76
76
|
}),
|
|
77
77
|
);
|
|
78
|
-
expect(result.success).toBe(true);
|
|
79
78
|
expect(result.build.success).toBe(true);
|
|
80
79
|
await engine.stop();
|
|
81
80
|
});
|
|
@@ -101,7 +100,7 @@ describe("TscEngine", () => {
|
|
|
101
100
|
const engine = new TscEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
102
101
|
const result = await engine.run({ js: true, dts: true });
|
|
103
102
|
|
|
104
|
-
expect(result.success).toBe(false);
|
|
103
|
+
expect(result.build.success).toBe(false);
|
|
105
104
|
expect(result.build.errors).toEqual(["type error"]);
|
|
106
105
|
expect(result.build.diagnostics).toHaveLength(1);
|
|
107
106
|
await engine.stop();
|
|
@@ -74,7 +74,6 @@ describe("ViteEngine", () => {
|
|
|
74
74
|
pkgDir: "/packages/my-client",
|
|
75
75
|
}),
|
|
76
76
|
);
|
|
77
|
-
expect(result.success).toBe(true);
|
|
78
77
|
expect(result.build.success).toBe(true);
|
|
79
78
|
expect(result.build.errors).toEqual([]);
|
|
80
79
|
expect(result.build.diagnostics).toEqual([]);
|
|
@@ -91,7 +90,6 @@ describe("ViteEngine", () => {
|
|
|
91
90
|
const engine = new ViteEngine({ cwd: "/root", pkg: createMockPkg() });
|
|
92
91
|
const result = await engine.run({ js: true, dts: false });
|
|
93
92
|
|
|
94
|
-
expect(result.success).toBe(false);
|
|
95
93
|
expect(result.build.success).toBe(false);
|
|
96
94
|
expect(result.build.errors).toContain("TS2345: Argument of type...");
|
|
97
95
|
await engine.stop();
|
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import { describe, it
|
|
1
|
+
import { describe, it } from "vitest";
|
|
2
2
|
import { SignalHandler } from "../../src/infra/SignalHandler";
|
|
3
3
|
|
|
4
4
|
describe("SignalHandler", () => {
|
|
5
5
|
it("resolves waitForTermination on requestTermination", async () => {
|
|
6
6
|
const handler = new SignalHandler();
|
|
7
7
|
|
|
8
|
-
expect(handler.isTerminated()).toBe(false);
|
|
9
|
-
|
|
10
8
|
handler.requestTermination();
|
|
11
9
|
|
|
12
10
|
await handler.waitForTermination();
|
|
13
|
-
expect(handler.isTerminated()).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("is not terminated initially", () => {
|
|
17
|
-
const handler = new SignalHandler();
|
|
18
|
-
expect(handler.isTerminated()).toBe(false);
|
|
19
|
-
// Clean up: prevent dangling signal listeners
|
|
20
|
-
handler.requestTermination();
|
|
21
11
|
});
|
|
22
12
|
|
|
23
13
|
it("handles double requestTermination gracefully", async () => {
|
|
@@ -27,6 +17,5 @@ describe("SignalHandler", () => {
|
|
|
27
17
|
handler.requestTermination();
|
|
28
18
|
|
|
29
19
|
await handler.waitForTermination();
|
|
30
|
-
expect(handler.isTerminated()).toBe(true);
|
|
31
20
|
});
|
|
32
21
|
});
|
|
@@ -66,7 +66,6 @@ vi.mock("../../src/engines/index", () => ({
|
|
|
66
66
|
createBuildEngine: vi.fn(() => {
|
|
67
67
|
const engine = {
|
|
68
68
|
run: vi.fn().mockResolvedValue({
|
|
69
|
-
success: true,
|
|
70
69
|
build: { success: true, errors: [], warnings: [], diagnostics: [] },
|
|
71
70
|
}),
|
|
72
71
|
startWatch: vi.fn().mockResolvedValue(undefined),
|
|
@@ -159,7 +158,6 @@ beforeEach(() => {
|
|
|
159
158
|
vi.mocked(createBuildEngine).mockImplementation(() => {
|
|
160
159
|
const engine = {
|
|
161
160
|
run: vi.fn().mockResolvedValue({
|
|
162
|
-
success: true,
|
|
163
161
|
build: { success: true, errors: [], warnings: [], diagnostics: [] },
|
|
164
162
|
|
|
165
163
|
}),
|
|
@@ -218,7 +216,7 @@ describe("BuildOrchestrator.initialize", () => {
|
|
|
218
216
|
await orchestrator.initialize();
|
|
219
217
|
|
|
220
218
|
expect(loadSdConfig).toHaveBeenCalledWith(
|
|
221
|
-
expect.objectContaining({
|
|
219
|
+
expect.objectContaining({ opt: ["production"] }),
|
|
222
220
|
);
|
|
223
221
|
});
|
|
224
222
|
|
|
@@ -413,7 +411,6 @@ describe("BuildOrchestrator.start", () => {
|
|
|
413
411
|
});
|
|
414
412
|
vi.mocked(createBuildEngine).mockReturnValue({
|
|
415
413
|
run: vi.fn().mockResolvedValue({
|
|
416
|
-
success: false,
|
|
417
414
|
build: { success: false, errors: ["Module not found"], warnings: [], diagnostics: [] },
|
|
418
415
|
}),
|
|
419
416
|
startWatch: vi.fn(),
|
|
@@ -603,7 +600,6 @@ describe("BuildOrchestrator client build", () => {
|
|
|
603
600
|
});
|
|
604
601
|
vi.mocked(createBuildEngine).mockReturnValue({
|
|
605
602
|
run: vi.fn().mockResolvedValue({
|
|
606
|
-
success: true,
|
|
607
603
|
build: { success: true, errors: [], warnings: [], diagnostics: [] },
|
|
608
604
|
}),
|
|
609
605
|
startWatch: vi.fn(),
|
|
@@ -637,7 +633,6 @@ describe("BuildOrchestrator client build", () => {
|
|
|
637
633
|
});
|
|
638
634
|
vi.mocked(createBuildEngine).mockReturnValue({
|
|
639
635
|
run: vi.fn().mockResolvedValue({
|
|
640
|
-
success: false,
|
|
641
636
|
build: { success: false, errors: ["Template error"], warnings: [], diagnostics: [] },
|
|
642
637
|
}),
|
|
643
638
|
startWatch: vi.fn(),
|
|
@@ -957,7 +952,6 @@ describe("BuildOrchestrator native build integration (Slice 1)", () => {
|
|
|
957
952
|
});
|
|
958
953
|
vi.mocked(createBuildEngine).mockReturnValue({
|
|
959
954
|
run: vi.fn().mockResolvedValue({
|
|
960
|
-
success: false,
|
|
961
955
|
build: { success: false, errors: ["Build error"], warnings: [], diagnostics: [] },
|
|
962
956
|
}),
|
|
963
957
|
startWatch: vi.fn(),
|
|
@@ -651,8 +651,9 @@ describe("DevWatchOrchestrator", () => {
|
|
|
651
651
|
expect.any(Object),
|
|
652
652
|
);
|
|
653
653
|
expect(mockBuildEngines[0].startWatch).toHaveBeenCalledWith({ js: true, dts: false, lint: false });
|
|
654
|
-
// Runtime
|
|
655
|
-
expect(Worker.create).
|
|
654
|
+
// Runtime starts during start() after all engines ready
|
|
655
|
+
expect(Worker.create).toHaveBeenCalled();
|
|
656
|
+
expect(mockRuntimeProxies[0].start).toHaveBeenCalled();
|
|
656
657
|
});
|
|
657
658
|
|
|
658
659
|
// --- Acceptance: Server 패키지 dev 모드 실행 ---
|
|
@@ -833,7 +834,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
833
834
|
});
|
|
834
835
|
|
|
835
836
|
// --- Acceptance: Server batchComplete 시 runtime 시작 ---
|
|
836
|
-
it("starts runtime
|
|
837
|
+
it("starts runtime during start() after all engines ready", async () => {
|
|
837
838
|
setupDefaults(createConfig({
|
|
838
839
|
packages: { "service-server": { target: "server" } },
|
|
839
840
|
}));
|
|
@@ -843,18 +844,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
843
844
|
await orchestrator.initialize();
|
|
844
845
|
await orchestrator.start();
|
|
845
846
|
|
|
846
|
-
//
|
|
847
|
-
expect(Worker.create).not.toHaveBeenCalled();
|
|
848
|
-
|
|
849
|
-
// Get batchComplete handler
|
|
850
|
-
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
851
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
852
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
853
|
-
|
|
854
|
-
batchHandler?.(["service-server:build"]);
|
|
855
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
856
|
-
|
|
857
|
-
// First runtime created via batchComplete
|
|
847
|
+
// Runtime starts during start(), not via batchComplete
|
|
858
848
|
expect(mockRuntimeProxies).toHaveLength(1);
|
|
859
849
|
expect(mockRuntimeProxies[0].start).toHaveBeenCalled();
|
|
860
850
|
});
|
|
@@ -870,12 +860,8 @@ describe("DevWatchOrchestrator", () => {
|
|
|
870
860
|
await orchestrator.initialize();
|
|
871
861
|
await orchestrator.start();
|
|
872
862
|
|
|
873
|
-
//
|
|
874
|
-
|
|
875
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
876
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
877
|
-
batchHandler?.(["service-server:build"]);
|
|
878
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
863
|
+
// Runtime is now started during start()
|
|
864
|
+
expect(mockRuntimeProxies).toHaveLength(1);
|
|
879
865
|
|
|
880
866
|
await orchestrator.shutdown();
|
|
881
867
|
|
|
@@ -911,7 +897,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
911
897
|
});
|
|
912
898
|
|
|
913
899
|
// Unit: multiple server packages
|
|
914
|
-
it("creates engines for multiple server packages (runtimes start
|
|
900
|
+
it("creates engines for multiple server packages (runtimes start during start())", async () => {
|
|
915
901
|
setupDefaults(createConfig({
|
|
916
902
|
packages: {
|
|
917
903
|
"service-server": { target: "server" },
|
|
@@ -925,8 +911,8 @@ describe("DevWatchOrchestrator", () => {
|
|
|
925
911
|
await orchestrator.start();
|
|
926
912
|
|
|
927
913
|
expect(createBuildEngine).toHaveBeenCalledTimes(2);
|
|
928
|
-
// Runtimes
|
|
929
|
-
expect(Worker.create).
|
|
914
|
+
// Runtimes start during start() after all engines ready
|
|
915
|
+
expect(Worker.create).toHaveBeenCalledTimes(2);
|
|
930
916
|
});
|
|
931
917
|
|
|
932
918
|
// Unit: skips start when no packages
|
|
@@ -1027,16 +1013,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1027
1013
|
await orchestrator.initialize();
|
|
1028
1014
|
await orchestrator.start();
|
|
1029
1015
|
|
|
1030
|
-
// Runtime starts
|
|
1031
|
-
expect(Worker.create).not.toHaveBeenCalled();
|
|
1032
|
-
|
|
1033
|
-
// Trigger batchComplete
|
|
1034
|
-
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
1035
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
1036
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
1037
|
-
batchHandler?.(["service-server:build"]);
|
|
1038
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1039
|
-
|
|
1016
|
+
// Runtime starts during start() with client ports available
|
|
1040
1017
|
expect(Worker.create).toHaveBeenCalled();
|
|
1041
1018
|
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
1042
1019
|
expect.objectContaining({
|
|
@@ -1059,13 +1036,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1059
1036
|
await orchestrator.initialize();
|
|
1060
1037
|
await orchestrator.start();
|
|
1061
1038
|
|
|
1062
|
-
//
|
|
1063
|
-
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
1064
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
1065
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
1066
|
-
batchHandler?.(["service-server:build"]);
|
|
1067
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1068
|
-
|
|
1039
|
+
// Runtime starts during start(), standalone client ports excluded
|
|
1069
1040
|
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
1070
1041
|
expect.objectContaining({
|
|
1071
1042
|
clientPorts: {},
|
|
@@ -1112,13 +1083,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1112
1083
|
await orchestrator.initialize();
|
|
1113
1084
|
await orchestrator.start();
|
|
1114
1085
|
|
|
1115
|
-
//
|
|
1116
|
-
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
1117
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
1118
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
1119
|
-
batchHandler?.(["service-server:build"]);
|
|
1120
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1121
|
-
|
|
1086
|
+
// Runtime starts during start() but client port unavailable → empty clientPorts
|
|
1122
1087
|
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
1123
1088
|
expect.objectContaining({
|
|
1124
1089
|
clientPorts: {},
|
|
@@ -1126,8 +1091,8 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1126
1091
|
);
|
|
1127
1092
|
});
|
|
1128
1093
|
|
|
1129
|
-
// --- Acceptance: printServers
|
|
1130
|
-
it("
|
|
1094
|
+
// --- Acceptance: printServers는 start()에서 직접 호출되지 않는다 (serverReady 이벤트 경로만 사용) ---
|
|
1095
|
+
it("does not call printServers directly during start()", async () => {
|
|
1131
1096
|
setupDefaults(createConfig({
|
|
1132
1097
|
packages: {
|
|
1133
1098
|
"service-server": { target: "server" },
|
|
@@ -1140,12 +1105,9 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1140
1105
|
await orchestrator.initialize();
|
|
1141
1106
|
await orchestrator.start();
|
|
1142
1107
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
);
|
|
1147
|
-
const serverClientsMap = vi.mocked(_printServers).mock.calls[0][1] as Map<string, string[]>;
|
|
1148
|
-
expect(serverClientsMap.get("service-server")).toEqual(["my-client"]);
|
|
1108
|
+
// printServers should NOT be called directly during start()
|
|
1109
|
+
// It should only be called via serverReady → _schedulePrintServers()
|
|
1110
|
+
expect(_printServers).not.toHaveBeenCalled();
|
|
1149
1111
|
});
|
|
1150
1112
|
|
|
1151
1113
|
// --- Acceptance: 서버 rebuild 시 프록시 재등록 ---
|
|
@@ -1162,14 +1124,7 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1162
1124
|
await orchestrator.initialize();
|
|
1163
1125
|
await orchestrator.start();
|
|
1164
1126
|
|
|
1165
|
-
//
|
|
1166
|
-
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
1167
|
-
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
1168
|
-
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
1169
|
-
batchHandler?.(["service-server:build"]);
|
|
1170
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
1171
|
-
|
|
1172
|
-
// First runtime created with clientPorts
|
|
1127
|
+
// First runtime created during start() with clientPorts
|
|
1173
1128
|
expect(mockRuntimeProxies).toHaveLength(1);
|
|
1174
1129
|
expect(mockRuntimeProxies[0].start).toHaveBeenCalledWith(
|
|
1175
1130
|
expect.objectContaining({
|
|
@@ -1177,11 +1132,14 @@ describe("DevWatchOrchestrator", () => {
|
|
|
1177
1132
|
}),
|
|
1178
1133
|
);
|
|
1179
1134
|
|
|
1180
|
-
// Trigger
|
|
1135
|
+
// Trigger batchComplete (rebuild after initial build)
|
|
1136
|
+
const rebuildInstance = vi.mocked(RebuildManager).mock.instances[0];
|
|
1137
|
+
const onCall = vi.mocked(rebuildInstance.on).mock.calls.find((c) => c[0] === "batchComplete");
|
|
1138
|
+
const batchHandler = onCall?.[1] as ((keys: string[]) => void) | undefined;
|
|
1181
1139
|
batchHandler?.(["service-server:build"]);
|
|
1182
1140
|
await new Promise((r) => setTimeout(r, 200));
|
|
1183
1141
|
|
|
1184
|
-
// Second runtime start should also have clientPorts
|
|
1142
|
+
// Second runtime start (rebuild) should also have clientPorts
|
|
1185
1143
|
expect(mockRuntimeProxies).toHaveLength(2);
|
|
1186
1144
|
expect(mockRuntimeProxies[0].terminate).toHaveBeenCalled();
|
|
1187
1145
|
expect(mockRuntimeProxies[1].start).toHaveBeenCalledWith(
|