@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,8 +1,7 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import path from "path";
3
3
 
4
- // --- Mocks ---
5
-
4
+ // 외부 npm + Node 표준 모듈은 ESM namespace immutable이라 vi.mock 유지
6
5
  const mockContext = {
7
6
  rebuild: vi.fn(),
8
7
  watch: vi.fn(),
@@ -15,18 +14,6 @@ vi.mock("esbuild", () => ({
15
14
  },
16
15
  }));
17
16
 
18
- const mockAngularPlugin = { name: "sd-angular-compiler" };
19
-
20
- vi.mock("../../src/esbuild/esbuild-angular-compiler-plugin", () => ({
21
- createAngularCompilerPlugin: vi.fn(() => mockAngularPlugin),
22
- }));
23
-
24
- const mockTransformStylesheet = vi.fn();
25
-
26
- vi.mock("../../src/angular/client-transform-stylesheet", () => ({
27
- createClientTransformStylesheet: vi.fn(() => mockTransformStylesheet),
28
- }));
29
-
30
17
  vi.mock("browserslist-to-esbuild", () => ({
31
18
  default: vi.fn(() => ["chrome61"]),
32
19
  }));
@@ -44,7 +31,14 @@ vi.mock("module", async (importOriginal) => {
44
31
  };
45
32
  });
46
33
 
47
- // --- Imports (after mocks) ---
34
+ import * as angularPluginMod from "../../src/esbuild/esbuild-angular-compiler-plugin";
35
+ import * as transformStylesheetMod from "../../src/angular/client-transform-stylesheet";
36
+
37
+ const mockAngularPlugin = { name: "sd-angular-compiler" };
38
+ const mockTransformStylesheet = vi.fn();
39
+
40
+ vi.spyOn(angularPluginMod, "createAngularCompilerPlugin").mockReturnValue(mockAngularPlugin as any);
41
+ vi.spyOn(transformStylesheetMod, "createClientTransformStylesheet").mockReturnValue(mockTransformStylesheet as any);
48
42
 
49
43
  const { createClientEsbuildContext, ClientSourceFileCache } = await import(
50
44
  "../../src/esbuild/esbuild-client-config"
@@ -2,8 +2,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import path from "path";
3
3
  import type esbuildTypes from "esbuild";
4
4
 
5
- // --- Mocks ---
6
-
5
+ // 외부 npm + Node 표준 모듈은 ESM namespace immutable이라 vi.mock 유지
7
6
  const mockContext = {
8
7
  rebuild: vi.fn(),
9
8
  watch: vi.fn(),
@@ -16,18 +15,6 @@ vi.mock("esbuild", () => ({
16
15
  },
17
16
  }));
18
17
 
19
- const mockAngularPlugin = { name: "sd-angular-compiler" };
20
-
21
- vi.mock("../../src/esbuild/esbuild-angular-compiler-plugin", () => ({
22
- createAngularCompilerPlugin: vi.fn(() => mockAngularPlugin),
23
- }));
24
-
25
- const mockTransformStylesheet = vi.fn();
26
-
27
- vi.mock("../../src/angular/client-transform-stylesheet", () => ({
28
- createClientTransformStylesheet: vi.fn(() => mockTransformStylesheet),
29
- }));
30
-
31
18
  vi.mock("browserslist-to-esbuild", () => ({
32
19
  default: vi.fn(() => ["chrome61"]),
33
20
  }));
@@ -45,18 +32,19 @@ vi.mock("module", async (importOriginal) => {
45
32
  };
46
33
  });
47
34
 
48
- // --- Imports (after mocks) ---
35
+ import * as angularPluginMod from "../../src/esbuild/esbuild-angular-compiler-plugin";
36
+ import * as transformStylesheetMod from "../../src/angular/client-transform-stylesheet";
37
+
38
+ const mockAngularPlugin = { name: "sd-angular-compiler" };
39
+ const mockTransformStylesheet = vi.fn();
40
+
41
+ vi.spyOn(angularPluginMod, "createAngularCompilerPlugin").mockReturnValue(mockAngularPlugin as any);
42
+ vi.spyOn(transformStylesheetMod, "createClientTransformStylesheet").mockReturnValue(mockTransformStylesheet as any);
49
43
 
