pi-lens 2.0.7 → 2.0.8

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 (34) hide show
  1. package/clients/ast-grep-client.test.ts +146 -116
  2. package/clients/ast-grep-client.ts +645 -551
  3. package/clients/biome-client.test.ts +154 -137
  4. package/clients/biome-client.ts +397 -337
  5. package/clients/complexity-client.test.ts +188 -200
  6. package/clients/complexity-client.ts +815 -667
  7. package/clients/dependency-checker.test.ts +55 -55
  8. package/clients/dependency-checker.ts +358 -333
  9. package/clients/go-client.test.ts +121 -111
  10. package/clients/go-client.ts +218 -216
  11. package/clients/jscpd-client.test.ts +132 -132
  12. package/clients/jscpd-client.ts +155 -118
  13. package/clients/knip-client.test.ts +123 -133
  14. package/clients/knip-client.ts +231 -218
  15. package/clients/metrics-client.test.ts +171 -167
  16. package/clients/metrics-client.ts +283 -252
  17. package/clients/ruff-client.test.ts +128 -117
  18. package/clients/ruff-client.ts +300 -269
  19. package/clients/rust-client.test.ts +104 -85
  20. package/clients/rust-client.ts +241 -234
  21. package/clients/subprocess-client.ts +1 -1
  22. package/clients/test-runner-client.test.ts +248 -215
  23. package/clients/test-runner-client.ts +728 -608
  24. package/clients/test-utils.ts +10 -3
  25. package/clients/todo-scanner.test.ts +288 -202
  26. package/clients/todo-scanner.ts +225 -187
  27. package/clients/type-coverage-client.test.ts +119 -119
  28. package/clients/type-coverage-client.ts +142 -115
  29. package/clients/types.ts +28 -28
  30. package/clients/typescript-client.test.ts +99 -93
  31. package/clients/typescript-client.ts +527 -502
  32. package/index.ts +662 -212
  33. package/package.json +1 -1
  34. package/tsconfig.json +12 -12
@@ -1,12 +1,16 @@
1
1
  import * as fs from "node:fs";
2
- import * as path from "node:path";
3
2
  import * as os from "node:os";
3
+ import * as path from "node:path";
4
4
 
5
5
  /**
6
6
  * Creates a temporary file within the given temp directory.
7
7
  * Automatically creates parent directories if they don't exist.
8
8
  */
