@simplysm/sd-cli 14.0.64 → 14.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/capacitor/capacitor-android.d.ts +2 -0
  2. package/dist/capacitor/capacitor-android.d.ts.map +1 -1
  3. package/dist/capacitor/capacitor-android.js +13 -0
  4. package/dist/capacitor/capacitor-android.js.map +1 -1
  5. package/dist/capacitor/capacitor-npm-config.d.ts.map +1 -1
  6. package/dist/capacitor/capacitor-npm-config.js +2 -6
  7. package/dist/capacitor/capacitor-npm-config.js.map +1 -1
  8. package/dist/electron/electron.d.ts.map +1 -1
  9. package/dist/electron/electron.js +1 -2
  10. package/dist/electron/electron.js.map +1 -1
  11. package/package.json +8 -8
  12. package/src/capacitor/capacitor-android.ts +14 -0
  13. package/src/capacitor/capacitor-npm-config.ts +2 -6
  14. package/src/electron/electron.ts +1 -2
  15. package/tests/angular/ngtsc-build-core.acc.spec.ts +36 -94
  16. package/tests/capacitor/capacitor-android.spec.ts +65 -28
  17. package/tests/capacitor/capacitor-build.spec.ts +40 -385
  18. package/tests/capacitor/capacitor-config-writer.acc.spec.ts +3 -17
  19. package/tests/capacitor/capacitor-config-writer.spec.ts +3 -17
  20. package/tests/capacitor/capacitor-init.spec.ts +40 -636
  21. package/tests/capacitor/capacitor-npm-config.acc.spec.ts +38 -168
  22. package/tests/capacitor/capacitor-npm-config.spec.ts +33 -71
  23. package/tests/commands/check.spec.ts +25 -36
  24. package/tests/commands/deployment-phase.acc.spec.ts +17 -26
  25. package/tests/commands/git-phase.acc.spec.ts +13 -112
  26. package/tests/commands/lint.spec.ts +7 -24
  27. package/tests/commands/post-publish-phase.acc.spec.ts +5 -10
  28. package/tests/commands/typecheck.spec.ts +43 -65
  29. package/tests/electron/electron.spec.ts +22 -46
  30. package/tests/engines/base-engine.spec.ts +4 -13
  31. package/tests/engines/engine-selection.spec.ts +14 -17
  32. package/tests/engines/engine-typecheck-selection.acc.spec.ts +13 -16
  33. package/tests/engines/esbuild-client-engine.acc.spec.ts +36 -40
  34. package/tests/engines/esbuild-client-engine.spec.ts +4 -23
  35. package/tests/engines/ngtsc-engine.spec.ts +3 -10
  36. package/tests/engines/server-esbuild-engine.spec.ts +3 -10
  37. package/tests/engines/tsc-engine.spec.ts +3 -10
  38. package/tests/esbuild/esbuild-tsc-plugin.acc.spec.ts +3 -8
  39. package/tests/esbuild/esbuild-tsc-plugin.spec.ts +3 -8
  40. package/tests/orchestrators/build-orchestrator.spec.ts +57 -102
  41. package/tests/orchestrators/dev-orchestrator.spec.ts +68 -109
  42. package/tests/orchestrators/typecheck-orchestrator.spec.ts +25 -57
  43. package/tests/orchestrators/watch-orchestrator.spec.ts +73 -99
  44. package/tests/sd-cli-entry.spec.ts +17 -20
  45. package/tests/utils/angular-source-file-cache.spec.ts +4 -8
  46. package/tests/utils/copy-src.spec.ts +9 -20
  47. package/tests/utils/esbuild-client-config.acc.spec.ts +9 -15
  48. package/tests/utils/esbuild-client-config.spec.ts +12 -24
  49. package/tests/utils/esbuild-config.spec.ts +51 -42
  50. package/tests/utils/lint-core.spec.ts +13 -19
  51. package/tests/utils/lint-utils.spec.ts +8 -15
  52. package/tests/utils/lint-with-program.spec.ts +3 -7
  53. package/tests/utils/ngtsc-build-core.spec.ts +2 -99
  54. package/tests/utils/orchestrator-utils.spec.ts +7 -20
  55. package/tests/utils/output-utils.spec.ts +5 -11
  56. package/tests/utils/sd-config.spec.ts +4 -12
  57. package/tests/utils/typecheck-env.spec.ts +49 -77
  58. package/tests/utils/typecheck-non-package.spec.ts +23 -16
  59. package/tests/workers/build-watch-paths.acc.spec.ts +4 -10
  60. package/tests/workers/build-watch-paths.spec.ts +4 -9
  61. package/tests/workers/client-worker.acc.spec.ts +64 -137
  62. package/tests/workers/client-worker.spec.ts +63 -89
  63. package/tests/workers/library-build-lint.spec.ts +19 -30
  64. package/tests/workers/library-build-worker.spec.ts +28 -55
  65. package/tests/workers/server-esbuild-context.acc.spec.ts +6 -15
  66. package/tests/workers/server-esbuild-context.spec.ts +7 -16
  67. package/tests/workers/server-runtime-worker.spec.ts +8 -10
  68. package/tests/workers/shared-worker-lifecycle.acc.spec.ts +3 -5
  69. package/tests/workers/shared-worker-lifecycle.spec.ts +4 -5
  70. package/tests/capacitor/capacitor-icon.spec.ts +0 -285
  71. package/tests/capacitor/capacitor-run.spec.ts +0 -256
  72. package/tests/capacitor/capacitor-workspace.spec.ts +0 -203
  73. package/tests/commands/device.spec.ts +0 -237
  74. package/tests/commands/publish.spec.ts +0 -1183
  75. package/tests/utils/external-modules.spec.ts +0 -217
  76. package/tests/workers/server-build-lint.spec.ts +0 -201
  77. package/tests/workers/server-build-worker.spec.ts +0 -765
  78. package/tests/workers/server-watch-manager.acc.spec.ts +0 -162
  79. package/tests/workers/server-watch-manager.spec.ts +0 -199
