@simplysm/sd-cli 13.0.74 → 13.0.76

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 (28) hide show
  1. package/dist/commands/lint.d.ts +2 -2
  2. package/dist/commands/lint.d.ts.map +1 -1
  3. package/dist/commands/lint.js +0 -94
  4. package/dist/commands/lint.js.map +1 -1
  5. package/package.json +4 -5
  6. package/src/commands/lint.ts +2 -113
  7. package/templates/init/package.json.hbs +2 -3
  8. package/templates/init/packages/client-admin/package.json.hbs +5 -5
  9. package/templates/init/packages/client-admin/src/views/home/base/employee/EmployeeDetail.tsx.hbs +86 -105
  10. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RoleDetail.tsx.hbs +4 -12
  11. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionDetail.tsx.hbs +0 -2
  12. package/templates/init/packages/client-admin/src/views/home/base/role-permission/RolePermissionView.tsx +1 -1
  13. package/templates/init/packages/client-admin/src/views/home/my-info/MyInfoDetail.tsx.hbs +36 -43
  14. package/templates/init/packages/db-main/package.json.hbs +2 -2
  15. package/templates/init/packages/server/package.json.hbs +4 -4
  16. package/templates/init/tests/e2e/package.json.hbs +1 -1
  17. package/tests/get-compiler-options-for-package.spec.ts +0 -45
  18. package/tests/get-package-source-files.spec.ts +0 -42
  19. package/tests/get-types-from-package-json.spec.ts +0 -19
  20. package/tests/infra/ResultCollector.spec.ts +0 -9
  21. package/tests/infra/WorkerManager.spec.ts +0 -34
  22. package/tests/load-ignore-patterns.spec.ts +0 -29
  23. package/tests/load-sd-config.spec.ts +0 -39
  24. package/tests/run-lint.spec.ts +0 -53
  25. package/tests/run-typecheck.spec.ts +0 -168
  26. package/tests/run-watch.spec.ts +0 -34
  27. package/tests/sd-cli.spec.ts +0 -88
  28. package/templates/init/stylelint.config.ts +0 -1
