@simplysm/sd-cli 14.0.19 → 14.0.21

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 (89) hide show
  1. package/dist/angular/vite-postcss-inline-plugin.d.ts.map +1 -1
  2. package/dist/angular/vite-postcss-inline-plugin.js +4 -1
  3. package/dist/angular/vite-postcss-inline-plugin.js.map +1 -1
  4. package/dist/capacitor/capacitor-android.d.ts +16 -0
  5. package/dist/capacitor/capacitor-android.d.ts.map +1 -0
  6. package/dist/capacitor/capacitor-android.js +289 -0
  7. package/dist/capacitor/capacitor-android.js.map +1 -0
  8. package/dist/capacitor/capacitor.d.ts +0 -49
  9. package/dist/capacitor/capacitor.d.ts.map +1 -1
  10. package/dist/capacitor/capacitor.js +4 -244
  11. package/dist/capacitor/capacitor.js.map +1 -1
  12. package/dist/commands/check.js +2 -2
  13. package/dist/commands/check.js.map +1 -1
  14. package/dist/commands/lint.d.ts +1 -42
  15. package/dist/commands/lint.d.ts.map +1 -1
  16. package/dist/commands/lint.js +1 -151
  17. package/dist/commands/lint.js.map +1 -1
  18. package/dist/commands/publish.d.ts.map +1 -1
  19. package/dist/commands/publish.js +2 -1
  20. package/dist/commands/publish.js.map +1 -1
  21. package/dist/commands/typecheck.d.ts +3 -40
  22. package/dist/commands/typecheck.d.ts.map +1 -1
  23. package/dist/commands/typecheck.js +3 -232
  24. package/dist/commands/typecheck.js.map +1 -1
  25. package/dist/electron/electron.js +11 -4
  26. package/dist/electron/electron.js.map +1 -1
  27. package/dist/orchestrators/DevWatchOrchestrator.d.ts +1 -0
  28. package/dist/orchestrators/DevWatchOrchestrator.d.ts.map +1 -1
  29. package/dist/orchestrators/DevWatchOrchestrator.js +10 -6
  30. package/dist/orchestrators/DevWatchOrchestrator.js.map +1 -1
  31. package/dist/orchestrators/TypecheckOrchestrator.d.ts +74 -0
  32. package/dist/orchestrators/TypecheckOrchestrator.d.ts.map +1 -0
  33. package/dist/orchestrators/TypecheckOrchestrator.js +285 -0
  34. package/dist/orchestrators/TypecheckOrchestrator.js.map +1 -0
  35. package/dist/sd-cli.js +6 -1
  36. package/dist/sd-cli.js.map +1 -1
  37. package/dist/utils/lint-core.d.ts +43 -0
  38. package/dist/utils/lint-core.d.ts.map +1 -0
  39. package/dist/utils/lint-core.js +154 -0
  40. package/dist/utils/lint-core.js.map +1 -0
  41. package/dist/utils/lint-utils.d.ts +1 -1
  42. package/dist/utils/lint-utils.d.ts.map +1 -1
  43. package/dist/utils/server-production-files.d.ts +22 -0
  44. package/dist/utils/server-production-files.d.ts.map +1 -0
  45. package/dist/utils/server-production-files.js +162 -0
  46. package/dist/utils/server-production-files.js.map +1 -0
  47. package/dist/workers/lint.worker.d.ts +1 -1
  48. package/dist/workers/lint.worker.d.ts.map +1 -1
  49. package/dist/workers/lint.worker.js +1 -1
  50. package/dist/workers/lint.worker.js.map +1 -1
  51. package/dist/workers/server-build.worker.d.ts.map +1 -1
  52. package/dist/workers/server-build.worker.js +11 -161
  53. package/dist/workers/server-build.worker.js.map +1 -1
  54. package/package.json +4 -4
  55. package/src/angular/vite-postcss-inline-plugin.ts +5 -1
  56. package/src/capacitor/capacitor-android.ts +368 -0
  57. package/src/capacitor/capacitor.ts +4 -317
  58. package/src/commands/check.ts +2 -2
  59. package/src/commands/lint.ts +1 -201
  60. package/src/commands/publish.ts +2 -1
  61. package/src/commands/typecheck.ts +7 -292
  62. package/src/electron/electron.ts +4 -4
  63. package/src/orchestrators/DevWatchOrchestrator.ts +10 -6
  64. package/src/orchestrators/TypecheckOrchestrator.ts +364 -0
  65. package/src/sd-cli.ts +6 -1
  66. package/src/utils/lint-core.ts +205 -0
  67. package/src/utils/lint-utils.ts +1 -1
  68. package/src/utils/server-production-files.ts +186 -0
  69. package/src/workers/lint.worker.ts +1 -1
  70. package/src/workers/server-build.worker.ts +10 -185
  71. package/tests/angular/vite-postcss-inline-plugin.spec.ts +10 -0
  72. package/tests/capacitor/capacitor-android-exports.verify.md +11 -0
  73. package/tests/capacitor/capacitor-android.spec.ts +219 -0
  74. package/tests/capacitor/capacitor-build.spec.ts +17 -21
  75. package/tests/capacitor/capacitor-icon.spec.ts +17 -19
  76. package/tests/capacitor/capacitor-init.spec.ts +18 -14
  77. package/tests/capacitor/capacitor-run.spec.ts +10 -24
  78. package/tests/capacitor/capacitor-workspace.spec.ts +10 -15
  79. package/tests/commands/check.spec.ts +2 -2
  80. package/tests/commands/lint.spec.ts +33 -194
  81. package/tests/commands/publish-set.verify.md +7 -0
  82. package/tests/electron/electron-symlink-cleanup.verify.md +8 -0
  83. package/tests/orchestrators/dist-delete-watcher.verify.md +10 -0
  84. package/tests/orchestrators/typecheck-orchestrator.spec.ts +180 -0
  85. package/tests/sd-cli-catch-all.verify.md +7 -0
  86. package/tests/utils/lint-core-import-paths.verify.md +10 -0
  87. package/tests/utils/lint-core.spec.ts +188 -0
  88. package/tests/utils/server-production-files-import-paths.verify.md +14 -0
  89. package/tests/workers/server-build-context-dispose.verify.md +8 -0
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
 