@@ -1,144 +1,118 @@
1
- import { describe, it, expect, vi, afterEach } from "vitest";
2
-
3
- // Mock fs for getTypesFromPackageJson
4
- const mockExistsSync = vi.fn<(path: string) => boolean>();
5
- const mockReadFileSync = vi.fn<(path: string, encoding: string) => string>();
6
- vi.mock("fs", () => {
7
- const fsMock = {
8
- existsSync: (path: string) => mockExistsSync(path),
9
- readFileSync: (path: string, encoding: string) => mockReadFileSync(path, encoding),
10
- };
11
- return { ...fsMock, default: fsMock };
12
- });
13
-
14
- vi.mock("typescript", () => ({
15
- default: {
16
- readConfigFile: vi.fn(),
17
- sys: { readFile: vi.fn() },
18
- parseJsonConfigFileContent: vi.fn(),
19
- },
20
- }));
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
21
5
 
22
- const {
6
+ import {
23
7
  getTypesFromPackageJson,
24
8
  getCompilerOptionsForEnv,
25
9
  toTypecheckEnvs,
26
- } = await import("../../src/utils/tsconfig");
10
+ } from "../../src/utils/tsconfig";
11
+
12
+ let tmpRoot: string;
27
13
 
28
- afterEach(() => {
29
- vi.clearAllMocks();
14
+ beforeAll(() => {
15
+ tmpRoot = mkdtempSync(path.join(tmpdir(), "typecheck-env-"));
30
16
  });
31
17
 
32
- //#region Acceptance Tests — Slice 1 Scenarios
18
+ afterAll(() => {
19
+ rmSync(tmpRoot, { recursive: true, force: true });
20
+ });
21
+
22
+ function makePkgDir(devDependencies?: Record<string, string>): string {
23
+ const dir = mkdtempSync(path.join(tmpRoot, "pkg-"));
24
+ if (devDependencies) {
25
+ writeFileSync(path.join(dir, "package.json"), JSON.stringify({ devDependencies }));
26
+ }
27
+ return dir;
28
+ }
29
+
30
+ function makePkgDirWithJson(json: object): string {
31
+ const dir = mkdtempSync(path.join(tmpRoot, "pkg-"));
32
+ writeFileSync(path.join(dir, "package.json"), JSON.stringify(json));
33
+ return dir;
34
+ }
33
35
 
34
36
  describe("getCompilerOptionsForEnv", () => {
35
37
  // Scenario: node env에서 DOM 및 WebWorker lib 제거
36
38
  it("removes DOM and WebWorker libs in node env", () => {
37
- mockExistsSync.mockReturnValue(false);
39
+ const pkgDir = makePkgDir(); // package.json 없음
38
40
  const base = { lib: ["lib.esnext.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts"] };
39
- const result = getCompilerOptionsForEnv(base, "node", "/pkg");
41
+ const result = getCompilerOptionsForEnv(base, "node", pkgDir);
40
42
  expect(result.lib).toEqual(["lib.esnext.d.ts"]);
41
43
  });
42
44
 
43
45
  // Scenario: node env에서 사용자 커스텀 lib 보존
44
46
  it("preserves custom libs in node env", () => {
45
- mockExistsSync.mockReturnValue(false);
47
+ const pkgDir = makePkgDir();
46
48
  const base = { lib: ["lib.esnext.d.ts", "lib.webworker.d.ts", "lib.scripthost.d.ts"] };
47
- const result = getCompilerOptionsForEnv(base, "node", "/pkg");
49
+ const result = getCompilerOptionsForEnv(base, "node", pkgDir);
48
50
  expect(result.lib).toEqual(["lib.esnext.d.ts", "lib.scripthost.d.ts"]);
49
51
  });
50
52
 
51
53
  // Scenario: browser env에서 lib 변화 없음
52
54
  it("does not modify lib in browser env", () => {
53
- mockExistsSync.mockReturnValue(false);
55
+ const pkgDir = makePkgDir();
54
56
  const base = { lib: ["lib.esnext.d.ts", "lib.webworker.d.ts"] };
55
- const result = getCompilerOptionsForEnv(base, "browser", "/pkg");
57
+ const result = getCompilerOptionsForEnv(base, "browser", pkgDir);
56
58
  expect(result.lib).toEqual(["lib.esnext.d.ts", "lib.webworker.d.ts"]);
57
59
  });
58
60
 
59
61
  // Scenario: node env에서 types 변화 없음
60
62
  it("does not set types in node env", () => {
61
- mockExistsSync.mockReturnValue(false);
63
+ const pkgDir = makePkgDir();
62
64
  const base = { lib: ["lib.esnext.d.ts"] };
63
- const result = getCompilerOptionsForEnv(base, "node", "/pkg");
65
+ const result = getCompilerOptionsForEnv(base, "node", pkgDir);
64
66
  expect(result.types).toBeUndefined();
65
67
  });
66
68
 
67
69
  // Scenario: browser env에서 @types/node 제거
68
70
  it("removes @types/node in browser env via devDeps", () => {
69
- mockExistsSync.mockReturnValue(true);
70
- mockReadFileSync.mockReturnValue(
71
- JSON.stringify({ devDependencies: { "@types/node": "^20", "@types/ws": "^8" } }),
72
- );
71
+ const pkgDir = makePkgDir({ "@types/node": "^20", "@types/ws": "^8" });
73
72
  const base = { lib: ["lib.esnext.d.ts"] };
74
- const result = getCompilerOptionsForEnv(base, "browser", "/pkg");
73
+ const result = getCompilerOptionsForEnv(base, "browser", pkgDir);
75
74
  expect(result.types).toEqual(["ws"]);
76
75
  });
77
76
 
78
77
  // Scenario: browser env에서 @types/node만 있는 경우
79
78
  it("returns empty types when only @types/node in browser env", () => {
80
- mockExistsSync.mockReturnValue(true);
81
- mockReadFileSync.mockReturnValue(
82
- JSON.stringify({ devDependencies: { "@types/node": "^20" } }),
83
- );
79
+ const pkgDir = makePkgDir({ "@types/node": "^20" });
84
80
  const base = { lib: ["lib.esnext.d.ts"] };
85
- const result = getCompilerOptionsForEnv(base, "browser", "/pkg");
81
+ const result = getCompilerOptionsForEnv(base, "browser", pkgDir);
86
82
  expect(result.types).toEqual([]);
87
83
  });
88
84
 
89
85
  // Scenario: browser env에서 @types가 없는 경우
90
86
  it("returns empty types when no @types in browser env", () => {
91
- mockExistsSync.mockReturnValue(true);
92
- mockReadFileSync.mockReturnValue(
93
- JSON.stringify({ devDependencies: { vitest: "^1" } }),
94
- );
87
+ const pkgDir = makePkgDir({ vitest: "^1" });
95
88
  const base = { lib: ["lib.esnext.d.ts"] };
96
- const result = getCompilerOptionsForEnv(base, "browser", "/pkg");
89
+ const result = getCompilerOptionsForEnv(base, "browser", pkgDir);
97
90
  expect(result.types).toEqual([]);
98
91
  });
99
92
  });
100
93
 
101
- //#endregion
102
-
103
- //#region Unit Tests — getTypesFromPackageJson
104
-
105
94
  describe("getTypesFromPackageJson", () => {
106
95
  it("extracts @types packages from devDependencies", () => {
107
- mockExistsSync.mockReturnValue(true);
108
- mockReadFileSync.mockReturnValue(
109
- JSON.stringify({
110
- devDependencies: { "@types/node": "^20", "@types/ws": "^8", "vitest": "^1" },
111
- }),
112
- );
113
- expect(getTypesFromPackageJson("/pkg")).toEqual(["node", "ws"]);
96
+ const pkgDir = makePkgDir({ "@types/node": "^20", "@types/ws": "^8", "vitest": "^1" });
97
+ expect(getTypesFromPackageJson(pkgDir)).toEqual(["node", "ws"]);
114
98
  });
115
99
 
116
100
  it("returns empty array when package.json does not exist", () => {
117
- mockExistsSync.mockReturnValue(false);
118
- expect(getTypesFromPackageJson("/nonexistent")).toEqual([]);
101
+ const pkgDir = mkdtempSync(path.join(tmpRoot, "pkg-empty-"));
102
+ expect(getTypesFromPackageJson(pkgDir)).toEqual([]);
119
103
  });
120
104
 
121
105
  it("returns empty array when no devDependencies", () => {
122
- mockExistsSync.mockReturnValue(true);
123
- mockReadFileSync.mockReturnValue(JSON.stringify({ name: "test" }));
124
- expect(getTypesFromPackageJson("/pkg")).toEqual([]);
106
+ const pkgDir = makePkgDirWithJson({ name: "test" });
107
+ expect(getTypesFromPackageJson(pkgDir)).toEqual([]);
125
108
  });
126
109
 
127
110
  it("handles scoped @types packages", () => {
128
- mockExistsSync.mockReturnValue(true);
129
- mockReadFileSync.mockReturnValue(
130
- JSON.stringify({
131
- devDependencies: { "@types/ssh2-sftp-client": "^9" },
132
- }),
133
- );
134
- expect(getTypesFromPackageJson("/pkg")).toEqual(["ssh2-sftp-client"]);
111
+ const pkgDir = makePkgDir({ "@types/ssh2-sftp-client": "^9" });
112
+ expect(getTypesFromPackageJson(pkgDir)).toEqual(["ssh2-sftp-client"]);
135
113
  });
136
114
  });
137
115
 
138
- //#endregion
139
-
140
- //#region Unit Tests — toTypecheckEnvs
141
-
142
116
  describe("toTypecheckEnvs", () => {
143
117
  it("returns ['node'] for node target", () => {
144
118
  expect(toTypecheckEnvs("node")).toEqual(["node"]);
@@ -164,5 +138,3 @@ describe("toTypecheckEnvs", () => {
164
138
  expect(toTypecheckEnvs(undefined)).toEqual(["node", "browser"]);
165
139
  });
166
140
  });
167
-
168
- //#endregion
@@ -1,18 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
 
3
- const mocks = vi.hoisted(() => ({
4
- parseTsconfig: vi.fn(),
3
+ // typescript default export는 ESM에서 spyOn 불가 (Cannot redefine property) vi.mock 유지
4
+ const tsMocks = vi.hoisted(() => ({
5
5
  createIncrementalCompilerHost: vi.fn(),
6
6
  createIncrementalProgram: vi.fn(),
7
- serializeDiagnostic: vi.fn((d: any) => d),
8
- }));
9
-
10
- vi.mock("../../src/utils/tsconfig", () => ({
11
- parseTsconfig: mocks.parseTsconfig,
12
- }));
13
-
14
- vi.mock("../../src/typecheck/typecheck-serialization", () => ({
15
- serializeDiagnostic: mocks.serializeDiagnostic,
16
7
  }));
17
8
 
18
9
  vi.mock("typescript", async (importOriginal) => {
@@ -22,18 +13,34 @@ vi.mock("typescript", async (importOriginal) => {
22
13
  ...orig,
23
14
  default: {
24
15
  ...origDefault,
25
- createIncrementalCompilerHost: mocks.createIncrementalCompilerHost,
26
- createIncrementalProgram: mocks.createIncrementalProgram,
16
+ createIncrementalCompilerHost: tsMocks.createIncrementalCompilerHost,
17
+ createIncrementalProgram: tsMocks.createIncrementalProgram,
27
18
  DiagnosticCategory: { Error: 1, Warning: 0 },
28
19
  },
29
20
  };
30
21
  });
31
22
 
32
- const { typecheckNonPackageFiles } = await import("../../src/typecheck/typecheck-non-package");
23
+ import * as tsconfigMod from "../../src/utils/tsconfig";
24
+ import * as typecheckSerializationMod from "../../src/typecheck/typecheck-serialization";
25
+
26
+ import { typecheckNonPackageFiles } from "../../src/typecheck/typecheck-non-package";
27
+
28
+ const mocks = {
29
+ parseTsconfig: undefined as unknown as ReturnType<typeof vi.spyOn>,
30
+ createIncrementalCompilerHost: tsMocks.createIncrementalCompilerHost,
31
+ createIncrementalProgram: tsMocks.createIncrementalProgram,
32
+ serializeDiagnostic: undefined as unknown as ReturnType<typeof vi.spyOn>,
33
+ };
33
34
 
34
35
  describe("typecheckNonPackageFiles", () => {
35
36
  beforeEach(() => {
36
- vi.clearAllMocks();
37
+ vi.restoreAllMocks();
38
+
39
+ mocks.parseTsconfig = vi.spyOn(tsconfigMod, "parseTsconfig");
40
+ mocks.serializeDiagnostic = vi.spyOn(typecheckSerializationMod, "serializeDiagnostic")
41
+ .mockImplementation((d: any) => d);
42
+ mocks.createIncrementalCompilerHost.mockReset();
43
+ mocks.createIncrementalProgram.mockReset();
37
44
 
38
45
  mocks.parseTsconfig.mockReturnValue({
39
46
  fileNames: [
@@ -45,7 +52,7 @@ describe("typecheckNonPackageFiles", () => {
45
52
  errors: [],
46
53
  });
47
54
 
48
- mocks.createIncrementalCompilerHost.mockReturnValue({});
55
+ mocks.createIncrementalCompilerHost.mockReturnValue({} as any);
49
56
 
50
57
  const mockProgram = {
51
58
  emit: vi.fn(() => ({ diagnostics: [] })),
@@ -1,17 +1,11 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import * as collectDepsMod from "../../src/deps/replace-deps/collect-deps";
3
+ import { buildWatchPaths } from "../../src/workers/build-watch-paths";
2
4
 
3
- // collectDeps는 파일 시스템에 의존하므로 모킹
4
- vi.mock("../../src/deps/replace-deps/collect-deps", () => ({
5
- collectDeps: vi.fn(),
6
- }));
7
-
8
- const { collectDeps } = await import("../../src/deps/replace-deps/collect-deps");
9
- const { buildWatchPaths } = await import("../../src/workers/build-watch-paths");
10
-
11
- const mockCollectDeps = vi.mocked(collectDeps);
5
+ const mockCollectDeps = vi.spyOn(collectDepsMod, "collectDeps");
12
6
 
13
7
  beforeEach(() => {
14
- vi.restoreAllMocks();
8
+ mockCollectDeps.mockReset();
15
9
  });
16
10
 
17
11
  describe("buildWatchPaths", () => {
@@ -1,16 +1,11 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import * as collectDepsMod from "../../src/deps/replace-deps/collect-deps";
3
+ import { buildWatchPaths } from "../../src/workers/build-watch-paths";
2
4
 
3
- vi.mock("../../src/deps/replace-deps/collect-deps", () => ({
4
- collectDeps: vi.fn(),
5
- }));
6
-
7
- const { collectDeps } = await import("../../src/deps/replace-deps/collect-deps");
8
- const { buildWatchPaths } = await import("../../src/workers/build-watch-paths");
9
-
10
- const mockCollectDeps = vi.mocked(collectDeps);
5
+ const mockCollectDeps = vi.spyOn(collectDepsMod, "collectDeps");
11
6
 
12
7
  beforeEach(() => {
13
- vi.restoreAllMocks();
8
+ mockCollectDeps.mockReset();
14
9
  });
15
10
 
16
11
  describe("buildWatchPaths", () => {
@@ -1,105 +1,78 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
-
3
- //#region Mocks
1
+ import { describe, it, expect, vi, beforeAll, afterAll, beforeEach } from "vitest";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+
6
+ import * as coreNode from "@simplysm/core-node";
7
+ import * as sharedWorkerLifecycle from "../../src/workers/shared-worker-lifecycle";
8
+ import * as esbuildClientConfig from "../../src/esbuild/esbuild-client-config";
9
+ import * as esbuildIndexHtml from "../../src/esbuild/esbuild-index-html";
10
+ import * as esbuildPwa from "../../src/esbuild/esbuild-pwa";
11
+ import * as devHttpServer from "../../src/dev-server/dev-http-server";
12
+ import * as hmrService from "../../src/dev-server/hmr-service";
13
+ import * as hmrClientScript from "../../src/dev-server/hmr-client-script";
14
+ import * as copyPublic from "../../src/utils/copy-public";
15
+ import * as sdConfig from "../../src/utils/sd-config";
4
16
 
5
17
  let workerFns: Record<string, (...args: any[]) => any>;
6
18
  let mockSend: ReturnType<typeof vi.fn>;
7
19
 
8
20
  const mockRebuild = vi.fn();
9
21
  const mockDispose = vi.fn();
10
- const mockReadFileSync = vi.fn();
11
- const mockExistsSync = vi.fn();
12
-
13
- vi.mock("@simplysm/core-node", () => ({
14
- createWorker: vi.fn((fns: Record<string, Function>) => {
15
- workerFns = fns as any;
16
- mockSend = vi.fn();
17
- return { send: mockSend };
18
- }),
19
- }));
20
-
21
- vi.mock("../../src/workers/shared-worker-lifecycle", () => ({
22
- setupWorkerLifecycle: vi.fn(() => ({
23
- logger: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() },
24
- guardStartWatch: vi.fn(),
25
- })),
26
- }));
27
-
28
- vi.mock("../../src/esbuild/esbuild-client-config", () => ({
29
- createClientEsbuildContext: vi.fn(() =>
30
- Promise.resolve({
31
- context: { rebuild: mockRebuild, dispose: mockDispose, watch: vi.fn() },
32
- sourceFileCache: {},
33
- }),
34
- ),
35
- }));
36
-
37
- vi.mock("../../src/esbuild/esbuild-index-html", () => ({
38
- generateIndexHtml: vi.fn(() =>
39
- Promise.resolve({ content: "<html></html>", errors: [], warnings: [] }),
40
- ),
41
- }));
42
-
43
- vi.mock("../../src/esbuild/esbuild-pwa", () => ({
44
- applyPwa: vi.fn(() => Promise.resolve()),
45
- createPwaHtmlTransform: vi.fn(),
46
- }));
47
-
48
- vi.mock("../../src/dev-server/dev-http-server", () => ({
49
- createDevHttpServer: vi.fn(),
50
- }));
51
-
52
- vi.mock("../../src/dev-server/hmr-service", () => ({
53
- createHmrService: vi.fn(),
54
- }));
55
-
56
- vi.mock("../../src/dev-server/hmr-client-script", () => ({
57
- createHmrPostTransform: vi.fn(),
58
- }));
59
-
60
- vi.mock("../../src/utils/copy-public", () => ({
61
- copyPublicFiles: vi.fn(() => Promise.resolve()),
62
- watchPublicFiles: vi.fn(),
63
- }));
64
-
65
- vi.mock("../../src/utils/sd-config", () => ({
66
- loadSdConfig: vi.fn(() =>
67
- Promise.resolve({
68
- packages: { "my-app": { target: "client", server: "my-server" } },
69
- }),
70
- ),
71
- }));
72
-
73
- vi.mock("node:fs", () => ({
74
- default: {
75
- readFileSync: (...args: any[]) => mockReadFileSync(...args),
76
- writeFileSync: vi.fn(),
77
- existsSync: (...args: any[]) => mockExistsSync(...args),
78
- mkdirSync: vi.fn(),
79
- rmSync: vi.fn(),
80
- },
81
- }));
82
-
83
- //#endregion
84
-
85
- // Import triggers createWorker, capturing the functions
22
+
23
+ vi.spyOn(coreNode, "createWorker").mockImplementation((fns: Record<string, Function>) => {
24
+ workerFns = fns as any;
25
+ mockSend = vi.fn();
26
+ return { send: mockSend } as any;
27
+ });
28
+
29
+ vi.spyOn(sharedWorkerLifecycle, "setupWorkerLifecycle").mockImplementation(() => ({
30
+ logger: { debug: vi.fn(), error: vi.fn(), warn: vi.fn() },
31
+ guardStartWatch: vi.fn(),
32
+ }) as any);
33
+
34
+ vi.spyOn(esbuildClientConfig, "createClientEsbuildContext").mockResolvedValue({
35
+ context: { rebuild: mockRebuild, dispose: mockDispose, watch: vi.fn() },
36
+ sourceFileCache: {},
37
+ } as any);
38
+
39
+ vi.spyOn(esbuildIndexHtml, "generateIndexHtml").mockResolvedValue({
40
+ content: "<html></html>", errors: [], warnings: [],
41
+ });
42
+
43
+ vi.spyOn(esbuildPwa, "applyPwa").mockResolvedValue(undefined);
44
+ vi.spyOn(esbuildPwa, "createPwaHtmlTransform").mockReturnValue(undefined as any);
45
+
46
+ vi.spyOn(devHttpServer, "createDevHttpServer").mockReturnValue(undefined as any);
47
+ vi.spyOn(hmrService, "createHmrService").mockReturnValue(undefined as any);
48
+ vi.spyOn(hmrClientScript, "createHmrPostTransform").mockReturnValue(undefined as any);
49
+
50
+ vi.spyOn(copyPublic, "copyPublicFiles").mockResolvedValue(undefined);
51
+ vi.spyOn(copyPublic, "watchPublicFiles").mockReturnValue(undefined as any);
52
+
53
+ vi.spyOn(sdConfig, "loadSdConfig").mockResolvedValue({
54
+ packages: { "my-app": { target: "client", server: "my-server" } },
55
+ } as any);
56
+
86
57
  await import("../../src/workers/client.worker");
87
58
 
88
- const baseBuildInfo = {
89
- name: "my-app",
90
- cwd: "/workspace",
91
- pkgDir: "/workspace/packages/my-app",
92
- };
59
+ let tmpRoot: string;
60
+ let baseBuildInfo: { name: string; cwd: string; pkgDir: string };
61
+
62
+ beforeAll(() => {
63
+ tmpRoot = mkdtempSync(path.join(tmpdir(), "client-worker-acc-"));
64
+ const pkgDir = path.join(tmpRoot, "packages", "my-app");
65
+ mkdirSync(pkgDir, { recursive: true });
66
+ writeFileSync(path.join(pkgDir, "package.json"), JSON.stringify({ name: "@scope/my-app" }));
67
+ baseBuildInfo = { name: "my-app", cwd: tmpRoot, pkgDir };
68
+ });
69
+
70
+ afterAll(() => {
71
+ rmSync(tmpRoot, { recursive: true, force: true });
72
+ });
93
73
 
94
74
  beforeEach(() => {
95
75
  vi.clearAllMocks();
96
- mockReadFileSync.mockImplementation((filePath: any) => {
97
- if (String(filePath).endsWith("package.json")) {
98
- return JSON.stringify({ name: "@scope/my-app" });
99
- }
100
- return "";
101
- });
102
- mockExistsSync.mockReturnValue(false);
103
76
  mockRebuild.mockResolvedValue({
104
77
  metafile: { outputs: {} },
105
78
  errors: [],
@@ -140,49 +113,3 @@ describe("client.worker build() — Acceptance", () => {
140
113
  expect(result.errors).toContain("Unexpected crash");
141
114
  });
142
115
  });
143
-
144
- describe("client.worker build() — browserSupport 전달", () => {
145
- // Scenario: browserSupport가 ClientBuildInfo를 통해 전달된다
146
- it("browserSupport 설정이 createClientEsbuildContext에 legacyModule/browserslist/postcssPlugins로 전달된다", async () => {
147
- const { createClientEsbuildContext } = await import(
148
- "../../src/esbuild/esbuild-client-config"
149
- );
150
-
151
- const result = await workerFns["build"]({
152
- ...baseBuildInfo,
153
- browserSupport: {
154
- legacyModule: true,
155
- browserslist: "Chrome 61",
156
- postCss: { plugins: [["autoprefixer"]] },
157
- },
158
- });
159
-
160
- expect(result.success).toBe(true);
161
- expect(vi.mocked(createClientEsbuildContext)).toHaveBeenCalledWith(
162
- expect.objectContaining({
163
- legacyModule: true,
164
- browserslist: "Chrome 61",
165
- postcssPlugins: [["autoprefixer"]],
166
- }),
167
- );
168
- });
169
-
170
- // Scenario: browserSupport 미설정 시 기본값으로 동작한다
171
- it("browserSupport 미설정 시 legacyModule=false, browserslist=undefined로 전달된다", async () => {
172
- const { createClientEsbuildContext } = await import(
173
- "../../src/esbuild/esbuild-client-config"
174
- );
175
-
176
- const result = await workerFns["build"](baseBuildInfo);
177
-
178
- expect(result.success).toBe(true);
179
- expect(vi.mocked(createClientEsbuildContext)).toHaveBeenCalledWith(
180
- expect.objectContaining({
181
- legacyModule: false,
182
- browserslist: undefined,
183
- postcssPlugins: undefined,
184
- }),
185
- );
186
- });
187
- });
188
-