@@ -33,7 +33,6 @@ export function RoleDetail(props: { itemId?: number }) {
33
33
  return appService.orm.connectWithoutTransaction(async (db) => {
34
34
  const role = await db
35
35
  .role()
36
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
37
36
  .where((c) => [expr.eq(c.id, props.itemId)])
38
37
  .select((c) => ({
39
38
  name: c.name,
@@ -61,7 +60,6 @@ export function RoleDetail(props: { itemId?: number }) {
61
60
  await appService.orm.connect(async (db) => {
62
61
  const count = await db
63
62
  .role()
64
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
65
63
  .where((c) => [
66
64
  expr.eq(c.name, data.name ?? ""),
67
65
 
@@ -78,7 +76,6 @@ export function RoleDetail(props: { itemId?: number }) {
78
76
  });
79
77
 
80
78
  // 저장
81
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
82
79
  const savedId = await appService.orm.connect(async (db) => {
83
80
  if (props.itemId == null) {
84
81
  const [inserted] = await db.role().insert(
@@ -95,7 +92,6 @@ export function RoleDetail(props: { itemId?: number }) {
95
92
  } else {
96
93
  await db
97
94
  .role()
98
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
99
95
  .where((_c) => [expr.eq(_c.id, props.itemId)])
100
96
  .update((_c) => ({
101
97
  name: data.name ?? "",
@@ -112,7 +108,6 @@ export function RoleDetail(props: { itemId?: number }) {
112
108
  await appService.orm.connect(async (db) => {
113
109
  await db
114
110
  .role()
115
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
116
111
  .where((_c) => [expr.eq(_c.id, props.itemId!)])
117
112
  .update((_c) => ({
118
113
  isDeleted: del,
@@ -133,19 +128,16 @@ export function RoleDetail(props: { itemId?: number }) {
133
128
  {(ctx) => (
134
129
  <div class="p-2">
135
130
  <FormTable>
136
- <tbody>
137
- <tr>
138
- <th>이름</th>
139
- <td>
131
+ <FormTable.Row>
132
+ <FormTable.Item label="이름">
140
133
  <TextInput
141
134
  required
142
135
  disabled={!perms().edit}
143
136
  value={ctx.data.name ?? ""}
144
137
  onValueChange={(v) => ctx.setData("name", v)}
145
138
  />
146
- </td>
147
- </tr>
148
- </tbody>
139
+ </FormTable.Item>
140
+ </FormTable.Row>
149
141
  </FormTable>
150
142
  </div>
151
143
  )}
@@ -57,11 +57,9 @@ export function RolePermissionDetail(props: { roleId: number }) {
57
57
  }
58
58
 
59
59
  async function handleSubmit(data: Record<string, boolean>) {
60
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
61
60
  await appService.orm.connect(async (db) => {
62
61
  await db
63
62
  .rolePermission()
64
- // eslint-disable-next-line solid/reactivity -- 비동기 콜백 내부
65
63
  .where((c) => [expr.eq(c.roleId, props.roleId)])
66
64
  .delete();
67
65
 
@@ -24,7 +24,7 @@ export function RolePermissionView() {
24
24
  variant={"ghost"}
25
25
  theme={"primary"}
26
26
  size={"xs"}
27
- onClick={() => dialog.show(() => <RoleSheet />, { header: "권한그룹", closeOnBackdrop: true })}
27
+ onClick={() => dialog.show(() => <RoleSheet />, { header: "권한그룹", closeOnInteractOutside: true })}
28
28
  >
29
29
  <Icon icon={IconExternalLink} />
30
30
  </Button>
@@ -148,9 +148,8 @@ export function MyInfoDetail() {
148
148
  <section>
149
149
  <h3 class="mb-4 border-l-4 border-base-500 pl-3 font-bold text-base-500">인증정보</h3>
150
150
  <FormTable>
151
- <tr>
152
- <th>이메일</th>
153
- <td>
151
+ <FormTable.Row>
152
+ <FormTable.Item label="이메일">
154
153
  <TextInput
155
154
  type="email"
156
155
  required
@@ -158,11 +157,10 @@ export function MyInfoDetail() {
158
157
  onValueChange={(v) => ctx.setData("email", v)}
159
158
  autocomplete="off"
160
159
  />
161
- </td>
162
- </tr>
163
- <tr>
164
- <th>현재 비밀번호</th>
165
- <td>
160
+ </FormTable.Item>
161
+ </FormTable.Row>
162
+ <FormTable.Row>
163
+ <FormTable.Item label="현재 비밀번호">
166
164
  <TextInput
167
165
  required={Boolean(ctx.data.newPassword || ctx.data.confirmPassword)}
168
166
  class="w-56"
@@ -172,11 +170,10 @@ export function MyInfoDetail() {
172
170
  onValueChange={(v) => ctx.setData("currentPassword", v)}
173
171
  placeholder="비밀번호 변경 시에만 입력"
174
172
  />
175
- </td>
176
- </tr>
177
- <tr>
178
- <th>새 비밀번호</th>
179
- <td>
173
+ </FormTable.Item>
174
+ </FormTable.Row>
175
+ <FormTable.Row>
176
+ <FormTable.Item label="새 비밀번호">
180
177
  <TextInput
181
178
  required={Boolean(ctx.data.currentPassword || ctx.data.confirmPassword)}
182
179
  minLength={8}
@@ -187,11 +184,10 @@ export function MyInfoDetail() {
187
184
  onValueChange={(v) => ctx.setData("newPassword", v)}
188
185
  placeholder="비밀번호 변경 시에만 입력"
189
186
  />
190
- </td>
191
- </tr>
192
- <tr>
193
- <th>새 비밀번호 확인</th>
194
- <td>
187
+ </FormTable.Item>
188
+ </FormTable.Row>
189
+ <FormTable.Row>
190
+ <FormTable.Item label="새 비밀번호 확인">
195
191
  <TextInput
196
192
  required={Boolean(ctx.data.currentPassword || ctx.data.newPassword)}
197
193
  minLength={8}
@@ -207,38 +203,35 @@ export function MyInfoDetail() {
207
203
  onValueChange={(v) => ctx.setData("confirmPassword", v)}
208
204
  placeholder="비밀번호 변경 시에만 입력"
209
205
  />
210
- </td>
211
- </tr>
206
+ </FormTable.Item>
207
+ </FormTable.Row>
212
208
  </FormTable>
213
209
  </section>
214
210
 
215
211
  <section>
216
212
  <h3 class="mb-4 border-l-4 border-base-500 pl-3 font-bold text-base-500">시스템설정</h3>
217
213
  <FormTable>
218
- <tbody>
219
- <tr>
220
- <th>로그인 시 화면</th>
221
- <td>
222
- <Select
223
- class="w-full"
224
- value={ctx.data.firstRouterLink}
225
- onValueChange={(v) => ctx.setData("firstRouterLink", v)}
226
- items={routeOptions()}
227
- renderValue={(v) => <>{routeMap().get(v) ?? v}</>}
228
- placeholder="선택 안함"
214
+ <FormTable.Row>
215
+ <FormTable.Item label="로그인 시 화면">
216
+ <Select
217
+ class="w-full"
218
+ value={ctx.data.firstRouterLink}
219
+ onValueChange={(v) => ctx.setData("firstRouterLink", v)}
220
+ items={routeOptions()}
221
+ renderValue={(v) => <>{routeMap().get(v) ?? v}</>}
222
+ placeholder="선택 안함"
223
+ >
224
+ <Select.ItemTemplate>
225
+ {(href: string) => <>{routeMap().get(href) ?? href}</>}
226
+ </Select.ItemTemplate>
227
+ <Select.Action
228
+ onClick={() => ctx.setData("firstRouterLink", undefined as any)}
229
229
  >
230
- <Select.ItemTemplate>
231
- {(href: string) => <>{routeMap().get(href) ?? href}</>}
232
- </Select.ItemTemplate>
233
- <Select.Action
234
- onClick={() => ctx.setData("firstRouterLink", undefined as any)}
235
- >
236
- <Icon icon={IconX} class={"text-danger-500"} />
237
- </Select.Action>
238
- </Select>
239
- </td>
240
- </tr>
241
- </tbody>
230
+ <Icon icon={IconX} class={"text-danger-500"} />
231
+ </Select.Action>
232
+ </Select>
233
+ </FormTable.Item>
234
+ </FormTable.Row>
242
235
  </FormTable>
243
236
  </section>
244
237
  </div>
@@ -7,7 +7,7 @@
7
7
  ".": "./src/index.ts"
8
8
  },
9
9
  "dependencies": {
10
- "@simplysm/core-common": "~13.0.74",
11
- "@simplysm/orm-common": "~13.0.74"
10
+ "@simplysm/core-common": "~13.0.76",
11
+ "@simplysm/orm-common": "~13.0.76"
12
12
  }
13
13
  }
@@ -5,11 +5,11 @@
5
5
  "private": true,
6
6
  "dependencies": {
7
7
  "@{{projectName}}/db-main": "workspace:*",
8
- "@simplysm/core-common": "~13.0.74",
8
+ "@simplysm/core-common": "~13.0.76",
9
9
  "@simplysm/excel": "^13.0.71",
10
- "@simplysm/orm-common": "~13.0.74",
11
- "@simplysm/orm-node": "~13.0.74",
12
- "@simplysm/service-server": "~13.0.74",
10
+ "@simplysm/orm-common": "~13.0.76",
11
+ "@simplysm/orm-node": "~13.0.76",
12
+ "@simplysm/service-server": "~13.0.76",
13
13
  "bcrypt": "^6.0.0",
14
14
  "pg": "^8.19.0",
15
15
  "pg-copy-streams": "^7.0.0"
@@ -6,7 +6,7 @@
6
6
  "private": true,
7
7
  "dependencies": {
8
8
  "@{{projectName}}/db-main": "workspace:*",
9
- "@simplysm/orm-node": "~13.0.74",
9
+ "@simplysm/orm-node": "~13.0.76",
10
10
  "bcrypt": "^6.0.0",
11
11
  "playwright": "^1.58.2"
12
12
  },
@@ -81,21 +81,6 @@ describe("getCompilerOptionsForPackage", () => {
81
81
  expect(result.types).toContain("lodash");
82
82
  });
83
83
 
84
- it("node target: removes duplicate node types", async () => {
85
- const packageDir = "/project/packages/core-node";
86
- vi.mocked(fsExists).mockResolvedValue(true);
87
- vi.mocked(fsReadJson).mockResolvedValue({
88
- devDependencies: {
89
- "@types/node": "^20.0.0",
90
- },
91
- });
92
-
93
- const result = await getCompilerOptionsForPackage(baseOptions, "node", packageDir);
94
-
95
- // node type is included only once without duplicates
96
- expect(result.types?.filter((t) => t === "node")).toHaveLength(1);
97
- });
98
-
99
84
  it("handles missing package.json with empty types", async () => {
100
85
  const packageDir = "/project/packages/unknown";
101
86
  vi.mocked(fsExists).mockResolvedValue(false);
@@ -106,34 +91,4 @@ describe("getCompilerOptionsForPackage", () => {
106
91
  expect(result.types).toEqual(["node"]);
107
92
  });
108
93
 
109
- it("handles undefined lib correctly", async () => {
110
- const optionsWithoutLib: ts.CompilerOptions = {
111
- strict: true,
112
- };
113
- const packageDir = "/project/packages/core-node";
114
- vi.mocked(fsExists).mockResolvedValue(false);
115
-
116
- const result = await getCompilerOptionsForPackage(optionsWithoutLib, "node", packageDir);
117
-
118
- // handles undefined lib without error
119
- expect(result.lib).toBeUndefined();
120
- expect(result.types).toEqual(["node"]);
121
- });
122
-
123
- it("does not mutate original baseOptions (immutability)", async () => {
124
- const originalOptions: ts.CompilerOptions = {
125
- lib: ["ES2024", "DOM"],
126
- types: ["original"],
127
- strict: true,
128
- };
129
- const packageDir = "/project/packages/core-node";
130
- vi.mocked(fsExists).mockResolvedValue(false);
131
-
132
- await getCompilerOptionsForPackage(originalOptions, "node", packageDir);
133
-
134
- // original options are not changed
135
- expect(originalOptions.lib).toEqual(["ES2024", "DOM"]);
136
- expect(originalOptions.types).toEqual(["original"]);
137
- expect(originalOptions.noEmit).toBeUndefined();
138
- });
139
94
  });
@@ -4,8 +4,6 @@ import type ts from "typescript";
4
4
  import { getPackageSourceFiles, getPackageFiles } from "../src/utils/tsconfig";
5
5
 
6
6
  describe("getPackageSourceFiles", () => {
7
- const sep = path.sep;
8
-
9
7
  it("filters files within package src directory only", () => {
10
8
  const pkgDir = `/project/packages/core-common`;
11
9
  const parsedConfig = {
@@ -60,35 +58,6 @@ describe("getPackageSourceFiles", () => {
60
58
  ]);
61
59
  });
62
60
 
63
- it("returns empty array if no files", () => {
64
- const pkgDir = `/project/packages/empty`;
65
- const parsedConfig = {
66
- fileNames: [
67
- `/project/packages/core/src/index.ts`,
68
- `/project/packages/core-common/src/index.ts`,
69
- ],
70
- } as ts.ParsedCommandLine;
71
-
72
- const result = getPackageSourceFiles(pkgDir, parsedConfig);
73
-
74
- expect(result).toEqual([]);
75
- });
76
-
77
- it("handles path separators correctly", () => {
78
- // use path.sep for platform-independent test
79
- const pkgDir = `${sep}project${sep}packages${sep}core`;
80
- const parsedConfig = {
81
- fileNames: [
82
- `${sep}project${sep}packages${sep}core${sep}src${sep}index.ts`,
83
- `${sep}project${sep}packages${sep}core-common${sep}src${sep}index.ts`,
84
- ],
85
- } as ts.ParsedCommandLine;
86
-
87
- const result = getPackageSourceFiles(pkgDir, parsedConfig);
88
-
89
- expect(result).toEqual([`${sep}project${sep}packages${sep}core${sep}src${sep}index.ts`]);
90
- });
91
-
92
61
  it("handles forward slash paths from TypeScript API correctly (Windows compatible)", () => {
93
62
  // TypeScript API returns forward slash paths even on Windows
94
63
  // pkgDir is created with path.join using OS-native separator
@@ -152,17 +121,6 @@ describe("getPackageFiles", () => {
152
121
  ]);
153
122
  });
154
123
 
155
- it("returns empty array if no files", () => {
156
- const pkgDir = `/project/packages/empty`;
157
- const parsedConfig = {
158
- fileNames: [`/project/packages/core/src/index.ts`],
159
- } as ts.ParsedCommandLine;
160
-
161
- const result = getPackageFiles(pkgDir, parsedConfig);
162
-
163
- expect(result).toEqual([]);
164
- });
165
-
166
124
  it("handles forward slash paths from TypeScript API correctly (Windows compatible)", () => {
167
125
  const pkgDir = path.resolve("/project/packages/core-common");
168
126
  const parsedConfig = {
@@ -68,25 +68,6 @@ describe("getTypesFromPackageJson", () => {
68
68
  expect(result).toEqual([]);
69
69
  });
70
70
 
71
- it("filters out dependencies that are not @types/*", async () => {
72
- const packageDir = "/project/packages/core-common";
73
- const mockFsExists = vi.mocked(fsExists);
74
- const mockFsReadJson = vi.mocked(fsReadJson);
75
-
76
- mockFsExists.mockResolvedValue(true);
77
- mockFsReadJson.mockResolvedValue({
78
- devDependencies: {
79
- typescript: "^5.0.0",
80
- vitest: "^1.0.0",
81
- eslint: "^9.0.0",
82
- },
83
- });
84
-
85
- const result = await getTypesFromPackageJson(packageDir);
86
-
87
- expect(result).toEqual([]);
88
- });
89
-
90
71
  it("handles scoped @types packages correctly", async () => {
91
72
  const packageDir = "/project/packages/core-common";
92
73
  const mockFsExists = vi.mocked(fsExists);
@@ -27,13 +27,4 @@ describe("ResultCollector", () => {
27
27
  expect(collector.toMap().size).toBe(1);
28
28
  });
29
29
 
30
- it("returns internal Map", () => {
31
- const collector = new ResultCollector();
32
- collector.add({ name: "pkg1", target: "node", type: "build", status: "success" });
33
-
34
- const map = collector.toMap();
35
-
36
- expect(map).toBeInstanceOf(Map);
37
- expect(map.size).toBe(1);
38
- });
39
30
  });
@@ -26,12 +26,6 @@ describe("WorkerManager", () => {
26
26
  expect(manager.get("test-worker")).toBe(worker);
27
27
  });
28
28
 
29
- it("returns undefined when retrieving a non-existent Worker", () => {
30
- const manager = new WorkerManager();
31
-
32
- expect(manager.get("nonexistent")).toBeUndefined();
33
- });
34
-
35
29
  it("terminates all Workers", async () => {
36
30
  const manager = new WorkerManager();
37
31
  const worker1 = manager.create("worker1", "/path/to/worker.ts");
@@ -57,34 +51,6 @@ describe("WorkerManager", () => {
57
51
  expect(manager.get("worker2")).toBe(worker2);
58
52
  });
59
53
 
60
- it("handles terminating a non-existent Worker without error", async () => {
61
- const manager = new WorkerManager();
62
-
63
- await expect(manager.terminate("nonexistent")).resolves.toBeUndefined();
64
- });
65
-
66
- it("retrieves the count of managed Workers", () => {
67
- const manager = new WorkerManager();
68
-
69
- expect(manager.size).toBe(0);
70
-
71
- manager.create("worker1", "/path/to/worker.ts");
72
- expect(manager.size).toBe(1);
73
-
74
- manager.create("worker2", "/path/to/worker.ts");
75
- expect(manager.size).toBe(2);
76
- });
77
-
78
- it("retrieves list of all Worker IDs", () => {
79
- const manager = new WorkerManager();
80
- manager.create("worker1", "/path/to/worker.ts");
81
- manager.create("worker2", "/path/to/worker.ts");
82
-
83
- const ids = manager.ids;
84
-
85
- expect(ids).toEqual(["worker1", "worker2"]);
86
- });
87
-
88
54
  it("overwrites existing Worker when creating with the same ID", () => {
89
55
  const manager = new WorkerManager();
90
56
  const worker1 = manager.create("same-id", "/path/to/worker1.ts");
@@ -156,33 +156,4 @@ describe("loadIgnorePatterns", () => {
156
156
  expect(mockJitiImportFn).toHaveBeenCalledWith(expect.stringContaining("eslint.config.mts"));
157
157
  });
158
158
 
159
- it("returns empty pattern array if empty array exported", async () => {
160
- const cwd = "/project";
161
- const mockExists = vi.mocked(fsExists);
162
-
163
- mockExists.mockImplementation((filePath: string) => {
164
- return Promise.resolve(filePath === path.join(cwd, "eslint.config.ts"));
165
- });
166
-
167
- mockJitiImportFn.mockResolvedValue({
168
- default: [],
169
- });
170
-
171
- const patterns = await loadIgnorePatterns(cwd);
172
-
173
- expect(patterns).toEqual([]);
174
- });
175
-
176
- it("propagates error if jiti import fails", async () => {
177
- const cwd = "/project";
178
- const mockExists = vi.mocked(fsExists);
179
-
180
- mockExists.mockImplementation((filePath: string) => {
181
- return Promise.resolve(filePath === path.join(cwd, "eslint.config.ts"));
182
- });
183
-
184
- mockJitiImportFn.mockRejectedValue(new Error("Syntax error in config file"));
185
-
186
- await expect(loadIgnorePatterns(cwd)).rejects.toThrow("Syntax error in config file");
187
- });
188
159
  });
@@ -65,28 +65,6 @@ describe("loadSdConfig", () => {
65
65
  );
66
66
  });
67
67
 
68
- it("throws error if return value is wrong format (packages is array)", async () => {
69
- vi.mocked(fsExists).mockResolvedValue(true);
70
- mockJitiImport.mockResolvedValue({
71
- default: () => ({ packages: [] }), // packages is array
72
- });
73
-
74
- await expect(loadSdConfig({ cwd: "/project", dev: false, opt: [] })).rejects.toThrow(
75
- /sd\.config\.ts return value is not in .* correct format/,
76
- );
77
- });
78
-
79
- it("throws error if return value is wrong format (packages is null)", async () => {
80
- vi.mocked(fsExists).mockResolvedValue(true);
81
- mockJitiImport.mockResolvedValue({
82
- default: () => ({ packages: null }),
83
- });
84
-
85
- await expect(loadSdConfig({ cwd: "/project", dev: false, opt: [] })).rejects.toThrow(
86
- /sd\.config\.ts return value is not in .* correct format/,
87
- );
88
- });
89
-
90
68
  it("returns correct config", async () => {
91
69
  vi.mocked(fsExists).mockResolvedValue(true);
92
70
  mockJitiImport.mockResolvedValue({
@@ -117,21 +95,4 @@ describe("loadSdConfig", () => {
117
95
  expect(config.packages).toEqual({});
118
96
  });
119
97
 
120
- it("handles async function default export correctly", async () => {
121
- vi.mocked(fsExists).mockResolvedValue(true);
122
- mockJitiImport.mockResolvedValue({
123
- // eslint-disable-next-line @typescript-eslint/require-await
124
- default: async () => ({
125
- packages: {
126
- "core-common": { target: "neutral" },
127
- },
128
- }),
129
- });
130
-
131
- const config = await loadSdConfig({ cwd: "/project", dev: false, opt: [] });
132
-
133
- expect(config.packages).toEqual({
134
- "core-common": { target: "neutral" },
135
- });
136
- });
137
98
  });
@@ -255,25 +255,6 @@ describe("runLint", () => {
255
255
  expect(mockState.lintedFiles).toHaveLength(0);
256
256
  });
257
257
 
258
- it("does not set exitCode when only warnings exist", async () => {
259
- const cwd = "/project";
260
- vi.mocked(fsExists).mockImplementation((filePath: string) => {
261
- return Promise.resolve(filePath === path.join(cwd, "eslint.config.ts"));
262
- });
263
-
264
- mockJitiImportFn.mockResolvedValue({
265
- default: [{ ignores: ["node_modules/**"] }],
266
- });
267
-
268
- vi.mocked(fsGlob).mockResolvedValue(["/project/src/index.ts"]);
269
-
270
- mockState.lintResults = [{ errorCount: 0, warningCount: 3 }];
271
-
272
- await runLint({ targets: [], fix: false, timing: false });
273
-
274
- expect(process.exitCode).toBeUndefined();
275
- });
276
-
277
258
  it("sets TIMING environment variable when timing option is enabled", async () => {
278
259
  const cwd = "/project";
279
260
  vi.mocked(fsExists).mockImplementation((filePath: string) => {
@@ -326,20 +307,6 @@ describe("runLint", () => {
326
307
  expect(process.exitCode).toBeUndefined();
327
308
  });
328
309
 
329
- it("propagates error when glob fails", async () => {
330
- const cwd = "/project";
331
- vi.mocked(fsExists).mockImplementation((filePath: string) => {
332
- return Promise.resolve(filePath === path.join(cwd, "eslint.config.ts"));
333
- });
334
-
335
- mockJitiImportFn.mockResolvedValue({
336
- default: [{ ignores: ["node_modules/**"] }],
337
- });
338
-
339
- vi.mocked(fsGlob).mockRejectedValue(new Error("Glob error"));
340
-
341
- await expect(runLint({ targets: [], fix: false, timing: false })).rejects.toThrow("Glob error");
342
- });
343
310
  });
344
311
 
345
312
  describe("executeLint", () => {
@@ -392,24 +359,4 @@ describe("executeLint", () => {
392
359
  expect(result.warningCount).toBe(1);
393
360
  });
394
361
 
395
- it("includes formatter output in formattedOutput", async () => {
396
- mockState.lintResults = [{ errorCount: 1, warningCount: 0 }];
397
- vi.mocked(fsGlob).mockResolvedValue(["/project/packages/core-common/src/index.ts"]);
398
-
399
- const result = await executeLint({ targets: [], fix: false, timing: false });
400
-
401
- // MockESLint's formatter returns empty string, so formattedOutput is also empty string
402
- expect(result.formattedOutput).toBeDefined();
403
- expect(typeof result.formattedOutput).toBe("string");
404
- });
405
-
406
- it("returns success result when no files exist", async () => {
407
- vi.mocked(fsGlob).mockResolvedValue([]);
408
-
409
- const result = await executeLint({ targets: [], fix: false, timing: false });
410
-
411
- expect(result.success).toBe(true);
412
- expect(result.errorCount).toBe(0);
413
- expect(result.warningCount).toBe(0);
414
- });
415
362
  });