4
4
  //#region Mocks
5
5
 
@@ -36,14 +36,6 @@ vi.mock("@simplysm/core-node", () => ({
36
36
  },
37
37
  }));
38
38
 
39
- // env mock
40
- let mockEnv: Record<string, unknown> = {};
41
- vi.mock("@simplysm/core-common", () => ({
42
- env: new Proxy({} as Record<string, unknown>, {
43
- get: (_target, prop) => mockEnv[prop as string],
44
- }),
45
- }));
46
-
47
39
  // cpx mock (was execa)
48
40
  const execaCalls: { command: string; args: string[] }[] = [];
49
41
  const mockCpxSpawn = vi.fn((...args: unknown[]) => {
@@ -71,19 +63,15 @@ vi.mock("sharp", () => ({
71
63
  }),
72
64
  }));
73
65
 
74
- // consola mock
75
- const mockLoggerDebug = vi.fn();
66
+ // consola mock (logger assertion 필요)
76
67
  const mockLoggerWarn = vi.fn();
77
- const mockLoggerSuccess = vi.fn();
68
+ const _mockConsola = {
69
+ level: 0,
70
+ withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
71
+ };
78
72
  vi.mock("consola", () => ({
79
- consola: {
80
- withTag: () => ({
81
- debug: mockLoggerDebug,
82
- warn: mockLoggerWarn,
83
- success: mockLoggerSuccess,
84
- }),
85
- level: 0,
86
- },
73
+ consola: _mockConsola,
74
+ default: _mockConsola,
87
75
  LogLevels: { debug: 4 },
88
76
  }));
89
77
 
@@ -167,7 +155,7 @@ function setupDefaultMocks() {
167
155
  return [];
168
156
  });
169
157
 
170
- mockEnv = { ANDROID_HOME: "C:/Android/Sdk" };
158
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
171
159
 
172
160
  execaCalls.length = 0;
173
161
  mockFsWriteFile.mockReset();