50
- const { createClientEsbuildContext, ClientSourceFileCache } = await import(
51
- "../../src/esbuild/esbuild-client-config"
52
- );
44
+ import { createClientEsbuildContext, ClientSourceFileCache } from "../../src/esbuild/esbuild-client-config";
45
+ import { createAngularCompilerPlugin } from "../../src/esbuild/esbuild-angular-compiler-plugin";
46
+ import { createClientTransformStylesheet } from "../../src/angular/client-transform-stylesheet";
53
47
  const esbuild = (await import("esbuild")).default;
54
- const { createAngularCompilerPlugin } = await import(
55
- "../../src/esbuild/esbuild-angular-compiler-plugin"
56
- );
57
- const { createClientTransformStylesheet } = await import(
58
- "../../src/angular/client-transform-stylesheet"
59
- );
60
48
  const browserslistToEsbuild = (await import("browserslist-to-esbuild")).default;
61
49
 
62
50
  // --- Helpers ---
@@ -1,18 +1,13 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import path from "path";
3
+ import fs from "fs/promises";
4
+ import os from "os";
3
5
 
4
- vi.mock("fs/promises", () => ({
5
- default: {
6
- readFile: vi.fn(),
7
- writeFile: vi.fn(),
8
- mkdir: vi.fn(),
9
- },
10
- }));
11
-
12
- const { createServerEsbuildOptions, createEnvBanner, writeChangedOutputFiles } =
13
- await import("../../src/esbuild/esbuild-config");
14
-
15
- const { default: mockFs } = await import("fs/promises");
6
+ import {
7
+ createServerEsbuildOptions,
8
+ createEnvBanner,
9
+ writeChangedOutputFiles,
10
+ } from "../../src/esbuild/esbuild-config";
16
11
 
