@simplysm/sd-cli 14.0.10 → 14.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -253
- package/dist/angular/client-transform-stylesheet.js +1 -1
- package/dist/angular/client-transform-stylesheet.js.map +1 -1
- package/dist/angular/vite-angular-plugin.d.ts +1 -1
- package/dist/angular/vite-angular-plugin.d.ts.map +1 -1
- package/dist/angular/vite-angular-plugin.js +60 -34
- package/dist/angular/vite-angular-plugin.js.map +1 -1
- package/dist/angular/vite-postcss-inline-plugin.d.ts +1 -1
- package/dist/angular/vite-postcss-inline-plugin.js +1 -1
- package/dist/capacitor/capacitor.d.ts +14 -2
- package/dist/capacitor/capacitor.d.ts.map +1 -1
- package/dist/capacitor/capacitor.js +131 -17
- package/dist/capacitor/capacitor.js.map +1 -1
- package/dist/commands/build.d.ts +3 -10
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +3 -10
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/check.js +3 -3
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/dev.d.ts +3 -9
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +3 -9
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/device.d.ts +13 -0
- package/dist/commands/device.d.ts.map +1 -0
- package/dist/commands/device.js +53 -0
- package/dist/commands/device.js.map +1 -0
- package/dist/commands/publish.d.ts +1 -1
- package/dist/commands/publish.d.ts.map +1 -1
- package/dist/commands/publish.js +18 -26
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/replace-deps.d.ts +3 -3
- package/dist/commands/replace-deps.d.ts.map +1 -1
- package/dist/commands/replace-deps.js +1 -1
- package/dist/commands/typecheck.d.ts +4 -3
- package/dist/commands/typecheck.d.ts.map +1 -1
- package/dist/commands/typecheck.js +5 -11
- package/dist/commands/typecheck.js.map +1 -1
- package/dist/commands/watch.d.ts +9 -9
- package/dist/commands/watch.js +9 -9
- package/dist/electron/electron.d.ts.map +1 -1
- package/dist/electron/electron.js +42 -3
- package/dist/electron/electron.js.map +1 -1
- package/dist/engines/BaseEngine.d.ts +1 -1
- package/dist/engines/BaseEngine.d.ts.map +1 -1
- package/dist/engines/BaseEngine.js +3 -1
- package/dist/engines/BaseEngine.js.map +1 -1
- package/dist/engines/NgtscEngine.d.ts +7 -7
- package/dist/engines/NgtscEngine.d.ts.map +1 -1
- package/dist/engines/NgtscEngine.js +3 -3
- package/dist/engines/ServerEsbuildEngine.d.ts +7 -7
- package/dist/engines/ServerEsbuildEngine.d.ts.map +1 -1
- package/dist/engines/ServerEsbuildEngine.js +3 -3
- package/dist/engines/TscEngine.d.ts +7 -7
- package/dist/engines/TscEngine.d.ts.map +1 -1
- package/dist/engines/TscEngine.js +3 -3
- package/dist/engines/ViteEngine.d.ts +1 -1
- package/dist/engines/ViteEngine.d.ts.map +1 -1
- package/dist/engines/ViteEngine.js +7 -12
- package/dist/engines/ViteEngine.js.map +1 -1
- package/dist/engines/index.d.ts +5 -5
- package/dist/engines/index.js +5 -5
- package/dist/engines/types.d.ts +20 -20
- package/dist/engines/types.d.ts.map +1 -1
- package/dist/infra/ResultCollector.d.ts +9 -9
- package/dist/infra/ResultCollector.js +8 -8
- package/dist/infra/SignalHandler.d.ts +7 -7
- package/dist/infra/SignalHandler.js +7 -7
- package/dist/infra/WorkerManager.d.ts +14 -14
- package/dist/infra/WorkerManager.js +14 -14
- package/dist/orchestrators/BuildOrchestrator.d.ts +25 -25
- package/dist/orchestrators/BuildOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/BuildOrchestrator.js +29 -29
- package/dist/orchestrators/BuildOrchestrator.js.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.d.ts +7 -7
- package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
- package/dist/orchestrators/DevWatchOrchestrator.js +35 -57
- package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
- package/dist/sd-cli-entry.d.ts +2 -2
- package/dist/sd-cli-entry.d.ts.map +1 -1
- package/dist/sd-cli-entry.js +45 -9
- package/dist/sd-cli-entry.js.map +1 -1
- package/dist/sd-cli.d.ts +3 -3
- package/dist/sd-cli.js +16 -16
- package/dist/sd-cli.js.map +1 -1
- package/dist/sd-config.types.d.ts +105 -105
- package/dist/sd-config.types.d.ts.map +1 -1
- package/dist/utils/angular-compiler.js +5 -5
- package/dist/utils/angular-compiler.js.map +1 -1
- package/dist/utils/build-env.d.ts +1 -1
- package/dist/utils/build-env.js +1 -1
- package/dist/utils/concurrency.d.ts +7 -7
- package/dist/utils/concurrency.js +7 -7
- package/dist/utils/copy-public.d.ts +9 -9
- package/dist/utils/copy-public.js +17 -17
- package/dist/utils/copy-public.js.map +1 -1
- package/dist/utils/copy-src.d.ts +9 -9
- package/dist/utils/copy-src.js +11 -11
- package/dist/utils/copy-src.js.map +1 -1
- package/dist/utils/engine-stop.d.ts +8 -9
- package/dist/utils/engine-stop.d.ts.map +1 -1
- package/dist/utils/engine-stop.js +9 -10
- package/dist/utils/engine-stop.js.map +1 -1
- package/dist/utils/esbuild-config.d.ts +23 -23
- package/dist/utils/esbuild-config.d.ts.map +1 -1
- package/dist/utils/esbuild-config.js +25 -25
- package/dist/utils/esbuild-config.js.map +1 -1
- package/dist/utils/lint-with-program.d.ts +15 -15
- package/dist/utils/lint-with-program.d.ts.map +1 -1
- package/dist/utils/lint-with-program.js +29 -29
- package/dist/utils/lint-with-program.js.map +1 -1
- package/dist/utils/ngtsc-build-core.d.ts +8 -8
- package/dist/utils/ngtsc-build-core.d.ts.map +1 -1
- package/dist/utils/ngtsc-build-core.js +14 -14
- package/dist/utils/ngtsc-build-core.js.map +1 -1
- package/dist/utils/output-path-rewriter.d.ts +14 -14
- package/dist/utils/output-path-rewriter.js +18 -18
- package/dist/utils/output-path-rewriter.js.map +1 -1
- package/dist/utils/output-utils.d.ts +6 -6
- package/dist/utils/output-utils.js +11 -11
- package/dist/utils/output-utils.js.map +1 -1
- package/dist/utils/package-utils.d.ts +21 -21
- package/dist/utils/package-utils.d.ts.map +1 -1
- package/dist/utils/package-utils.js +56 -45
- package/dist/utils/package-utils.js.map +1 -1
- package/dist/utils/replace-deps.d.ts +25 -25
- package/dist/utils/replace-deps.d.ts.map +1 -1
- package/dist/utils/replace-deps.js +84 -65
- package/dist/utils/replace-deps.js.map +1 -1
- package/dist/utils/sd-config.d.ts +3 -3
- package/dist/utils/sd-config.js +3 -3
- package/dist/utils/tsc-build.d.ts +13 -13
- package/dist/utils/tsc-build.d.ts.map +1 -1
- package/dist/utils/tsc-build.js +9 -9
- package/dist/utils/tsc-build.js.map +1 -1
- package/dist/utils/tsconfig.d.ts +11 -9
- package/dist/utils/tsconfig.d.ts.map +1 -1
- package/dist/utils/tsconfig.js +11 -9
- package/dist/utils/tsconfig.js.map +1 -1
- package/dist/utils/typecheck-non-package.d.ts +5 -6
- package/dist/utils/typecheck-non-package.d.ts.map +1 -1
- package/dist/utils/typecheck-non-package.js +7 -8
- package/dist/utils/typecheck-non-package.js.map +1 -1
- package/dist/utils/typecheck-serialization.d.ts +8 -8
- package/dist/utils/typecheck-serialization.d.ts.map +1 -1
- package/dist/utils/typecheck-serialization.js +12 -16
- package/dist/utils/typecheck-serialization.js.map +1 -1
- package/dist/utils/vite-config.d.ts +8 -5
- package/dist/utils/vite-config.d.ts.map +1 -1
- package/dist/utils/vite-config.js +36 -29
- package/dist/utils/vite-config.js.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.d.ts.map +1 -1
- package/dist/utils/vite-scope-watch-plugin.js +1 -1
- package/dist/utils/vite-scope-watch-plugin.js.map +1 -1
- package/dist/utils/worker-events.d.ts +12 -12
- package/dist/utils/worker-events.d.ts.map +1 -1
- package/dist/utils/worker-events.js +10 -10
- package/dist/utils/worker-events.js.map +1 -1
- package/dist/utils/worker-utils.d.ts +12 -13
- package/dist/utils/worker-utils.d.ts.map +1 -1
- package/dist/utils/worker-utils.js +12 -13
- package/dist/utils/worker-utils.js.map +1 -1
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js +5 -7
- package/dist/vitest-plugin.js.map +1 -1
- package/dist/workers/client.worker.d.ts +4 -2
- package/dist/workers/client.worker.d.ts.map +1 -1
- package/dist/workers/client.worker.js +209 -1
- package/dist/workers/client.worker.js.map +1 -1
- package/dist/workers/library-build.worker.d.ts +1 -1
- package/dist/workers/library-build.worker.d.ts.map +1 -1
- package/dist/workers/library-build.worker.js +7 -7
- package/dist/workers/library-build.worker.js.map +1 -1
- package/dist/workers/lint.worker.d.ts +2 -2
- package/dist/workers/lint.worker.js +2 -2
- package/dist/workers/ngtsc-build.worker.js +30 -30
- package/dist/workers/ngtsc-build.worker.js.map +1 -1
- package/dist/workers/server-build.worker.d.ts +17 -17
- package/dist/workers/server-build.worker.d.ts.map +1 -1
- package/dist/workers/server-build.worker.js +46 -46
- package/dist/workers/server-build.worker.js.map +1 -1
- package/dist/workers/server-runtime.worker.d.ts +7 -7
- package/dist/workers/server-runtime.worker.d.ts.map +1 -1
- package/dist/workers/server-runtime.worker.js +17 -17
- package/dist/workers/server-runtime.worker.js.map +1 -1
- package/docs/config.md +340 -0
- package/docs/publish-configuration-types.md +87 -0
- package/docs/pwa-configuration-types.md +55 -0
- package/docs/vitest-plugin.md +47 -0
- package/package.json +9 -7
- package/src/angular/client-transform-stylesheet.ts +1 -1
- package/src/angular/vite-angular-plugin.ts +70 -37
- package/src/angular/vite-postcss-inline-plugin.ts +1 -1
- package/src/capacitor/capacitor.ts +159 -23
- package/src/commands/build.ts +3 -10
- package/src/commands/check.ts +3 -3
- package/src/commands/dev.ts +3 -9
- package/src/commands/device.ts +65 -0
- package/src/commands/publish.ts +30 -26
- package/src/commands/replace-deps.ts +3 -3
- package/src/commands/typecheck.ts +7 -13
- package/src/commands/watch.ts +9 -9
- package/src/electron/electron.ts +49 -4
- package/src/engines/BaseEngine.ts +4 -1
- package/src/engines/NgtscEngine.ts +7 -7
- package/src/engines/ServerEsbuildEngine.ts +7 -7
- package/src/engines/TscEngine.ts +7 -7
- package/src/engines/ViteEngine.ts +8 -13
- package/src/engines/index.ts +5 -5
- package/src/engines/types.ts +20 -20
- package/src/infra/ResultCollector.ts +9 -9
- package/src/infra/SignalHandler.ts +7 -7
- package/src/infra/WorkerManager.ts +14 -14
- package/src/orchestrators/BuildOrchestrator.ts +37 -37
- package/src/orchestrators/DevWatchOrchestrator.ts +37 -61
- package/src/sd-cli-entry.ts +51 -9
- package/src/sd-cli.ts +16 -16
- package/src/sd-config.types.ts +107 -107
- package/src/utils/angular-compiler.ts +5 -5
- package/src/utils/build-env.ts +1 -1
- package/src/utils/concurrency.ts +7 -7
- package/src/utils/copy-public.ts +17 -17
- package/src/utils/copy-src.ts +11 -11
- package/src/utils/engine-stop.ts +9 -10
- package/src/utils/esbuild-config.ts +29 -29
- package/src/utils/lint-with-program.ts +34 -34
- package/src/utils/ngtsc-build-core.ts +17 -17
- package/src/utils/output-path-rewriter.ts +18 -18
- package/src/utils/output-utils.ts +11 -11
- package/src/utils/package-utils.ts +57 -45
- package/src/utils/replace-deps.ts +92 -67
- package/src/utils/sd-config.ts +3 -3
- package/src/utils/tsc-build.ts +18 -18
- package/src/utils/tsconfig.ts +11 -9
- package/src/utils/typecheck-non-package.ts +7 -8
- package/src/utils/typecheck-serialization.ts +13 -15
- package/src/utils/vite-config.ts +45 -35
- package/src/utils/vite-scope-watch-plugin.ts +6 -1
- package/src/utils/worker-events.ts +16 -16
- package/src/utils/worker-utils.ts +12 -13
- package/src/vitest-plugin.ts +5 -8
- package/src/workers/client.worker.ts +236 -2
- package/src/workers/library-build.worker.ts +8 -8
- package/src/workers/lint.worker.ts +2 -2
- package/src/workers/ngtsc-build.worker.ts +31 -31
- package/src/workers/server-build.worker.ts +60 -60
- package/src/workers/server-runtime.worker.ts +22 -22
- package/tests/angular/vite-angular-plugin-hmr-fallback.spec.ts +1 -0
- package/tests/angular/vite-angular-plugin-hmr.spec.ts +78 -0
- package/tests/angular/vite-angular-plugin.spec.ts +67 -0
- package/tests/capacitor/capacitor-build.spec.ts +6 -4
- package/tests/capacitor/capacitor-icon.spec.ts +7 -5
- package/tests/capacitor/capacitor-init.spec.ts +120 -10
- package/tests/capacitor/capacitor-run.spec.ts +14 -17
- package/tests/capacitor/capacitor-workspace.spec.ts +5 -3
- package/tests/commands/check.spec.ts +2 -2
- package/tests/commands/device.spec.ts +147 -0
- package/tests/commands/publish.spec.ts +2 -2
- package/tests/commands/typecheck.spec.ts +8 -0
- package/tests/electron/electron.spec.ts +12 -10
- package/tests/engines/base-engine.spec.ts +37 -0
- package/tests/engines/vite-engine.spec.ts +115 -3
- package/tests/orchestrators/dev-watch-orchestrator.spec.ts +21 -93
- package/tests/utils/vite-config.spec.ts +144 -90
- package/tests/workers/client-worker.spec.ts +690 -0
- package/tests/workers/server-build-worker.spec.ts +3 -3
|
@@ -27,8 +27,8 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
27
27
|
copy: mockFsxCopy,
|
|
28
28
|
},
|
|
29
29
|
cpx: {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
spawn: mockCpxSpawn,
|
|
31
|
+
spawnSync: vi.fn().mockReturnValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
32
32
|
},
|
|
33
33
|
pathx: {
|
|
34
34
|
posixResolve: (...args: string[]) => path.resolve(...args).replace(/\\/g, "/"),
|
|
@@ -49,7 +49,7 @@ const execaCalls: { command: string; args: string[] }[] = [];
|
|
|
49
49
|
let execaFactory: (...args: unknown[]) => Promise<{ stdout: string; stderr: string; exitCode: number }> = () =>
|
|
50
50
|
Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
|
|
51
51
|
|
|
52
|
-
const
|
|
52
|
+
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
53
53
|
execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
|
|
54
54
|
return execaFactory(...args);
|
|
55
55
|
});
|
|
@@ -84,7 +84,9 @@ vi.mock("consola", () => ({
|
|
|
84
84
|
warn: mockLoggerWarn,
|
|
85
85
|
info: vi.fn(),
|
|
86
86
|
}),
|
|
87
|
+
level: 0,
|
|
87
88
|
},
|
|
89
|
+
LogLevels: { debug: 4 },
|
|
88
90
|
}));
|
|
89
91
|
|
|
90
92
|
//#endregion
|
|
@@ -175,14 +177,14 @@ describe("Capacitor.run()", () => {
|
|
|
175
177
|
|
|
176
178
|
// cap copy android + cap run android
|
|
177
179
|
const capCmds = execaCalls.filter(
|
|
178
|
-
(c) => c.command === "
|
|
180
|
+
(c) => c.command === "pnpm" && c.args.includes("cap"),
|
|
179
181
|
);
|
|
180
182
|
expect(capCmds.some((c) => c.args.includes("copy") && c.args.includes("android"))).toBe(true);
|
|
181
183
|
expect(capCmds.some((c) => c.args.includes("run") && c.args.includes("android"))).toBe(true);
|
|
182
184
|
});
|
|
183
185
|
|
|
184
|
-
// Unit: adb kill-server
|
|
185
|
-
it("
|
|
186
|
+
// Unit: cap run 실패 시 adb kill-server 호출 후 에러를 re-throw한다
|
|
187
|
+
it("calls adb kill-server and re-throws on android platform cap run failure", async () => {
|
|
186
188
|
const { Capacitor } = await import("../../src/capacitor/capacitor.js");
|
|
187
189
|
|
|
188
190
|
let capRunCallCount = 0;
|
|
@@ -191,16 +193,13 @@ describe("Capacitor.run()", () => {
|
|
|
191
193
|
const cmdArgs = (args[1] as string[] | undefined) ?? [];
|
|
192
194
|
|
|
193
195
|
if (
|
|
194
|
-
cmd === "
|
|
196
|
+
cmd === "pnpm" &&
|
|
195
197
|
cmdArgs.includes("cap") &&
|
|
196
198
|
cmdArgs.includes("run") &&
|
|
197
199
|
cmdArgs.includes("android")
|
|
198
200
|
) {
|
|
199
201
|
capRunCallCount++;
|
|
200
|
-
|
|
201
|
-
// First cap run fails
|
|
202
|
-
return Promise.reject(new Error("cap run failed"));
|
|
203
|
-
}
|
|
202
|
+
return Promise.reject(new Error("cap run failed"));
|
|
204
203
|
}
|
|
205
204
|
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
|
|
206
205
|
};
|
|
@@ -211,16 +210,14 @@ describe("Capacitor.run()", () => {
|
|
|
211
210
|
platform: { android: {} },
|
|
212
211
|
});
|
|
213
212
|
|
|
214
|
-
await cap.run("http://localhost:4200");
|
|
213
|
+
await expect(cap.run("http://localhost:4200")).rejects.toThrow("cap run failed");
|
|
215
214
|
|
|
216
|
-
// adb kill-server should have been called
|
|
215
|
+
// adb kill-server should have been called
|
|
217
216
|
expect(
|
|
218
217
|
execaCalls.some((c) => c.command === "adb" && c.args.includes("kill-server")),
|
|
219
218
|
).toBe(true);
|
|
220
|
-
|
|
221
|
-
expect(
|
|
222
|
-
expect.stringContaining("adb kill-server"),
|
|
223
|
-
);
|
|
219
|
+
// cap run은 한 번만 호출 (재시도 없음)
|
|
220
|
+
expect(capRunCallCount).toBe(1);
|
|
224
221
|
});
|
|
225
222
|
|
|
226
223
|
// Unit: _updateServerUrl replaces existing url
|
|
@@ -26,8 +26,8 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
26
26
|
copy: mockFsxCopy,
|
|
27
27
|
},
|
|
28
28
|
cpx: {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
spawn: mockCpxSpawn,
|
|
30
|
+
spawnSync: vi.fn().mockReturnValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
31
31
|
},
|
|
32
32
|
pathx: {
|
|
33
33
|
posixResolve: (...args: string[]) => path.resolve(...args).replace(/\\/g, "/"),
|
|
@@ -43,7 +43,7 @@ vi.mock("@simplysm/core-common", () => ({
|
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
45
|
const execaCalls: { command: string; args: string[] }[] = [];
|
|
46
|
-
const
|
|
46
|
+
const mockCpxSpawn = vi.fn((...args: unknown[]) => {
|
|
47
47
|
execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
|
|
48
48
|
return Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
|
|
49
49
|
});
|
|
@@ -75,7 +75,9 @@ vi.mock("fs/promises", () => ({
|
|
|
75
75
|
vi.mock("consola", () => ({
|
|
76
76
|
consola: {
|
|
77
77
|
withTag: () => ({ debug: vi.fn(), warn: vi.fn() }),
|
|
78
|
+
level: 0,
|
|
78
79
|
},
|
|
80
|
+
LogLevels: { debug: 4 },
|
|
79
81
|
}));
|
|
80
82
|
|
|
81
83
|
//#endregion
|
|
@@ -37,8 +37,8 @@ vi.mock("../../src/utils/sd-config", () => ({
|
|
|
37
37
|
|
|
38
38
|
vi.mock("@simplysm/core-node", () => ({
|
|
39
39
|
cpx: {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
spawn: mocks.execa,
|
|
41
|
+
spawnSync: vi.fn().mockReturnValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
42
42
|
},
|
|
43
43
|
}));
|
|
44
44
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Capacitor mock
|
|
4
|
+
const mockCapacitorInstance = {
|
|
5
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
6
|
+
run: vi.fn().mockResolvedValue(undefined),
|
|
7
|
+
build: vi.fn().mockResolvedValue(undefined),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
vi.mock("../../src/capacitor/capacitor", () => ({
|
|
11
|
+
Capacitor: {
|
|
12
|
+
create: vi.fn().mockResolvedValue(mockCapacitorInstance),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Electron mock
|
|
17
|
+
const mockElectronInstance = {
|
|
18
|
+
initialize: vi.fn().mockResolvedValue(undefined),
|
|
19
|
+
run: vi.fn().mockResolvedValue(undefined),
|
|
20
|
+
build: vi.fn().mockResolvedValue(undefined),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
vi.mock("../../src/electron/electron", () => ({
|
|
24
|
+
Electron: {
|
|
25
|
+
create: vi.fn().mockResolvedValue(mockElectronInstance),
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// loadSdConfig mock
|
|
30
|
+
vi.mock("../../src/utils/sd-config", () => ({
|
|
31
|
+
loadSdConfig: vi.fn(),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const { Capacitor } = await import("../../src/capacitor/capacitor");
|
|
35
|
+
const { Electron } = await import("../../src/electron/electron");
|
|
36
|
+
const { loadSdConfig } = await import("../../src/utils/sd-config");
|
|
37
|
+
const { runDevice } = await import("../../src/commands/device");
|
|
38
|
+
|
|
39
|
+
describe("runDevice", () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
mockCapacitorInstance.run.mockResolvedValue(undefined);
|
|
43
|
+
mockElectronInstance.run.mockResolvedValue(undefined);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Acceptance: Scenario "device 명령어로 Capacitor 앱 실행"
|
|
47
|
+
it("runs Capacitor.create + run when capacitor config exists", async () => {
|
|
48
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
49
|
+
packages: {
|
|
50
|
+
"client-pda": {
|
|
51
|
+
target: "client",
|
|
52
|
+
server: 40480,
|
|
53
|
+
capacitor: { appId: "com.test.app", appName: "TestApp" },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await runDevice({ package: "client-pda", options: [] });
|
|
59
|
+
|
|
60
|
+
expect(Capacitor.create).toHaveBeenCalledWith(
|
|
61
|
+
expect.stringContaining("client-pda"),
|
|
62
|
+
{ appId: "com.test.app", appName: "TestApp" },
|
|
63
|
+
undefined,
|
|
64
|
+
);
|
|
65
|
+
expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://localhost:40480");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Acceptance: Scenario "device 명령어로 Electron 앱 실행"
|
|
69
|
+
it("runs Electron.create + run when electron config exists", async () => {
|
|
70
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
71
|
+
packages: {
|
|
72
|
+
"my-client": {
|
|
73
|
+
target: "client",
|
|
74
|
+
server: 4200,
|
|
75
|
+
electron: { appId: "com.test.electron" },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await runDevice({ package: "my-client", options: [] });
|
|
81
|
+
|
|
82
|
+
expect(Electron.create).toHaveBeenCalledWith(
|
|
83
|
+
expect.stringContaining("my-client"),
|
|
84
|
+
{ appId: "com.test.electron" },
|
|
85
|
+
undefined,
|
|
86
|
+
);
|
|
87
|
+
expect(mockElectronInstance.run).toHaveBeenCalledWith("http://localhost:4200");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Acceptance: Scenario "device 명령어에 URL 옵션 지정"
|
|
91
|
+
it("uses provided URL instead of auto-generated one", async () => {
|
|
92
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
93
|
+
packages: {
|
|
94
|
+
"client-pda": {
|
|
95
|
+
target: "client",
|
|
96
|
+
server: 40480,
|
|
97
|
+
capacitor: { appId: "com.test.app", appName: "TestApp" },
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await runDevice({ package: "client-pda", url: "http://192.168.1.100:4200", options: [] });
|
|
103
|
+
|
|
104
|
+
expect(mockCapacitorInstance.run).toHaveBeenCalledWith("http://192.168.1.100:4200");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Unit: electron이 capacitor보다 우선 (v13 동작)
|
|
108
|
+
it("prefers electron over capacitor when both are configured", async () => {
|
|
109
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
110
|
+
packages: {
|
|
111
|
+
"my-client": {
|
|
112
|
+
target: "client",
|
|
113
|
+
server: 4200,
|
|
114
|
+
capacitor: { appId: "com.test.app", appName: "TestApp" },
|
|
115
|
+
electron: { appId: "com.test.electron" },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
await runDevice({ package: "my-client", options: [] });
|
|
121
|
+
|
|
122
|
+
expect(Electron.create).toHaveBeenCalled();
|
|
123
|
+
expect(Capacitor.create).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Unit: 존재하지 않는 패키지 에러
|
|
127
|
+
it("throws when package does not exist", async () => {
|
|
128
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
129
|
+
packages: {
|
|
130
|
+
"other-pkg": { target: "node" },
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await expect(runDevice({ package: "nonexistent", options: [] })).rejects.toThrow();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Unit: client가 아닌 패키지 에러
|
|
138
|
+
it("throws when package is not a client target", async () => {
|
|
139
|
+
vi.mocked(loadSdConfig).mockResolvedValue({
|
|
140
|
+
packages: {
|
|
141
|
+
"my-server": { target: "server" },
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await expect(runDevice({ package: "my-server", options: [] })).rejects.toThrow();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -51,8 +51,8 @@ vi.mock("../../src/utils/replace-deps", () => ({
|
|
|
51
51
|
vi.mock("@simplysm/core-node", () => ({
|
|
52
52
|
fsx: mocks.fsx,
|
|
53
53
|
cpx: {
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
spawn: mocks.execa,
|
|
55
|
+
spawnSync: vi.fn().mockReturnValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
56
56
|
},
|
|
57
57
|
}));
|
|
58
58
|
|
|
@@ -493,6 +493,14 @@ describe("executeTypecheck", () => {
|
|
|
493
493
|
|
|
494
494
|
//#endregion
|
|
495
495
|
|
|
496
|
+
it("throws when loadSdConfig fails (fail fast)", async () => {
|
|
497
|
+
mocks.loadSdConfig.mockRejectedValue(new Error("sd.config.ts not found"));
|
|
498
|
+
mocks.discoverWorkspacePackages.mockReturnValue(new Map());
|
|
499
|
+
mocks.mergeTestsPackagesIntoConfig.mockReturnValue({ merged: {}, pathMap: new Map() });
|
|
500
|
+
|
|
501
|
+
await expect(executeTypecheck({ targets: [], options: [] })).rejects.toThrow("sd.config.ts not found");
|
|
502
|
+
});
|
|
503
|
+
|
|
496
504
|
//#region Slice 1: executeTypecheck lint integration (Feature 3.2)
|
|
497
505
|
|
|
498
506
|
describe("lint integration", () => {
|
|
@@ -23,8 +23,8 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
23
23
|
glob: mockFsxGlob,
|
|
24
24
|
},
|
|
25
25
|
cpx: {
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
spawn: mockCpxSpawn,
|
|
27
|
+
spawnSync: vi.fn().mockReturnValue({ stdout: "", stderr: "", exitCode: 0 }),
|
|
28
28
|
},
|
|
29
29
|
pathx: {
|
|
30
30
|
posixResolve: (...args: string[]) => path.resolve(...args).replace(/\\/g, "/"),
|
|
@@ -33,7 +33,7 @@ vi.mock("@simplysm/core-node", () => ({
|
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
35
|
// cpx mock (was execa)
|
|
36
|
-
const
|
|
36
|
+
const mockCpxSpawn = vi.fn().mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 });
|
|
37
37
|
|
|
38
38
|
// esbuild mock
|
|
39
39
|
const mockEsbuildBuild = vi.fn().mockResolvedValue({});
|
|
@@ -75,7 +75,9 @@ vi.mock("consola", () => ({
|
|
|
75
75
|
warn: mockLoggerWarn,
|
|
76
76
|
info: mockLoggerInfo,
|
|
77
77
|
}),
|
|
78
|
+
level: 0,
|
|
78
79
|
},
|
|
80
|
+
LogLevels: { debug: 4 },
|
|
79
81
|
}));
|
|
80
82
|
|
|
81
83
|
//#endregion
|
|
@@ -105,7 +107,7 @@ function setupDefaultMocks() {
|
|
|
105
107
|
}
|
|
106
108
|
return Promise.resolve([]);
|
|
107
109
|
});
|
|
108
|
-
|
|
110
|
+
mockCpxSpawn.mockResolvedValue({ stdout: "", stderr: "", exitCode: 0 });
|
|
109
111
|
mockEsbuildBuild.mockResolvedValue({});
|
|
110
112
|
}
|
|
111
113
|
|
|
@@ -179,7 +181,7 @@ describe("Electron", () => {
|
|
|
179
181
|
|
|
180
182
|
expect(findElectronPackageJson()).toBeDefined();
|
|
181
183
|
|
|
182
|
-
const execaCalls =
|
|
184
|
+
const execaCalls = mockCpxSpawn.mock.calls;
|
|
183
185
|
expect(
|
|
184
186
|
execaCalls.find((c) => c[0] === "npm" && (c[1] as string[]).includes("install")),
|
|
185
187
|
).toBeDefined();
|
|
@@ -196,7 +198,7 @@ describe("Electron", () => {
|
|
|
196
198
|
const electron = await Electron.create(PKG_PATH, { appId: "com.test.app" });
|
|
197
199
|
await electron.initialize();
|
|
198
200
|
|
|
199
|
-
const rebuildCall =
|
|
201
|
+
const rebuildCall = mockCpxSpawn.mock.calls.find(
|
|
200
202
|
(c) => typeof c[0] === "string" && c[0].includes("electron-rebuild"),
|
|
201
203
|
);
|
|
202
204
|
expect(rebuildCall).toBeUndefined();
|
|
@@ -453,7 +455,7 @@ describe("Electron", () => {
|
|
|
453
455
|
const electronKill = vi.fn();
|
|
454
456
|
let resolveElectron: () => void = () => {};
|
|
455
457
|
|
|
456
|
-
|
|
458
|
+
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
457
459
|
if (typeof cmd === "string" && cmd.includes("electron")) {
|
|
458
460
|
// Electron process: create a deferred promise we can resolve externally
|
|
459
461
|
const p = new Promise<void>((resolve) => {
|
|
@@ -535,7 +537,7 @@ describe("Electron", () => {
|
|
|
535
537
|
describe("단위: run() 플러그인 동작", () => {
|
|
536
538
|
it("passes custom env and ELECTRON_DEV_URL via esbuild banner", async () => {
|
|
537
539
|
let resolveElectron: () => void = () => {};
|
|
538
|
-
|
|
540
|
+
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
539
541
|
if (typeof cmd === "string" && cmd.includes("electron")) {
|
|
540
542
|
const p = new Promise<void>((resolve) => {
|
|
541
543
|
resolveElectron = resolve;
|
|
@@ -568,7 +570,7 @@ describe("Electron", () => {
|
|
|
568
570
|
|
|
569
571
|
it("calls initialize() before starting esbuild context", async () => {
|
|
570
572
|
let resolveElectron: () => void = () => {};
|
|
571
|
-
|
|
573
|
+
mockCpxSpawn.mockImplementation((cmd: string) => {
|
|
572
574
|
if (typeof cmd === "string" && cmd.includes("electron")) {
|
|
573
575
|
const p = new Promise<void>((resolve) => {
|
|
574
576
|
resolveElectron = resolve;
|
|
@@ -588,7 +590,7 @@ describe("Electron", () => {
|
|
|
588
590
|
await runPromise;
|
|
589
591
|
|
|
590
592
|
// initialize calls npm install -> execa should have been called with npm install
|
|
591
|
-
const npmInstallCall =
|
|
593
|
+
const npmInstallCall = mockCpxSpawn.mock.calls.find(
|
|
592
594
|
(c: any[]) => c[0] === "npm" && (c[1] as string[]).includes("install"),
|
|
593
595
|
);
|
|
594
596
|
expect(npmInstallCall).toBeDefined();
|
|
@@ -263,6 +263,43 @@ describe("BaseEngine", () => {
|
|
|
263
263
|
await engine.stop();
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
+
it("calls resolver on error event to release RebuildManager batch", async () => {
|
|
267
|
+
const mockResolver = vi.fn();
|
|
268
|
+
const mockRebuildManager = { registerBuild: vi.fn(() => mockResolver) };
|
|
269
|
+
|
|
270
|
+
mockWorker.startWatch.mockImplementation(() => {
|
|
271
|
+
// Trigger initial build to move past isInitialBuild
|
|
272
|
+
const buildHandler = mockWorker.on.mock.calls.find(
|
|
273
|
+
(call: any[]) => call[0] === "build",
|
|
274
|
+
)?.[1];
|
|
275
|
+
buildHandler?.({ build: { success: true } });
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const engine = new TscEngine({
|
|
279
|
+
cwd: "/root",
|
|
280
|
+
pkg: createMockPkg(),
|
|
281
|
+
rebuildManager: mockRebuildManager as any,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await engine.startWatch({ js: true, dts: true });
|
|
285
|
+
|
|
286
|
+
// Simulate rebuild cycle: buildStart -> error (no build event)
|
|
287
|
+
const buildStartHandler = mockWorker.on.mock.calls.find(
|
|
288
|
+
(call: any[]) => call[0] === "buildStart",
|
|
289
|
+
)?.[1];
|
|
290
|
+
const errorHandler = mockWorker.on.mock.calls.find(
|
|
291
|
+
(call: any[]) => call[0] === "error",
|
|
292
|
+
)?.[1];
|
|
293
|
+
|
|
294
|
+
buildStartHandler?.({});
|
|
295
|
+
expect(mockRebuildManager.registerBuild).toHaveBeenCalled();
|
|
296
|
+
|
|
297
|
+
errorHandler?.({ message: "Worker crashed" });
|
|
298
|
+
expect(mockResolver).toHaveBeenCalled();
|
|
299
|
+
|
|
300
|
+
await engine.stop();
|
|
301
|
+
});
|
|
302
|
+
|
|
266
303
|
it("uses _getTarget() for BuildResult target", async () => {
|
|
267
304
|
const mockResultCollector = { add: vi.fn() };
|
|
268
305
|
|
|
@@ -191,6 +191,30 @@ describe("ViteEngine", () => {
|
|
|
191
191
|
await engine.stop();
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
+
// Acceptance: Scenario "exclude 전달 (build)"
|
|
195
|
+
it("passes exclude from config to worker build call", async () => {
|
|
196
|
+
mockWorker.build.mockResolvedValue({ success: true });
|
|
197
|
+
|
|
198
|
+
const engine = new ViteEngine({
|
|
199
|
+
cwd: "/root",
|
|
200
|
+
pkg: createMockPkg({
|
|
201
|
+
config: {
|
|
202
|
+
target: "client",
|
|
203
|
+
server: "my-server",
|
|
204
|
+
exclude: ["jeep-sqlite"],
|
|
205
|
+
} as any,
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
await engine.run({ js: true, dts: false });
|
|
209
|
+
|
|
210
|
+
expect(mockWorker.build).toHaveBeenCalledWith(
|
|
211
|
+
expect.objectContaining({
|
|
212
|
+
exclude: ["jeep-sqlite"],
|
|
213
|
+
}),
|
|
214
|
+
);
|
|
215
|
+
await engine.stop();
|
|
216
|
+
});
|
|
217
|
+
|
|
194
218
|
// Unit: build failure reflects in result
|
|
195
219
|
it("reflects build failure in result", async () => {
|
|
196
220
|
mockWorker.build.mockResolvedValue({
|
|
@@ -265,11 +289,18 @@ describe("ViteEngine", () => {
|
|
|
265
289
|
await engine.stop();
|
|
266
290
|
});
|
|
267
291
|
|
|
268
|
-
// Acceptance: Scenario "ResultCollector에 결과 보고"
|
|
269
|
-
it("reports build result to ResultCollector", async () => {
|
|
292
|
+
// Acceptance: Scenario "ResultCollector에 결과 보고 — build 이벤트 경유"
|
|
293
|
+
it("reports build result to ResultCollector via build event only", async () => {
|
|
270
294
|
const mockResultCollector = { add: vi.fn() };
|
|
271
295
|
|
|
272
|
-
mockWorker.startWatch.
|
|
296
|
+
mockWorker.startWatch.mockImplementation(() => {
|
|
297
|
+
// Simulate "build" event during startWatch (Angular plugin buildStart)
|
|
298
|
+
const buildHandler = mockWorker.on.mock.calls.find(
|
|
299
|
+
(call: any[]) => call[0] === "build",
|
|
300
|
+
)?.[1];
|
|
301
|
+
buildHandler?.({ success: true });
|
|
302
|
+
return Promise.resolve({ success: true });
|
|
303
|
+
});
|
|
273
304
|
|
|
274
305
|
const engine = new ViteEngine({
|
|
275
306
|
cwd: "/root",
|
|
@@ -388,6 +419,32 @@ describe("ViteEngine", () => {
|
|
|
388
419
|
await engine.stop();
|
|
389
420
|
});
|
|
390
421
|
|
|
422
|
+
// Acceptance: Scenario "exclude 전달 (watch)"
|
|
423
|
+
it("passes exclude from config to worker startWatch call", async () => {
|
|
424
|
+
mockWorker.startWatch.mockResolvedValue({ success: true });
|
|
425
|
+
|
|
426
|
+
const engine = new ViteEngine({
|
|
427
|
+
cwd: "/root",
|
|
428
|
+
pkg: createMockPkg({
|
|
429
|
+
config: {
|
|
430
|
+
target: "client",
|
|
431
|
+
server: "my-server",
|
|
432
|
+
exclude: ["jeep-sqlite", "another-pkg"],
|
|
433
|
+
} as any,
|
|
434
|
+
}),
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
await engine.startWatch({ js: true, dts: false });
|
|
438
|
+
|
|
439
|
+
expect(mockWorker.startWatch).toHaveBeenCalledWith(
|
|
440
|
+
expect.objectContaining({
|
|
441
|
+
exclude: ["jeep-sqlite", "another-pkg"],
|
|
442
|
+
}),
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
await engine.stop();
|
|
446
|
+
});
|
|
447
|
+
|
|
391
448
|
// Unit: server: string does not pass port
|
|
392
449
|
it("does not pass port when config.server is a string", async () => {
|
|
393
450
|
mockWorker.startWatch.mockResolvedValue({ success: true });
|
|
@@ -480,6 +537,61 @@ describe("ViteEngine", () => {
|
|
|
480
537
|
await engine.stop();
|
|
481
538
|
});
|
|
482
539
|
|
|
540
|
+
// Unit: mock worker가 serverReady를 발행하지 않으면 port가 undefined로 남는다
|
|
541
|
+
it("leaves port undefined when worker mock does not emit serverReady for legacyModule", async () => {
|
|
542
|
+
mockWorker.startWatch.mockResolvedValue({ success: true });
|
|
543
|
+
|
|
544
|
+
const engine = new ViteEngine({
|
|
545
|
+
cwd: "/root",
|
|
546
|
+
pkg: createMockPkg({
|
|
547
|
+
config: {
|
|
548
|
+
target: "client",
|
|
549
|
+
server: "my-server",
|
|
550
|
+
browserSupport: { legacyModule: true },
|
|
551
|
+
} as any,
|
|
552
|
+
}),
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
await engine.startWatch({ js: true, dts: false });
|
|
556
|
+
|
|
557
|
+
// serverReady is subscribed but never emitted — port stays undefined
|
|
558
|
+
expect(engine.port).toBeUndefined();
|
|
559
|
+
|
|
560
|
+
// buildStart/build event handlers are still registered
|
|
561
|
+
expect(mockWorker.on).toHaveBeenCalledWith("buildStart", expect.any(Function));
|
|
562
|
+
expect(mockWorker.on).toHaveBeenCalledWith("build", expect.any(Function));
|
|
563
|
+
|
|
564
|
+
await engine.stop();
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// Unit: initial build result is reported exactly once (not duplicated by startWatch return)
|
|
568
|
+
it("reports initial build result exactly once to ResultCollector", async () => {
|
|
569
|
+
const mockResultCollector = { add: vi.fn() };
|
|
570
|
+
|
|
571
|
+
mockWorker.startWatch.mockImplementation(() => {
|
|
572
|
+
const buildHandler = mockWorker.on.mock.calls.find(
|
|
573
|
+
(call: any[]) => call[0] === "build",
|
|
574
|
+
)?.[1];
|
|
575
|
+
buildHandler?.({ success: true });
|
|
576
|
+
return Promise.resolve({ success: true });
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const engine = new ViteEngine({
|
|
580
|
+
cwd: "/root",
|
|
581
|
+
pkg: createMockPkg(),
|
|
582
|
+
resultCollector: mockResultCollector as any,
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
await engine.startWatch({ js: true, dts: false });
|
|
586
|
+
|
|
587
|
+
const buildAddCalls = mockResultCollector.add.mock.calls.filter(
|
|
588
|
+
(c: any[]) => c[0].type === "build",
|
|
589
|
+
);
|
|
590
|
+
expect(buildAddCalls).toHaveLength(1);
|
|
591
|
+
|
|
592
|
+
await engine.stop();
|
|
593
|
+
});
|
|
594
|
+
|
|
483
595
|
// Unit: error event reports to ResultCollector
|
|
484
596
|
it("reports error from error event to ResultCollector", async () => {
|
|
485
597
|
const mockResultCollector = { add: vi.fn() };
|