@@ -176,6 +164,14 @@ function setupDefaultMocks() {
176
164
 
177
165
  //#endregion
178
166
 
167
+ let savedEnv: Record<string, string | undefined>;
168
+ beforeEach(() => {
169
+ savedEnv = { ...process.env };
170
+ });
171
+ afterEach(() => {
172
+ process.env = savedEnv;
173
+ });
174
+
179
175
  describe("Capacitor 빌드", () => {
180
176
  beforeEach(() => {
181
177
  vi.clearAllMocks();
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
 
4
4
  //#region Mocks
5
5
 
@@ -34,12 +34,6 @@ vi.mock("@simplysm/core-node", () => ({
34
34
  },
35
35
  }));
36
36
 
37
- // env mock
38
- let mockEnv: Record<string, string | undefined> = {};
39
- vi.mock("@simplysm/core-common", () => ({
40
- env: (key: string) => mockEnv[key],
41
- }));
42
-
43
37
  // cpx mock (was execa)
44
38
  const execaCalls: { command: string; args: string[] }[] = [];
45
39
  const mockCpxSpawn = vi.fn((...args: unknown[]) => {
@@ -69,19 +63,15 @@ const mockSharpInstance = {
69
63
  const mockSharp = vi.fn().mockReturnValue(mockSharpInstance);
70
64
  vi.mock("sharp", () => ({ default: mockSharp }));
71
65
 
72
- // consola mock
73
- const mockLoggerDebug = vi.fn();
66
+ // consola mock (logger assertion 필요)
74
67
  const mockLoggerWarn = vi.fn();
75
- const mockLoggerSuccess = vi.fn();
68
+ const _mockConsola = {
69
+ level: 0,
70
+ withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
71
+ };
76
72
  vi.mock("consola", () => ({
77
- consola: {
78
- withTag: () => ({
79
- debug: mockLoggerDebug,
80
- warn: mockLoggerWarn,
81
- success: mockLoggerSuccess,
82
- }),
83
- level: 0,
84
- },
73
+ consola: _mockConsola,
74
+ default: _mockConsola,
85
75
  LogLevels: { debug: 4 },
86
76
  }));
87
77
 
@@ -149,7 +139,7 @@ function setupDefaultMocks() {
149
139
  mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
150
140
 
151
141
  // env: Android SDK
152
- mockEnv = { ANDROID_HOME: "C:/Android/Sdk" };
142
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
153
143
 
154
144
  // execa 호출 기록 초기화
155
145
  execaCalls.length = 0;
@@ -157,6 +147,14 @@ function setupDefaultMocks() {
157
147
 
158
148
  //#endregion
159
149
 
150
+ let savedEnv: Record<string, string | undefined>;
151
+ beforeEach(() => {
152
+ savedEnv = { ...process.env };
153
+ });
154
+ afterEach(() => {
155
+ process.env = savedEnv;
156
+ });
157
+
160
158
  describe("Capacitor 아이콘 처리", () => {
161
159
  beforeEach(() => {
162
160
  vi.clearAllMocks();
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
 
4
4
  //#region Mocks
5
5
 
@@ -35,11 +35,6 @@ vi.mock("@simplysm/core-node", () => ({
35
35
  },
36
36
  }));
37
37
 
38
- let mockEnv: Record<string, string | undefined> = {};
39
- vi.mock("@simplysm/core-common", () => ({
40
- env: (key: string) => mockEnv[key],
41
- }));
42
-
43
38
  const execaCalls: { command: string; args: string[] }[] = [];
44
39
  const mockCpxSpawn = vi.fn((...args: unknown[]) => {
45
40
  execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
@@ -65,13 +60,14 @@ vi.mock("sharp", () => ({
65
60
  }),
66
61
  }));
67
62
 
68
- const mockLoggerDebug = vi.fn();
69
63
  const mockLoggerWarn = vi.fn();
64
+ const _mockConsola = {
65
+ level: 0,
66
+ withTag: () => ({ debug: vi.fn(), warn: mockLoggerWarn, error: vi.fn(), info: vi.fn(), success: vi.fn() }),
67
+ };
70
68
  vi.mock("consola", () => ({
71
- consola: {
72
- level: 0,
73
- withTag: () => ({ debug: mockLoggerDebug, warn: mockLoggerWarn }),
74
- },
69
+ consola: _mockConsola,
70
+ default: _mockConsola,
75
71
  LogLevels: { debug: 4 },
76
72
  }));
77
73
 
@@ -152,7 +148,7 @@ function setupDefaultMocks() {
152
148
 
153
149
  mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
154
150
 
155
- mockEnv = { ANDROID_HOME: "C:/Android/Sdk" };
151
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
156
152
 
157
153
  execaCalls.length = 0;
158
154
  mockFsWriteFile.mockReset();
@@ -161,6 +157,14 @@ function setupDefaultMocks() {
161
157
 
162
158
  //#endregion
163
159
 
160
+ let savedEnv: Record<string, string | undefined>;
161
+ beforeEach(() => {
162
+ savedEnv = { ...process.env };
163
+ });
164
+ afterEach(() => {
165
+ process.env = savedEnv;
166
+ });
167
+
164
168
  describe("Capacitor 설정 검증", () => {
165
169
  beforeEach(() => {
166
170
  vi.clearAllMocks();
@@ -398,7 +402,7 @@ describe("Android 개발 도구 감지", () => {
398
402
  });
399
403
 
400
404
  it("Android SDK 미설치 시 에러가 발생한다", async () => {
401
- mockEnv = {};
405
+ delete process.env["ANDROID_HOME"];
402
406
  mockFsxExists.mockImplementation((p: string) => {
403
407
  const n = p.replace(/\\/g, "/");
404
408
  if (n.includes(".capacitor.lock")) return false;
@@ -518,7 +522,7 @@ describe("Android 네이티브 설정", () => {
518
522
  typeof call[0] === "string" &&
519
523
  call[0].includes("build.gradle") &&
520
524
  typeof call[1] === "string" &&
521
- call[1].includes("versionCode 10203"),
525
+ call[1].includes("versionCode 1002003"),
522
526
  );
523
527
  expect(gradleWrite).toBeDefined();
524
528
  });
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
 
4
4
  //#region Mocks
5
5
 
@@ -36,14 +36,6 @@ vi.mock("@simplysm/core-node", () => ({
36
36
  },
37
37
  }));
38
38
 
39
- // env mock
40
- let mockEnv: Record<string, unknown> = {};
41
- vi.mock("@simplysm/core-common", () => ({
42
- env: new Proxy({} as Record<string, unknown>, {
43
- get: (_target, prop) => mockEnv[prop as string],
44
- }),
45
- }));
46
-
47
39
  // cpx mock (was execa) — tracks commands and resolves immediately
48
40
  const execaCalls: { command: string; args: string[] }[] = [];
49
41
  let execaFactory: (...args: unknown[]) => Promise<{ stdout: string; stderr: string; exitCode: number }> = () =>
@@ -74,20 +66,6 @@ vi.mock("sharp", () => ({
74
66
  }),
75
67
  }));
76
68
 
77
- // consola mock
78
- const mockLoggerDebug = vi.fn();
79
- const mockLoggerWarn = vi.fn();
80
- vi.mock("consola", () => ({
81
- consola: {
82
- withTag: () => ({
83
- debug: mockLoggerDebug,
84
- warn: mockLoggerWarn,
85
- info: vi.fn(),
86
- }),
87
- level: 0,
88
- },
89
- LogLevels: { debug: 4 },
90
- }));
91
69
 
92
70
  //#endregion
93
71
 
@@ -141,7 +119,7 @@ export default config;`;
141
119
  return [];
142
120
  });
143
121
 
144
- mockEnv = { ANDROID_HOME: "C:/Android/Sdk" };
122
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
145
123
 
146
124
  execaCalls.length = 0;
147
125
  execaFactory = () => Promise.resolve({ stdout: "", stderr: "", exitCode: 0 });
@@ -151,6 +129,14 @@ export default config;`;
151
129
 
152
130
  //#endregion
153
131
 
132
+ let savedEnv: Record<string, string | undefined>;
133
+ beforeEach(() => {
134
+ savedEnv = { ...process.env };
135
+ });
136
+ afterEach(() => {
137
+ process.env = savedEnv;
138
+ });
139
+
154
140
  describe("Capacitor.run()", () => {
155
141
  beforeEach(() => {
156
142
  vi.clearAllMocks();
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
3
3
 
4
4
  //#region Mocks
5
5
 
@@ -35,11 +35,6 @@ vi.mock("@simplysm/core-node", () => ({
35
35
  },
36
36
  }));
37
37
 
38
- let mockEnv: Record<string, string | undefined> = {};
39
- vi.mock("@simplysm/core-common", () => ({
40
- env: (key: string) => mockEnv[key],
41
- }));
42
-
43
38
  const execaCalls: { command: string; args: string[] }[] = [];
44
39
  const mockCpxSpawn = vi.fn((...args: unknown[]) => {
45
40
  execaCalls.push({ command: args[0] as string, args: (args[1] as string[] | undefined) ?? [] });
@@ -81,14 +76,6 @@ vi.mock("module", () => ({
81
76
  }),
82
77
  }));
83
78
 
84
- vi.mock("consola", () => ({
85
- consola: {
86
- withTag: () => ({ debug: vi.fn(), warn: vi.fn() }),
87
- level: 0,
88
- },
89
- LogLevels: { debug: 4 },
90
- }));
91
-
92
79
  //#endregion
93
80
 
94
81
  //#region Helpers
@@ -154,12 +141,20 @@ function setupDefaultMocks() {
154
141
  });
155
142
 
156
143
  mockFsxGlob.mockResolvedValue(["C:/Program Files/Amazon Corretto/jdk21.0.1"]);
157
- mockEnv = { ANDROID_HOME: "C:/Android/Sdk" };
144
+ process.env["ANDROID_HOME"] = "C:/Android/Sdk";
158
145
  execaCalls.length = 0;
159
146
  }
160
147
 
161
148
  //#endregion
162
149
 
150
+ let savedEnv: Record<string, string | undefined>;
151
+ beforeEach(() => {
152
+ savedEnv = { ...process.env };
153
+ });
154
+ afterEach(() => {
155
+ process.env = savedEnv;
156
+ });
157
+
163
158
  describe("workspace:* 플러그인 해석", () => {
164
159
  beforeEach(() => {
165
160
  vi.clearAllMocks();
@@ -9,11 +9,11 @@ const mocks = vi.hoisted(() => ({
9
9
  discoverWorkspacePackages: vi.fn(),
10
10
  }));
11
11
 
12
- vi.mock("../../src/commands/typecheck", () => ({
12
+ vi.mock("../../src/orchestrators/TypecheckOrchestrator", () => ({
13
13
  executeTypecheck: mocks.executeTypecheck,
14
14
  }));
15
15
 
16
- vi.mock("../../src/commands/lint", () => ({
16
+ vi.mock("../../src/utils/lint-core", () => ({
17
17
  executeLint: mocks.executeLint,
18
18
  }));
19
19
 
@@ -1,189 +1,16 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
2
 
3
- // Hoisted mock references — available inside vi.mock factories
4
- const mocks = vi.hoisted(() => ({
5
- fsxExists: vi.fn<(path: string) => Promise<boolean>>(),
6
- fsxGlob: vi.fn<(...args: unknown[]) => Promise<string[]>>(),
7
- filterByTargets: vi.fn(
8
- (files: string[], _targets: string[], _cwd: string) => files,
9
- ),
10
- lintFiles: vi.fn<() => Promise<Array<{ errorCount: number; warningCount: number }>>>(),
11
- loadFormatter: vi.fn(),
12
- outputFixes: vi.fn(),
13
- jitiImport: vi.fn(),
14
- eslintCtor: vi.fn(),
15
- }));
3
+ //#region Mocks
16
4
 
17
- vi.mock("@simplysm/core-node", () => ({
18
- fsx: { exists: mocks.fsxExists, glob: mocks.fsxGlob },
19
- pathx: { filterByTargets: mocks.filterByTargets },
20
- }));
21
-
22
- vi.mock("eslint", () => ({
23
- ESLint: class MockESLint {
24
- constructor(options: unknown) { mocks.eslintCtor(options); }
25
- lintFiles = mocks.lintFiles;
26
- loadFormatter = mocks.loadFormatter;
27
- static outputFixes = mocks.outputFixes;
28
- },
5
+ const mocks = vi.hoisted(() => ({
6
+ executeLint: vi.fn(),
29
7
  }));
30
8
 
31
- vi.mock("jiti", () => ({
32
- createJiti: vi.fn(() => ({ import: mocks.jitiImport })),
9
+ vi.mock("../../src/utils/lint-core", () => ({
10
+ executeLint: mocks.executeLint,
33
11
  }));
34
12
 
35
- vi.mock("consola", () => {
36
- const fns = (): Record<string, unknown> => ({
37
- debug: vi.fn(), start: vi.fn(), success: vi.fn(),
38
- info: vi.fn(), error: vi.fn(), warn: vi.fn(), log: vi.fn(),
39
- withTag: vi.fn(() => fns()),
40
- level: 0,
41
- });
42
- const c = fns();
43
- return { consola: c, default: c, LogLevels: {} };
44
- });
45
-
46
- const { loadIgnorePatterns, executeLint, runLint } = await import("../../src/commands/lint");
47
-
48
- //#region loadIgnorePatterns
49
-
50
- describe("loadIgnorePatterns", () => {
51
- beforeEach(() => vi.clearAllMocks());
52
-
53
- it("extracts globalIgnores patterns from eslint config", async () => {
54
- mocks.fsxExists.mockImplementation((p) =>
55
- Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
56
- );
57
- mocks.jitiImport.mockResolvedValue({
58
- default: [
59
- { ignores: ["dist/**", "node_modules/**"] },
60
- { files: ["*.ts"], rules: {} }, // not globalIgnores — has files key
61
- { ignores: [".cache/**"] },
62
- ],
63
- });
64
-
65
- const result = await loadIgnorePatterns("/project");
66
- expect(result).toEqual(["dist/**", "node_modules/**", ".cache/**"]);
67
- });
68
-
69
- it("ignores config objects that have files key", async () => {
70
- mocks.fsxExists.mockImplementation((p) =>
71
- Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
72
- );
73
- mocks.jitiImport.mockResolvedValue({
74
- default: [{ files: ["*.ts"], ignores: ["dist/**"] }],
75
- });
76
-
77
- const result = await loadIgnorePatterns("/project");
78
- expect(result).toEqual([]);
79
- });
80
-
81
- it("throws when no eslint config file found", async () => {
82
- mocks.fsxExists.mockResolvedValue(false);
83
-
84
- await expect(loadIgnorePatterns("/project")).rejects.toThrow(
85
- "ESLint 설정 파일",
86
- );
87
- });
88
- });
89
-
90
- //#endregion
91
-
92
- //#region executeLint
93
-
94
- describe("executeLint", () => {
95
- beforeEach(() => {
96
- vi.clearAllMocks();
97
- // Default: eslint config exists with no ignores
98
- mocks.fsxExists.mockImplementation((p) =>
99
- Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
100
- );
101
- mocks.jitiImport.mockResolvedValue({ default: [] });
102
- mocks.fsxGlob.mockResolvedValue(["/project/src/a.ts", "/project/src/b.ts"]);
103
- mocks.filterByTargets.mockImplementation((files) => files);
104
- mocks.lintFiles.mockResolvedValue([{ errorCount: 0, warningCount: 0 }]);
105
- mocks.loadFormatter.mockResolvedValue({
106
- format: vi.fn().mockResolvedValue(""),
107
- });
108
- });
109
-
110
- it("lints all collected files and returns success when no errors", async () => {
111
- const result = await executeLint({ targets: [], fix: false, timing: false });
112
-
113
- expect(result.success).toBe(true);
114
- expect(result.errorCount).toBe(0);
115
- expect(result.warningCount).toBe(0);
116
- expect(mocks.lintFiles).toHaveBeenCalled();
117
- });
118
-
119
- it("filters files by targets via pathx.filterByTargets", async () => {
120
- const filteredFiles = ["/project/packages/core-common/src/a.ts"];
121
- mocks.filterByTargets.mockReturnValue(filteredFiles);
122
-
123
- await executeLint({ targets: ["packages/core-common"], fix: false, timing: false });
124
-
125
- expect(mocks.filterByTargets).toHaveBeenCalledWith(
126
- expect.any(Array),
127
- ["packages/core-common"],
128
- expect.any(String),
129
- );
130
- });
131
-
132
- it("applies auto-fix when fix option is true", async () => {
133
- await executeLint({ targets: [], fix: true, timing: false });
134
-
135
- expect(mocks.outputFixes).toHaveBeenCalled();
136
- });
137
-
138
- it("sets TIMING env variable when timing option is true", async () => {
139
- const origTiming = process.env["TIMING"];
140
-
141
- await executeLint({ targets: [], fix: false, timing: true });
142
-
143
- expect(process.env["TIMING"]).toBe("1");
144
-
145
- // Cleanup
146
- if (origTiming == null) delete process.env["TIMING"];
147
- else process.env["TIMING"] = origTiming;
148
- });
149
-
150
- it("creates ESLint with cache enabled and correct cache location", async () => {
151
- await executeLint({ targets: [], fix: false, timing: false });
152
-
153
- expect(mocks.eslintCtor).toHaveBeenCalledWith(
154
- expect.objectContaining({
155
- cache: true,
156
- cacheLocation: expect.stringContaining("eslint.cache"),
157
- }),
158
- );
159
- });
160
-
161
- it("returns error count and formatted output when lint errors found", async () => {
162
- mocks.lintFiles.mockResolvedValue([
163
- { errorCount: 3, warningCount: 1 },
164
- { errorCount: 1, warningCount: 2 },
165
- ]);
166
- mocks.loadFormatter.mockResolvedValue({
167
- format: vi.fn().mockResolvedValue("error details"),
168
- });
169
-
170
- const result = await executeLint({ targets: [], fix: false, timing: false });
171
-
172
- expect(result.success).toBe(false);
173
- expect(result.errorCount).toBe(4);
174
- expect(result.warningCount).toBe(3);
175
- expect(result.formattedOutput).toBe("error details");
176
- });
177
-
178
- it("returns success when no files to lint", async () => {
179
- mocks.fsxGlob.mockResolvedValue([]);
180
-
181
- const result = await executeLint({ targets: [], fix: false, timing: false });
182
-
183
- expect(result.success).toBe(true);
184
- expect(mocks.lintFiles).not.toHaveBeenCalled();
185
- });
186
- });
13
+ const { runLint } = await import("../../src/commands/lint");
187
14
 
188
15
  //#endregion
189
16
 
@@ -200,15 +27,11 @@ describe("runLint", () => {
200
27
  writeSpy = vi.spyOn(process.stdout, "write").mockReturnValue(true);
201
28
 
202
29
  // Default: successful lint
203
- mocks.fsxExists.mockImplementation((p) =>
204
- Promise.resolve(typeof p === "string" && p.endsWith("eslint.config.ts")),
205
- );
206
- mocks.jitiImport.mockResolvedValue({ default: [] });
207
- mocks.fsxGlob.mockResolvedValue(["/project/src/a.ts"]);
208
- mocks.filterByTargets.mockImplementation((files) => files);
209
- mocks.lintFiles.mockResolvedValue([{ errorCount: 0, warningCount: 0 }]);
210
- mocks.loadFormatter.mockResolvedValue({
211
- format: vi.fn().mockResolvedValue(""),
30
+ mocks.executeLint.mockResolvedValue({
31
+ success: true,
32
+ errorCount: 0,
33
+ warningCount: 0,
34
+ formattedOutput: "",
212
35
  });
213
36
  });
214
37
 
@@ -218,9 +41,11 @@ describe("runLint", () => {
218
41
  });
219
42
 
220
43
  it("writes formatted output to stdout when there are results", async () => {
221
- mocks.lintFiles.mockResolvedValue([{ errorCount: 1, warningCount: 0 }]);
222
- mocks.loadFormatter.mockResolvedValue({
223
- format: vi.fn().mockResolvedValue("lint output here"),
44
+ mocks.executeLint.mockResolvedValue({
45
+ success: false,
46
+ errorCount: 1,
47
+ warningCount: 0,
48
+ formattedOutput: "lint output here",
224
49
  });
225
50
 
226
51
  await runLint({ targets: [], fix: false, timing: false });
@@ -229,15 +54,29 @@ describe("runLint", () => {
229
54
  });
230
55
 
231
56
  it("sets exitCode to 1 when lint errors are found", async () => {
232
- mocks.lintFiles.mockResolvedValue([{ errorCount: 1, warningCount: 0 }]);
233
- mocks.loadFormatter.mockResolvedValue({
234
- format: vi.fn().mockResolvedValue("errors"),
57
+ mocks.executeLint.mockResolvedValue({
58
+ success: false,
59
+ errorCount: 1,
60
+ warningCount: 0,
61
+ formattedOutput: "errors",
235
62
  });
236
63
 
237
64
  await runLint({ targets: [], fix: false, timing: false });
238
65
 
239
66
  expect(process.exitCode).toBe(1);
240
67
  });
68
+
69
+ it("does not set exitCode when lint passes", async () => {
70
+ await runLint({ targets: [], fix: false, timing: false });
71
+
72
+ expect(process.exitCode).toBeUndefined();
73
+ });
74
+
75
+ it("does not write to stdout when formattedOutput is empty", async () => {
76
+ await runLint({ targets: [], fix: false, timing: false });
77
+
78
+ expect(writeSpy).not.toHaveBeenCalled();
79
+ });
241
80
  });
242
81
 
243
82
  //#endregion
@@ -0,0 +1,7 @@
1
+ # 배포 실패 패키지 ���색 Set 사용 -- LLM 검증
2
+
3
+ ## 검증 항목
4
+
5
+ - [x] Set 생성: `publish.ts:773` — `const publishedSet = new Set(publishedPackages)` 확인
6
+ - [x] Set.has() 사용: `publish.ts:774` — `allPkgNames.filter(n => !publishedSet.has(n))` 확인
7
+ - [x] 동작 동등성: `Array.includes()` → `Set.has()` 변환은 동일한 boolean 결과를 반환하���로 기능 동등
@@ -0,0 +1,8 @@
1
+ # symlink 테스트 임시 파일 정리 -- LLM 검증
2
+
3
+ ## 검증 항목
4
+
5
+ - [x] try-catch-finally 구조 적용: `electron.ts:348-358` — try 블록에서 writeFile/symlink/lstat 수행, finally에서 정리
6
+ - [x] finally에서 testLink, testTarget 각각 unlink: `finally { try { fs.unlinkSync(testLink); } catch {} try { fs.unlinkSync(testTarget); } catch {} }` 확인
7
+ - [x] 성공 시에도 파일 정리: try 블록 return 후 finally가 실행되므로 정리 보장
8
+ - [x] 실패 시에도 파일 정리: catch 블록 return 후 finally가 실행되므로 정리 보장
@@ -0,0 +1,10 @@
1
+ # dist 삭제 감지 watcher 일반화 -- LLM 검증
2
+
3
+ ## 검증 항목
4
+
5
+ - [x] 디버그 하드코딩 제거: `DevWatchOrchestrator.ts:281` — `angular` 하드코딩 블록이 제거됨
6
+ - [x] 모든 라이브러리 패키지 감시: `_startWatchMode()`에서 `this._libraryPackages`를 순회하며 각 `pkg.dir/dist`를 감시
7
+ - [x] 클래스 필드 저장: `_distDeleteWatchers: FsWatcher[]` 필드에 push
8
+ - [x] shutdown() 정리: `shutdown()`에서 `this._distDeleteWatchers.map(w => w.close())` 호출 확인
9
+ - [x] 정리 후 초기화: `this._distDeleteWatchers = []`로 참조 해제 확인
10
+ - [x] 로그 형식: `[dist-delete:{패키지명}]` 형식으로 어떤 패키지의 dist가 삭제되었는지 식별 가능