9
- export function createTempFile(tmpDir: string, name: string, content: string): string {
9
+ export function createTempFile(
10
+ tmpDir: string,
11
+ name: string,
12
+ content: string,
13
+ ): string {
10
14
  const filePath = path.join(tmpDir, name);
11
15
  const dir = path.dirname(filePath);
12
16
  if (!fs.existsSync(dir)) {
@@ -20,7 +24,10 @@ export function createTempFile(tmpDir: string, name: string, content: string): s
20
24
  * Creates a temporary directory for testing.
21
25
  * Returns the path and a cleanup function.
22
26
  */
23
- export function setupTestEnvironment(prefix: string): { tmpDir: string; cleanup: () => void } {
27
+ export function setupTestEnvironment(prefix: string): {
28
+ tmpDir: string;
29
+ cleanup: () => void;
30
+ } {
24
31
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
25
32
  const cleanup = () => {
26
33
  if (tmpDir && fs.existsSync(tmpDir)) {
@@ -1,266 +1,352 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { TodoScanner } from "./todo-scanner.js";
1
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
2
  import { createTempFile, setupTestEnvironment } from "./test-utils.js";
3
+ import { TodoScanner } from "./todo-scanner.js";
4
4
 
5
5
  describe("TodoScanner", () => {
6
- let client: TodoScanner;
7
- let tmpDir: string;
8
- let cleanup: () => void;
9
-
10
- beforeEach(() => {
11
- client = new TodoScanner();
12
- ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-todo-test-"));
13
- });
14
-
15
- afterEach(() => {
16
- cleanup();
17
- });
18
-
19
- describe("scanFile", () => {
20
- it("should return empty array for non-existent files", () => {
21
- const result = client.scanFile("/nonexistent/file.ts");
22
- expect(result).toEqual([]);
23
- });
24
-
25
- it("should find TODO comments", () => {
26
- const content = `
6
+ let client: TodoScanner;
7
+ let tmpDir: string;
8
+ let cleanup: () => void;
9
+
10
+ beforeEach(() => {
11
+ client = new TodoScanner();
12
+ ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-todo-test-"));
13
+ });
14
+
15
+ afterEach(() => {
16
+ cleanup();
17
+ });
18
+
19
+ describe("scanFile", () => {
20
+ it("should return empty array for non-existent files", () => {
21
+ const result = client.scanFile("/nonexistent/file.ts");
22
+ expect(result).toEqual([]);
23
+ });
24
+
25
+ it("should find TODO comments", () => {
26
+ const content = `
27
27
  // TODO: implement this function
28
28
  function foo() {}
29
29
  `;
30
- const filePath = createTempFile(tmpDir, "test.ts", content);
31
- const result = client.scanFile(filePath);
30
+ const filePath = createTempFile(tmpDir, "test.ts", content);
31
+ const result = client.scanFile(filePath);
32
32
 
33
- expect(result.length).toBe(1);
34
- expect(result[0].type).toBe("TODO");
35
- expect(result[0].message).toContain("implement this function");
36
- });
33
+ expect(result.length).toBe(1);
34
+ expect(result[0].type).toBe("TODO");
35
+ expect(result[0].message).toContain("implement this function");
36
+ });
37
37
 
38
- it("should find FIXME comments", () => {
39
- const content = `
38
+ it("should find FIXME comments", () => {
39
+ const content = `
40
40
  // FIXME: this is broken
41
41
  function foo() {}
42
42
  `;
43
- const filePath = createTempFile(tmpDir, "test.ts", content);
44
- const result = client.scanFile(filePath);
43
+ const filePath = createTempFile(tmpDir, "test.ts", content);
44
+ const result = client.scanFile(filePath);
45
45
 
46
- expect(result.length).toBe(1);
47
- expect(result[0].type).toBe("FIXME");
48
- });
46
+ expect(result.length).toBe(1);
47
+ expect(result[0].type).toBe("FIXME");
48
+ });
49
49
 
50
- it("should find HACK comments", () => {
51
- const content = `
50
+ it("should find HACK comments", () => {
51
+ const content = `
52
52
  // HACK: temporary workaround
53
53
  const x = 1;
54
54
  `;
55
- const filePath = createTempFile(tmpDir, "test.ts", content);
56
- const result = client.scanFile(filePath);
55
+ const filePath = createTempFile(tmpDir, "test.ts", content);
56
+ const result = client.scanFile(filePath);
57
57
 
58
- expect(result.length).toBe(1);
59
- expect(result[0].type).toBe("HACK");
60
- });
58
+ expect(result.length).toBe(1);
59
+ expect(result[0].type).toBe("HACK");
60
+ });
61
61
 
62
- it("should find BUG comments", () => {
63
- const content = `
62
+ it("should find BUG comments", () => {
63
+ const content = `
64
64
  // BUG: this causes a crash
65
65
  const x = 1;
66
66
  `;
67
- const filePath = createTempFile(tmpDir, "test.ts", content);
68
- const result = client.scanFile(filePath);
67
+ const filePath = createTempFile(tmpDir, "test.ts", content);
68
+ const result = client.scanFile(filePath);
69
69
 
70
- expect(result.length).toBe(1);
71
- expect(result[0].type).toBe("BUG");
72
- });
70
+ expect(result.length).toBe(1);
71
+ expect(result[0].type).toBe("BUG");
72
+ });
73
73
 
74
- it("should find NOTE comments", () => {
75
- const content = `
74
+ it("should find NOTE comments", () => {
75
+ const content = `
76
76
  // NOTE: important design decision
77
77
  const x = 1;
78
78
  `;
79
- const filePath = createTempFile(tmpDir, "test.ts", content);
80
- const result = client.scanFile(filePath);
79
+ const filePath = createTempFile(tmpDir, "test.ts", content);
80
+ const result = client.scanFile(filePath);
81
81
 
82
- expect(result.length).toBe(1);
83
- expect(result[0].type).toBe("NOTE");
84
- });
82
+ expect(result.length).toBe(1);
83
+ expect(result[0].type).toBe("NOTE");
84
+ });
85
85
 
86
- it("should find TODO in block comments", () => {
87
- const content = `
86
+ it("should find TODO in block comments", () => {
87
+ const content = `
88
88
  /*
89
89
  * TODO: refactor this later
90
90
  */
91
91
  const x = 1;
92
92
  `;
93
- const filePath = createTempFile(tmpDir, "test.ts", content);
94
- const result = client.scanFile(filePath);
93
+ const filePath = createTempFile(tmpDir, "test.ts", content);
94
+ const result = client.scanFile(filePath);
95
95
 
96
- expect(result.length).toBe(1);
97
- expect(result[0].type).toBe("TODO");
98
- });
96
+ expect(result.length).toBe(1);
97
+ expect(result[0].type).toBe("TODO");
98
+ });
99
99
 
100
- it("should find TODO in JSDoc comments", () => {
101
- const content = `
100
+ it("should find TODO in JSDoc comments", () => {
101
+ const content = `
102
102
  /**
103
103
  * TODO: add proper documentation
104
104
  */
105
105
  function foo() {}
106
106
  `;
107
- const filePath = createTempFile(tmpDir, "test.ts", content);
108
- const result = client.scanFile(filePath);
107
+ const filePath = createTempFile(tmpDir, "test.ts", content);
108
+ const result = client.scanFile(filePath);
109
109
 
110
- expect(result.length).toBe(1);
111
- expect(result[0].type).toBe("TODO");
112
- });
110
+ expect(result.length).toBe(1);
111
+ expect(result[0].type).toBe("TODO");
112
+ });
113
113
 
114
- it("should find TODO in Python comments", () => {
115
- const content = `
114
+ it("should find TODO in Python comments", () => {
115
+ const content = `
116
116
  # TODO: implement this
117
117
  def foo():
118
118
  pass
119
119
  `;
120
- const filePath = createTempFile(tmpDir, "test.py", content);
121
- const result = client.scanFile(filePath);
120
+ const filePath = createTempFile(tmpDir, "test.py", content);
121
+ const result = client.scanFile(filePath);
122
122
 
123
- expect(result.length).toBe(1);
124
- expect(result[0].type).toBe("TODO");
125
- });
123
+ expect(result.length).toBe(1);
124
+ expect(result[0].type).toBe("TODO");
125
+ });
126
126
 
127
- it("should skip TODO in strings", () => {
128
- const content = `
127
+ it("should skip TODO in strings", () => {
128
+ const content = `
129
129
  const message = "TODO: buy milk";
130
130
  `;
131
- const filePath = createTempFile(tmpDir, "test.ts", content);
132
- const result = client.scanFile(filePath);
131
+ const filePath = createTempFile(tmpDir, "test.ts", content);
132
+ const result = client.scanFile(filePath);
133
133
 
134
- expect(result.length).toBe(0);
135
- });
134
+ expect(result.length).toBe(0);
135
+ });
136
136
 
137
- it("should report correct line numbers", () => {
138
- const content = `
137
+ it("should report correct line numbers", () => {
138
+ const content = `
139
139
  const x = 1;
140
140
  const y = 2;
141
141
  // TODO: fix this
142
142
  const z = 3;
143
143
  `;
144
- const filePath = createTempFile(tmpDir, "test.ts", content);
145
- const result = client.scanFile(filePath);
144
+ const filePath = createTempFile(tmpDir, "test.ts", content);
145
+ const result = client.scanFile(filePath);
146
146
 
147
- expect(result[0].line).toBe(4);
148
- });
147
+ expect(result[0].line).toBe(4);
148
+ });
149
149
 
150
- it("should find multiple annotations in one file", () => {
151
- const content = `
150
+ it("should find multiple annotations in one file", () => {
151
+ const content = `
152
152
  // TODO: first
153
153
  // FIXME: second
154
154
  // HACK: third
155
155
  `;
156
- const filePath = createTempFile(tmpDir, "test.ts", content);
157
- const result = client.scanFile(filePath);
158
-
159
- expect(result.length).toBe(3);
160
- });
161
- });
162
-
163
- describe("scanDirectory", () => {
164
- it("should scan all files in directory", () => {
165
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
166
- createTempFile(tmpDir, "file2.ts", "// FIXME: bug 1");
167
- createTempFile(tmpDir, "file3.py", "# HACK: workaround");
168
-
169
- const result = client.scanDirectory(tmpDir);
170
-
171
- expect(result.items.length).toBe(3);
172
- });
173
-
174
- it("should skip node_modules", () => {
175
- createTempFile(tmpDir, "src/file.ts", "// TODO: task 1");
176
- createTempFile(tmpDir, "node_modules/lib/file.ts", "// TODO: should be skipped");
177
-
178
- const result = client.scanDirectory(tmpDir);
179
-
180
- expect(result.items.length).toBe(1);
181
- expect(result.items[0].file).not.toContain("node_modules");
182
- });
183
-
184
- it("should group items by type", () => {
185
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
186
- createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
187
- createTempFile(tmpDir, "file3.ts", "// FIXME: bug 1");
188
-
189
- const result = client.scanDirectory(tmpDir);
190
-
191
- expect(result.byType.get("TODO")?.length).toBe(2);
192
- expect(result.byType.get("FIXME")?.length).toBe(1);
193
- });
194
-
195
- it("should group items by file", () => {
196
- createTempFile(tmpDir, "file1.ts", "// TODO: task 1\n// FIXME: bug 1");
197
- createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
198
-
199
- const result = client.scanDirectory(tmpDir);
200
-
201
- const file1Items = [...result.byFile.entries()].find(([k]) => k.includes("file1.ts"));
202
- expect(file1Items?.[1].length).toBe(2);
203
- });
204
- });
205
-
206
- describe("formatResult", () => {
207
- it("should return empty string for no results", () => {
208
- const result = { items: [], byType: new Map(), byFile: new Map() };
209
- expect(client.formatResult(result)).toBe("");
210
- });
211
-
212
- it("should format results with counts", () => {
213
- const result = {
214
- items: [
215
- { type: "TODO" as const, message: "task 1", file: "test.ts", line: 1, column: 0 },
216
- { type: "FIXME" as const, message: "bug 1", file: "test.ts", line: 2, column: 0 },
217
- ],
218
- byType: new Map([["TODO", [{ type: "TODO" as const, message: "task 1", file: "test.ts", line: 1, column: 0 }]]]),
219
- byFile: new Map([["test.ts", [{ type: "TODO" as const, message: "task 1", file: "test.ts", line: 1, column: 0 }]]]),
220
- };
221
-
222
- const formatted = client.formatResult(result);
223
- expect(formatted).toContain("2 annotation(s)");
224
- expect(formatted).toContain("TODO");
225
- expect(formatted).toContain("FIXME");
226
- });
227
-
228
- it("should prioritize FIXME/HACK before TODO", () => {
229
- const result = {
230
- items: [
231
- { type: "FIXME" as const, message: "bug", file: "test.ts", line: 2, column: 0 },
232
- { type: "TODO" as const, message: "task", file: "test.ts", line: 1, column: 0 },
233
- ],
234
- byType: new Map(),
235
- byFile: new Map(),
236
- };
237
-
238
- const formatted = client.formatResult(result);
239
- // Check that FIXME line comes before TODO line in the sorted output
240
- const fixmeLineIndex = formatted.indexOf("🔴");
241
- const todoLineIndex = formatted.indexOf("📝");
242
- expect(fixmeLineIndex).toBeLessThan(todoLineIndex);
243
- });
244
-
245
- it("should show correct icons", () => {
246
- const result = {
247
- items: [
248
- { type: "FIXME" as const, message: "bug", file: "test.ts", line: 1, column: 0 },
249
- { type: "HACK" as const, message: "hack", file: "test.ts", line: 2, column: 0 },
250
- { type: "BUG" as const, message: "bug", file: "test.ts", line: 3, column: 0 },
251
- { type: "TODO" as const, message: "todo", file: "test.ts", line: 4, column: 0 },
252
- { type: "NOTE" as const, message: "note", file: "test.ts", line: 5, column: 0 },
253
- ],
254
- byType: new Map(),
255
- byFile: new Map(),
256
- };
257
-
258
- const formatted = client.formatResult(result);
259
- expect(formatted).toContain("🔴"); // FIXME
260
- expect(formatted).toContain("🟠"); // HACK
261
- expect(formatted).toContain("🐛"); // BUG
262
- expect(formatted).toContain("📝"); // TODO
263
- expect(formatted).toContain("â„šī¸"); // NOTE
264
- });
265
- });
156
+ const filePath = createTempFile(tmpDir, "test.ts", content);
157
+ const result = client.scanFile(filePath);
158
+
159
+ expect(result.length).toBe(3);
160
+ });
161
+ });
162
+
163
+ describe("scanDirectory", () => {
164
+ it("should scan all files in directory", () => {
165
+ createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
166
+ createTempFile(tmpDir, "file2.ts", "// FIXME: bug 1");
167
+ createTempFile(tmpDir, "file3.py", "# HACK: workaround");
168
+
169
+ const result = client.scanDirectory(tmpDir);
170
+
171
+ expect(result.items.length).toBe(3);
172
+ });
173
+
174
+ it("should skip node_modules", () => {
175
+ createTempFile(tmpDir, "src/file.ts", "// TODO: task 1");
176
+ createTempFile(
177
+ tmpDir,
178
+ "node_modules/lib/file.ts",
179
+ "// TODO: should be skipped",
180
+ );
181
+
182
+ const result = client.scanDirectory(tmpDir);
183
+
184
+ expect(result.items.length).toBe(1);
185
+ expect(result.items[0].file).not.toContain("node_modules");
186
+ });
187
+
188
+ it("should group items by type", () => {
189
+ createTempFile(tmpDir, "file1.ts", "// TODO: task 1");
190
+ createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
191
+ createTempFile(tmpDir, "file3.ts", "// FIXME: bug 1");
192
+
193
+ const result = client.scanDirectory(tmpDir);
194
+
195
+ expect(result.byType.get("TODO")?.length).toBe(2);
196
+ expect(result.byType.get("FIXME")?.length).toBe(1);
197
+ });
198
+
199
+ it("should group items by file", () => {
200
+ createTempFile(tmpDir, "file1.ts", "// TODO: task 1\n// FIXME: bug 1");
201
+ createTempFile(tmpDir, "file2.ts", "// TODO: task 2");
202
+
203
+ const result = client.scanDirectory(tmpDir);
204
+
205
+ const file1Items = [...result.byFile.entries()].find(([k]) =>
206
+ k.includes("file1.ts"),
207
+ );
208
+ expect(file1Items?.[1].length).toBe(2);
209
+ });
210
+ });
211
+
212
+ describe("formatResult", () => {
213
+ it("should return empty string for no results", () => {
214
+ const result = { items: [], byType: new Map(), byFile: new Map() };
215
+ expect(client.formatResult(result)).toBe("");
216
+ });
217
+
218
+ it("should format results with counts", () => {
219
+ const result = {
220
+ items: [
221
+ {
222
+ type: "TODO" as const,
223
+ message: "task 1",
224
+ file: "test.ts",
225
+ line: 1,
226
+ column: 0,
227
+ },
228
+ {
229
+ type: "FIXME" as const,
230
+ message: "bug 1",
231
+ file: "test.ts",
232
+ line: 2,
233
+ column: 0,
234
+ },
235
+ ],
236
+ byType: new Map([
237
+ [
238
+ "TODO",
239
+ [
240
+ {
241
+ type: "TODO" as const,
242
+ message: "task 1",
243
+ file: "test.ts",
244
+ line: 1,
245
+ column: 0,
246
+ },
247
+ ],
248
+ ],
249
+ ]),
250
+ byFile: new Map([
251
+ [
252
+ "test.ts",
253
+ [
254
+ {
255
+ type: "TODO" as const,
256
+ message: "task 1",
257
+ file: "test.ts",
258
+ line: 1,
259
+ column: 0,
260
+ },
261
+ ],
262
+ ],
263
+ ]),
264
+ };
265
+
266
+ const formatted = client.formatResult(result);
267
+ expect(formatted).toContain("2 annotation(s)");
268
+ expect(formatted).toContain("TODO");
269
+ expect(formatted).toContain("FIXME");
270
+ });
271
+
272
+ it("should prioritize FIXME/HACK before TODO", () => {
273
+ const result = {
274
+ items: [
275
+ {
276
+ type: "FIXME" as const,
277
+ message: "bug",
278
+ file: "test.ts",
279
+ line: 2,
280
+ column: 0,
281
+ },
282
+ {
283
+ type: "TODO" as const,
284
+ message: "task",
285
+ file: "test.ts",
286
+ line: 1,
287
+ column: 0,
288
+ },
289
+ ],
290
+ byType: new Map(),
291
+ byFile: new Map(),
292
+ };
293
+
294
+ const formatted = client.formatResult(result);
295
+ // Check that FIXME line comes before TODO line in the sorted output
296
+ const fixmeLineIndex = formatted.indexOf("🔴");
297
+ const todoLineIndex = formatted.indexOf("📝");
298
+ expect(fixmeLineIndex).toBeLessThan(todoLineIndex);
299
+ });
300
+
301
+ it("should show correct icons", () => {
302
+ const result = {
303
+ items: [
304
+ {
305
+ type: "FIXME" as const,
306
+ message: "bug",
307
+ file: "test.ts",
308
+ line: 1,
309
+ column: 0,
310
+ },
311
+ {
312
+ type: "HACK" as const,
313
+ message: "hack",
314
+ file: "test.ts",
315
+ line: 2,
316
+ column: 0,
317
+ },
318
+ {
319
+ type: "BUG" as const,
320
+ message: "bug",
321
+ file: "test.ts",
322
+ line: 3,
323
+ column: 0,
324
+ },
325
+ {
326
+ type: "TODO" as const,
327
+ message: "todo",
328
+ file: "test.ts",
329
+ line: 4,
330
+ column: 0,
331
+ },
332
+ {
333
+ type: "NOTE" as const,
334
+ message: "note",
335
+ file: "test.ts",
336
+ line: 5,
337
+ column: 0,
338
+ },
339
+ ],
340
+ byType: new Map(),
341
+ byFile: new Map(),
342
+ };
343
+
344
+ const formatted = client.formatResult(result);
345
+ expect(formatted).toContain("🔴"); // FIXME
346
+ expect(formatted).toContain("🟠"); // HACK
347
+ expect(formatted).toContain("🐛"); // BUG
348
+ expect(formatted).toContain("📝"); // TODO
349
+ expect(formatted).toContain("â„šī¸"); // NOTE
350
+ });
351
+ });
266
352
  });