17
12
  describe("createServerEsbuildOptions", () => {
18
13
  const baseOptions = {
@@ -121,93 +116,107 @@ describe("createEnvBanner", () => {
121
116
  });
122
117
 
123
118
  describe("writeChangedOutputFiles", () => {
124
- beforeEach(() => {
125
- vi.clearAllMocks();
126
- vi.mocked(mockFs.mkdir).mockResolvedValue(undefined);
127
- vi.mocked(mockFs.writeFile).mockResolvedValue();
119
+ let tmpDir: string;
120
+
121
+ beforeEach(async () => {
122
+ tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "sd-esbuild-config-"));
123
+ });
124
+
125
+ afterEach(async () => {
126
+ await fs.rm(tmpDir, { recursive: true, force: true });
128
127
  });
129
128
 
130
129
  it("adds .js extension to relative import paths in .js files", async () => {
131
- vi.mocked(mockFs.readFile).mockRejectedValue(new Error("not found"));
130
+ const distDir = path.join(tmpDir, "dist");
131
+ const filePath = path.join(distDir, "foo.js");
132
132
 
133
133
  await writeChangedOutputFiles([
134
134
  {
135
- path: "/pkg/dist/foo.js",
135
+ path: filePath,
136
136
  text: 'import { bar } from "./bar";\nexport { baz } from "../utils/baz";',
137
137
  },
138
138
  ] as any);
139
139
 
140
- expect(mockFs.writeFile).toHaveBeenCalledWith(
141
- "/pkg/dist/foo.js",
140
+ const written = await fs.readFile(filePath, "utf8");
141
+ expect(written).toBe(
142
142
  'import { bar } from "./bar.js";\nexport { baz } from "../utils/baz.js";',
143
143
  );
144
144
  });
145
145
 
146
146
  it("preserves imports that already have extensions", async () => {
147
- vi.mocked(mockFs.readFile).mockRejectedValue(new Error("not found"));
147
+ const filePath = path.join(tmpDir, "dist", "foo.js");
148
148
 
149
149
  await writeChangedOutputFiles([
150
150
  {
151
- path: "/pkg/dist/foo.js",
151
+ path: filePath,
152
152
  text: 'import data from "./data.json";\nimport styles from "./styles.css";\nimport mod from "./native.node";',
153
153
  },
154
154
  ] as any);
155
155
 
156
- expect(mockFs.writeFile).toHaveBeenCalledWith(
157
- "/pkg/dist/foo.js",
156
+ const written = await fs.readFile(filePath, "utf8");
157
+ expect(written).toBe(
158
158
  'import data from "./data.json";\nimport styles from "./styles.css";\nimport mod from "./native.node";',
159
159
  );
160
160
  });
161
161
 
162
162
  it("skips writing when transformed content matches existing file", async () => {
163
- vi.mocked(mockFs.readFile).mockResolvedValue('import { bar } from "./bar.js";');
163
+ const filePath = path.join(tmpDir, "dist", "foo.js");
164
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
165
+ const existing = 'import { bar } from "./bar.js";';
166
+ await fs.writeFile(filePath, existing);
167
+ const statBefore = await fs.stat(filePath);
164
168
 
169
+ await new Promise((r) => setTimeout(r, 10));
165
170
  await writeChangedOutputFiles([
166
171
  {
167
- path: "/pkg/dist/foo.js",
172
+ path: filePath,
168
173
  text: 'import { bar } from "./bar";',
169
174
  },
170
175
  ] as any);
171
176
 
172
- expect(mockFs.writeFile).not.toHaveBeenCalled();
177
+ const statAfter = await fs.stat(filePath);
178
+ expect(statAfter.mtimeMs).toBe(statBefore.mtimeMs);
179
+ const written = await fs.readFile(filePath, "utf8");
180
+ expect(written).toBe(existing);
173
181
  });
174
182
 
175
183
  it("writes file when content changed", async () => {
176
- vi.mocked(mockFs.readFile).mockResolvedValue('import { old } from "./old.js";');
184
+ const filePath = path.join(tmpDir, "dist", "foo.js");
185
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
186
+ await fs.writeFile(filePath, 'import { old } from "./old.js";');
177
187
 
178
188
  await writeChangedOutputFiles([
179
189
  {
180
- path: "/pkg/dist/foo.js",
190
+ path: filePath,
181
191
  text: 'import { bar } from "./bar";',
182
192
  },
183
193
  ] as any);
184
194
 
185
- expect(mockFs.writeFile).toHaveBeenCalled();
195
+ const written = await fs.readFile(filePath, "utf8");
196
+ expect(written).toBe('import { bar } from "./bar.js";');
186
197
  });
187
198
 
188
199
  it("writes new file when existing file does not exist", async () => {
189
- vi.mocked(mockFs.readFile).mockRejectedValue(new Error("ENOENT"));
200
+ const filePath = path.join(tmpDir, "dist", "foo.js");
190
201
 
191
202
  await writeChangedOutputFiles([
192
- {
193
- path: "/pkg/dist/foo.js",
194
- text: 'const x = 1;',
195
- },
203
+ { path: filePath, text: "const x = 1;" },
196
204
  ] as any);
197
205
 
198
- expect(mockFs.mkdir).toHaveBeenCalledWith(path.dirname("/pkg/dist/foo.js"), { recursive: true });
199
- expect(mockFs.writeFile).toHaveBeenCalledWith("/pkg/dist/foo.js", "const x = 1;");
206
+ const written = await fs.readFile(filePath, "utf8");
207
+ expect(written).toBe("const x = 1;");
200
208
  });
201
209
 
202
210
  it("does not transform non-.js files", async () => {
203
- vi.mocked(mockFs.readFile).mockRejectedValue(new Error("not found"));
204
-
211
+ const filePath = path.join(tmpDir, "dist", "foo.js.map");
205
212
  const mapContent = '{"version":3,"sources":["./bar"]}';
213
+
206
214
  await writeChangedOutputFiles([
207
- { path: "/pkg/dist/foo.js.map", text: mapContent },
215
+ { path: filePath, text: mapContent },
208
216
  ] as any);
209
217
 
210
- expect(mockFs.writeFile).toHaveBeenCalledWith("/pkg/dist/foo.js.map", mapContent);
218
+ const written = await fs.readFile(filePath, "utf8");
219
+ expect(written).toBe(mapContent);
211
220
  });
212
221
  });
213
222
 
@@ -1,10 +1,8 @@
1
1
  /* eslint-disable no-restricted-properties -- 테스트 환경변수 조작 필요 */
2
2
  import { describe, it, expect, vi, beforeEach } from "vitest";
3
3
 
4
- // Hoisted mock references available inside vi.mock factories
4
+ // eslint, jiti는 외부 npm 패키지로 ESM namespace immutable — vi.mock 유지
5
5
  const mocks = vi.hoisted(() => ({
6
- fsxExists: vi.fn<(path: string) => Promise<boolean>>(),
7
- fsxGlob: vi.fn<(...args: unknown[]) => Promise<string[]>>(),
8
6
  lintFiles: vi.fn<() => Promise<Array<{ errorCount: number; warningCount: number }>>>(),
9
7
  loadFormatter: vi.fn(),
10
8
  outputFixes: vi.fn(),
@@ -12,14 +10,6 @@ const mocks = vi.hoisted(() => ({
12
10
  eslintCtor: vi.fn(),
13
11
  }));
14
12
 
15
- vi.mock("@simplysm/core-node", async (importOriginal) => {
16
- const actual = await importOriginal<typeof import("@simplysm/core-node")>();
17
- return {
18
- ...actual,
19
- fsx: { exists: mocks.fsxExists, glob: mocks.fsxGlob },
20
- };
21
- });
22
-
23
13
  vi.mock("eslint", () => ({
24
14
  ESLint: class MockESLint {
25
15
  constructor(options: unknown) { mocks.eslintCtor(options); }
@@ -33,7 +23,11 @@ vi.mock("jiti", () => ({
33
23
  createJiti: vi.fn(() => ({ import: mocks.jitiImport })),
34
24
  }));
35
25
 
36
- const { loadIgnorePatterns, executeLint } = await import("../../src/lint/lint-core");
26
+ import { fsx } from "@simplysm/core-node";
27
+ import { loadIgnorePatterns, executeLint } from "../../src/lint/lint-core";
28
+
29
+ const fsxExists = vi.spyOn(fsx, "exists");
30
+ const fsxGlob = vi.spyOn(fsx, "glob");
37
31
 
38
32
  //#region loadIgnorePatterns
39
33
 
@@ -41,7 +35,7 @@ describe("loadIgnorePatterns", () => {
41
35
  beforeEach(() => vi.clearAllMocks());
42
36
 
43
37
  it("extracts globalIgnores patterns from eslint config", async () => {
44
- mocks.fsxExists.mockImplementation((p) =>
38
+ fsxExists.mockImplementation((p) =>
45
39
  Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
46
40
  );
47
41
  mocks.jitiImport.mockResolvedValue({
@@ -57,7 +51,7 @@ describe("loadIgnorePatterns", () => {
57
51
  });
58
52
 
59
53
  it("ignores config objects that have files key", async () => {
60
- mocks.fsxExists.mockImplementation((p) =>
54
+ fsxExists.mockImplementation((p) =>
61
55
  Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
62
56
  );
63
57
  mocks.jitiImport.mockResolvedValue({
@@ -69,7 +63,7 @@ describe("loadIgnorePatterns", () => {
69
63
  });
70
64
 
71
65
  it("throws when no eslint config file found", async () => {
72
- mocks.fsxExists.mockResolvedValue(false);
66
+ fsxExists.mockResolvedValue(false);
73
67
 
74
68
  await expect(loadIgnorePatterns("/project")).rejects.toThrow(
75
69
  "ESLint 설정 파일",
@@ -85,11 +79,11 @@ describe("executeLint", () => {
85
79
  beforeEach(() => {
86
80
  vi.clearAllMocks();
87
81
  // Default: eslint config exists with no ignores
88
- mocks.fsxExists.mockImplementation((p) =>
82
+ fsxExists.mockImplementation((p) =>
89
83
  Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
90
84
  );
91
85
  mocks.jitiImport.mockResolvedValue({ default: [] });
92
- mocks.fsxGlob.mockResolvedValue(["/project/src/a.ts", "/project/src/b.ts"]);
86
+ fsxGlob.mockResolvedValue(["/project/src/a.ts", "/project/src/b.ts"]);
93
87
  mocks.lintFiles.mockResolvedValue([{ errorCount: 0, warningCount: 0 }]);
94
88
  mocks.loadFormatter.mockResolvedValue({
95
89
  format: vi.fn().mockResolvedValue(""),
@@ -106,7 +100,7 @@ describe("executeLint", () => {
106
100
 
107
101
  it("filters files by targets via pathx.filterByTargets", async () => {
108
102
  const cwd = process.cwd().replace(/\\/g, "/");
109
- mocks.fsxGlob.mockResolvedValue([
103
+ fsxGlob.mockResolvedValue([
110
104
  `${cwd}/packages/core-common/src/a.ts`,
111
105
  `${cwd}/packages/other/src/b.ts`,
112
106
  ]);
@@ -162,7 +156,7 @@ describe("executeLint", () => {
162
156
  });
163
157
 
164
158
  it("returns success when no files to lint", async () => {
165
- mocks.fsxGlob.mockResolvedValue([]);
159
+ fsxGlob.mockResolvedValue([]);
166
160
 
167
161
  const result = await executeLint({ targets: [], fix: false, timing: false });
168
162
 
@@ -1,29 +1,22 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { Worker } from "@simplysm/core-node";
2
3
 
3
- //#region Mocks
4
-
5
- const mocks = vi.hoisted(() => ({
6
- workerCreate: vi.fn(),
4
+ const mocks = {
5
+ workerCreate: vi.spyOn(Worker, "create"),
7
6
  lintFn: vi.fn(),
8
7
  terminateFn: vi.fn(async () => {}),
9
- }));
10
-
11
- vi.mock("@simplysm/core-node", () => ({
12
- Worker: {
13
- create: mocks.workerCreate,
14
- },
15
- }));
8
+ };
16
9
 
17
- const { runLintInWorker } = await import("../../src/lint/lint-utils");
18
-
19
- //#endregion
10
+ import { runLintInWorker } from "../../src/lint/lint-utils";
20
11
 
21
12
  beforeEach(() => {
22
13
  vi.clearAllMocks();
23
14
  mocks.workerCreate.mockReturnValue({
24
15
  lint: mocks.lintFn,
25
16
  terminate: mocks.terminateFn,
26
- });
17
+ on: vi.fn(),
18
+ off: vi.fn(),
19
+ } as never);
27
20
  });
28
21
 
29
22
  describe("runLintInWorker", () => {
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { consola } from "consola";
2
3
 
3
- // --- Mock ESLint ---
4
-
4
+ // eslint은 외부 npm으로 ESM namespace immutable — vi.mock 유지
5
5
  const { mockLintFiles, mockLoadFormatter, MockESLintClass } = vi.hoisted(() => {
6
6
  const lintFilesFn = vi.fn();
7
7
  const loadFormatterFn = vi.fn();
@@ -18,10 +18,6 @@ vi.mock("eslint", () => ({
18
18
  ESLint: MockESLintClass,
19
19
  }));
20
20
 
21
- // --- Spy consola ---
22
-
23
- import { consola } from "consola";
24
-
25
21
  const mockLintLogger = {
26
22
  debug: vi.fn(),
27
23
  info: vi.fn(),
@@ -31,7 +27,7 @@ const mockLintLogger = {
31
27
 
32
28
  vi.spyOn(consola, "withTag").mockReturnValue(mockLintLogger as any);
33
29
 
34
- const { LintWithProgramRunner } = await import("../../src/lint/lint-with-program");
30
+ import { LintWithProgramRunner } from "../../src/lint/lint-with-program";
35
31
 
36
32
  // --- Helpers ---
37
33
 
@@ -1,104 +1,8 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import ts from "typescript";
1
+ import { describe, it, expect } from "vitest";
3
2
 
4
- // --- Mock: AngularCompiler를 사용하는지 검증 ---
5
-
6
- const mockInitialize = vi.fn().mockResolvedValue({ affectedFiles: new Set() });
7
- const mockCollectDiagnostics = vi.fn().mockReturnValue([]);
8
- const mockEmitAffectedFiles = vi.fn().mockReturnValue([]);
9
- const mockGetTsProgram = vi.fn().mockReturnValue({
10
- getSourceFiles: () => [],
11
- });
12
-
13
- const angularCompilerConstructorSpy = vi.fn();
14
-
15
- vi.mock("../../src/angular/angular-compiler", () => {
16
- class AngularCompiler {
17
- constructor(options: unknown) {
18
- angularCompilerConstructorSpy(options);
19
- }
20
- initialize = mockInitialize;
21
- *collectDiagnostics() {
22
- yield* mockCollectDiagnostics();
23
- }
24
- *emitAffectedFiles() {
25
- yield* mockEmitAffectedFiles();
26
- }
27
- getTsProgram = mockGetTsProgram;
28
- }
29
-
30
- class AngularSourceFileCache extends Map<string, ts.SourceFile> {
31
- readonly modifiedFiles = new Set<string>();
32
- invalidate(_files: Iterable<string>): void {
33
- // no-op
34
- }
35
- }
36
-
37
- return {
38
- AngularCompiler,
39
- AngularSourceFileCache,
40
- augmentHostWithCaching: vi.fn(),
41
- };
42
- });
43
-
44
- vi.mock("../../src/utils/tsconfig", () => ({
45
- parseTsconfig: vi.fn().mockReturnValue({
46
- options: {
47
- target: ts.ScriptTarget.ESNext,
48
- module: ts.ModuleKind.ESNext,
49
- moduleResolution: ts.ModuleResolutionKind.Bundler,
50
- strict: false,
51
- skipLibCheck: true,
52
- },
53
- fileNames: ["/workspace/packages/test-pkg/src/main.ts"],
54
- }),
55
- getPackageSourceFiles: vi
56
- .fn()
57
- .mockReturnValue(["/workspace/packages/test-pkg/src/main.ts"]),
58
- getCompilerOptionsForEnv: vi.fn().mockImplementation((opts: ts.CompilerOptions) => opts),
59
- }));
60
-
61
- const ngtscProgramSpy = vi.fn();
62
- vi.mock("../../src/angular/angular-build", () => {
63
- class NgtscProgram {
64
- constructor(...args: unknown[]) {
65
- ngtscProgramSpy(...args);
66
- }
67
- compiler = {
68
- analyzeAsync: vi.fn().mockResolvedValue(undefined),
69
- getDiagnosticsForFile: vi.fn().mockReturnValue([]),
70
- getOptionDiagnostics: vi.fn().mockReturnValue([]),
71
- getResourceDependencies: vi.fn().mockReturnValue([]),
72
- ignoreForDiagnostics: new Set(),
73
- ignoreForEmit: new Set(),
74
- incrementalCompilation: {
75
- safeToSkipEmit: vi.fn().mockReturnValue(false),
76
- recordSuccessfulEmit: vi.fn(),
77
- },
78
- prepareEmit: vi.fn().mockReturnValue({ transformers: { before: [], after: [] } }),
79
- };
80
- getTsProgram() {
81
- return { getSourceFiles: () => [] } as unknown as ts.Program;
82
- }
83
- }
84
- return {
85
- NgtscProgram,
86
- OptimizeFor: { WholeProgram: 0, SingleFile: 1 },
87
- };
88
- });
89
-
90
- const {
91
- createLibraryTransformStylesheet,
92
- } = await import("../../src/angular/ngtsc-build-core");
93
-
94
-
95
- // ─── createLibraryTransformStylesheet ───
3
+ import { createLibraryTransformStylesheet } from "../../src/angular/ngtsc-build-core";
96
4
 
97
5
  describe("createLibraryTransformStylesheet", () => {
98
- beforeEach(() => {
99
- vi.restoreAllMocks();
100
- });
101
-
102
6
  it("외부 .scss 파일이면 compileScssFile로 CSS를 반환하고 의존성을 기록한다", async () => {
103
7
  const loadPaths = ["/pkg/scss", "/cwd/node_modules"];
104
8
  const scssErrors: string[] = [];
@@ -185,4 +89,3 @@ describe("createLibraryTransformStylesheet", () => {
185
89
  expect(scssDependencies.has("/project/src/app.component.ts")).toBe(true);
186
90
  });
187
91
  });
188
-
@@ -1,28 +1,15 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
2
  import type { SdConfig } from "../../src/sd-config.types";
3
3
 
4
- //#region Mocks
4
+ import * as sdConfigMod from "../../src/utils/sd-config";
5
+ import * as packageUtilsMod from "../../src/utils/package-utils";
5
6
 
6
- const mocks = vi.hoisted(() => ({
7
- loadSdConfig: vi.fn(),
8
- validateTargets: vi.fn(),
9
- }));
7
+ const mocks = {
8
+ loadSdConfig: vi.spyOn(sdConfigMod, "loadSdConfig"),
9
+ validateTargets: vi.spyOn(packageUtilsMod, "validateTargets"),
10
+ };
10
11
 
11
- vi.mock("../../src/utils/sd-config", () => ({
12
- loadSdConfig: mocks.loadSdConfig,
13
- }));
14
-
15
- vi.mock("../../src/utils/package-utils", async (importOriginal) => {
16
- const actual = await importOriginal<typeof import("../../src/utils/package-utils")>();
17
- return {
18
- ...actual,
19
- validateTargets: mocks.validateTargets,
20
- };
21
- });
22
-
23
- const { loadAndValidateConfig } = await import("../../src/utils/orchestrator-utils");
24
-
25
- //#endregion
12
+ import { loadAndValidateConfig } from "../../src/utils/orchestrator-utils";
26
13
 
27
14
  beforeEach(() => {
28
15
  vi.clearAllMocks();
@@ -4,21 +4,15 @@ import type { BuildResult } from "../../src/runtime/ResultCollector";
4
4
  import type { PartialMessage } from "esbuild";
5
5
 
6
6
  // output-utils.ts가 모듈 로드 시 consola.withTag("sd:cli:output")로 로거를 생성하므로,
7
- // withTag가 consola 자체를 반환하도록 하여 기존 스파이가 태그 로거 호출을 캡처하게 한다.
8
- vi.mock("consola", async (importOriginal) => {
9
- const mod = await importOriginal<typeof import("consola")>();
10
- const orig = mod.consola;
11
- vi.spyOn(orig, "withTag").mockReturnValue(orig);
12
- return { consola: orig };
13
- });
14
-
15
- const { formatBuildMessages, formatEsbuildMessages, printDiagnostics, printServers } =
16
- await import("../../src/utils/output-utils");
17
-
7
+ // 동적 import 전에 spy 등록하여 withTag가 consola 자체를 반환하게 한다
8
+ vi.spyOn(consola, "withTag").mockReturnValue(consola);
18
9
  vi.spyOn(consola, "error").mockImplementation(() => {});
19
10
  vi.spyOn(consola, "warn").mockImplementation(() => {});
20
11
  vi.spyOn(consola, "info").mockImplementation(() => {});
21
12
 
13
+ const { formatBuildMessages, formatEsbuildMessages, printDiagnostics, printServers } =
14
+ await import("../../src/utils/output-utils");
15
+
22
16
  describe("formatBuildMessages", () => {
23
17
  it("formats name, label, and messages into indented lines", () => {
24
18
  const result = formatBuildMessages("core", "node", ["error in file.ts"]);
@@ -1,25 +1,17 @@
1
1
  import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { fsx } from "@simplysm/core-node";
2
3
 
3
- const mockExists = vi.fn();
4
+ const mockExists = vi.spyOn(fsx, "exists");
4
5
  const mockJitiImport = vi.fn();
5
6
 
6
- vi.mock("@simplysm/core-node", async (importOriginal) => {
7
- const actual = await importOriginal<typeof import("@simplysm/core-node")>();
8
- return {
9
- ...actual,
10
- fsx: {
11
- exists: (...args: unknown[]) => mockExists(...args),
12
- },
13
- };
14
- });
15
-
7
+ // jiti는 외부 npm으로 ESM namespace immutable이라 vi.mock 유지
16
8
  vi.mock("jiti", () => ({
17
9
  createJiti: () => ({
18
10
  import: (...args: unknown[]) => mockJitiImport(...args),
19
11
  }),
20
12
  }));
21
13
 
22
- const { loadSdConfig } = await import("../../src/utils/sd-config");
14
+ import { loadSdConfig } from "../../src/utils/sd-config";
23
15
 
24
16
  describe("loadSdConfig", () => {
25
17
  const baseParams = { cwd: "/project", dev: true, opt: [] as string[] };