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,72 +1,69 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
1
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
2
  import { ComplexityClient } from "./complexity-client.js";
3
3
  import { createTempFile, setupTestEnvironment } from "./test-utils.js";
4
4
 
5
5
  describe("ComplexityClient", () => {
6
- let client: ComplexityClient;
7
- let tmpDir: string;
8
- let cleanup: () => void;
9
-
10
- beforeEach(() => {
11
- client = new ComplexityClient();
12
- ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-complexity-test-"));
13
- });
14
-
15
- afterEach(() => {
16
- cleanup();
17
- });
18
-
19
- describe("isSupportedFile", () => {
20
- it("should support TypeScript files", () => {
21
- expect(client.isSupportedFile("test.ts")).toBe(true);
22
- expect(client.isSupportedFile("test.tsx")).toBe(true);
23
- });
24
-
25
- it("should support JavaScript files", () => {
26
- expect(client.isSupportedFile("test.js")).toBe(true);
27
- expect(client.isSupportedFile("test.jsx")).toBe(true);
28
- expect(client.isSupportedFile("test.mjs")).toBe(true);
29
- expect(client.isSupportedFile("test.cjs")).toBe(true);
30
- });
31
-
32
- it("should not support non-TS/JS files", () => {
33
- expect(client.isSupportedFile("test.py")).toBe(false);
34
- expect(client.isSupportedFile("test.json")).toBe(false);
35
- expect(client.isSupportedFile("test.md")).toBe(false);
36
- });
37
- });
38
-
39
- describe("analyzeFile", () => {
40
- it("should return null for non-existent files", () => {
41
- const result = client.analyzeFile("/nonexistent/file.ts");
42
- expect(result).toBeNull();
43
- });
44
-
45
- it("should analyze a simple function", () => {
46
-
47
- try {
48
- const content = `
6
+ let client: ComplexityClient;
7
+ let tmpDir: string;
8
+ let cleanup: () => void;
9
+
10
+ beforeEach(() => {
11
+ client = new ComplexityClient();
12
+ ({ tmpDir, cleanup } = setupTestEnvironment("pi-lens-complexity-test-"));
13
+ });
14
+
15
+ afterEach(() => {
16
+ cleanup();
17
+ });
18
+
19
+ describe("isSupportedFile", () => {
20
+ it("should support TypeScript files", () => {
21
+ expect(client.isSupportedFile("test.ts")).toBe(true);
22
+ expect(client.isSupportedFile("test.tsx")).toBe(true);
23
+ });
24
+
25
+ it("should support JavaScript files", () => {
26
+ expect(client.isSupportedFile("test.js")).toBe(true);
27
+ expect(client.isSupportedFile("test.jsx")).toBe(true);
28
+ expect(client.isSupportedFile("test.mjs")).toBe(true);
29
+ expect(client.isSupportedFile("test.cjs")).toBe(true);
30
+ });
31
+
32
+ it("should not support non-TS/JS files", () => {
33
+ expect(client.isSupportedFile("test.py")).toBe(false);
34
+ expect(client.isSupportedFile("test.json")).toBe(false);
35
+ expect(client.isSupportedFile("test.md")).toBe(false);
36
+ });
37
+ });
38
+
39
+ describe("analyzeFile", () => {
40
+ it("should return null for non-existent files", () => {
41
+ const result = client.analyzeFile("/nonexistent/file.ts");
42
+ expect(result).toBeNull();
43
+ });
44
+
45
+ it("should analyze a simple function", () => {
46
+ try {
47
+ const content = `
49
48
  function greet(name: string): string {
50
49
  return "Hello, " + name;
51
50
  }
52
51
  `;
53
- const filePath = createTempFile(tmpDir, "simple.ts", content);
54
- const result = client.analyzeFile(filePath);
55
-
56
- expect(result).not.toBeNull();
57
- expect(result!.functionCount).toBe(1);
58
- expect(result!.cyclomaticComplexity).toBe(1);
59
- expect(result!.cognitiveComplexity).toBe(0);
60
- expect(result!.maxNestingDepth).toBeGreaterThanOrEqual(1);
61
- } finally {
62
-
63
- }
64
- });
65
-
66
- it("should detect if statements in cyclomatic complexity", () => {
67
-
68
- try {
69
- const content = `
52
+ const filePath = createTempFile(tmpDir, "simple.ts", content);
53
+ const result = client.analyzeFile(filePath);
54
+
55
+ expect(result).not.toBeNull();
56
+ expect(result?.functionCount).toBe(1);
57
+ expect(result?.cyclomaticComplexity).toBe(1);
58
+ expect(result?.cognitiveComplexity).toBe(0);
59
+ expect(result?.maxNestingDepth).toBeGreaterThanOrEqual(1);
60
+ } finally {
61
+ }
62
+ });
63
+
64
+ it("should detect if statements in cyclomatic complexity", () => {
65
+ try {
66
+ const content = `
70
67
  function check(x: number): string {
71
68
  if (x > 0) {
72
69
  return "positive";
@@ -77,40 +74,36 @@ function check(x: number): string {
77
74
  }
78
75
  }
79
76
  `;
80
- const filePath = createTempFile(tmpDir, "if-test.ts", content);
81
- const result = client.analyzeFile(filePath);
82
-
83
- expect(result).not.toBeNull();
84
- // 1 base + 1 if + 1 else-if = 3
85
- expect(result!.cyclomaticComplexity).toBeGreaterThanOrEqual(3);
86
- } finally {
87
-
88
- }
89
- });
90
-
91
- it("should calculate maintainability index", () => {
92
-
93
- try {
94
- const content = `
77
+ const filePath = createTempFile(tmpDir, "if-test.ts", content);
78
+ const result = client.analyzeFile(filePath);
79
+
80
+ expect(result).not.toBeNull();
81
+ // 1 base + 1 if + 1 else-if = 3
82
+ expect(result?.cyclomaticComplexity).toBeGreaterThanOrEqual(3);
83
+ } finally {
84
+ }
85
+ });
86
+
87
+ it("should calculate maintainability index", () => {
88
+ try {
89
+ const content = `
95
90
  function simple(): number {
96
91
  return 42;
97
92
  }
98
93
  `;
99
- const filePath = createTempFile(tmpDir, "mi-test.ts", content);
100
- const result = client.analyzeFile(filePath);
101
-
102
- expect(result).not.toBeNull();
103
- expect(result!.maintainabilityIndex).toBeGreaterThan(0);
104
- expect(result!.maintainabilityIndex).toBeLessThanOrEqual(100);
105
- } finally {
106
-
107
- }
108
- });
109
-
110
- it("should detect deep nesting", () => {
111
-
112
- try {
113
- const content = `
94
+ const filePath = createTempFile(tmpDir, "mi-test.ts", content);
95
+ const result = client.analyzeFile(filePath);
96
+
97
+ expect(result).not.toBeNull();
98
+ expect(result?.maintainabilityIndex).toBeGreaterThan(0);
99
+ expect(result?.maintainabilityIndex).toBeLessThanOrEqual(100);
100
+ } finally {
101
+ }
102
+ });
103
+
104
+ it("should detect deep nesting", () => {
105
+ try {
106
+ const content = `
114
107
  function deepNest(arr: number[][][][]): number {
115
108
  for (let i = 0; i < arr.length; i++) {
116
109
  for (let j = 0; j < arr[i].length; j++) {
@@ -126,20 +119,18 @@ function deepNest(arr: number[][][][]): number {
126
119
  return 0;
127
120
  }
128
121
  `;
129
- const filePath = createTempFile(tmpDir, "nesting-test.ts", content);
130
- const result = client.analyzeFile(filePath);
131
-
132
- expect(result).not.toBeNull();
133
- expect(result!.maxNestingDepth).toBeGreaterThanOrEqual(5);
134
- } finally {
135
-
136
- }
137
- });
138
-
139
- it("should count cognitive complexity with nesting penalty", () => {
140
-
141
- try {
142
- const content = `
122
+ const filePath = createTempFile(tmpDir, "nesting-test.ts", content);
123
+ const result = client.analyzeFile(filePath);
124
+
125
+ expect(result).not.toBeNull();
126
+ expect(result?.maxNestingDepth).toBeGreaterThanOrEqual(5);
127
+ } finally {
128
+ }
129
+ });
130
+
131
+ it("should count cognitive complexity with nesting penalty", () => {
132
+ try {
133
+ const content = `
143
134
  function nested(x: number, y: number): number {
144
135
  if (x > 0) {
145
136
  if (y > 0) {
@@ -151,40 +142,36 @@ function nested(x: number, y: number): number {
151
142
  return 0;
152
143
  }
153
144
  `;
154
- const filePath = createTempFile(tmpDir, "cognitive-test.ts", content);
155
- const result = client.analyzeFile(filePath);
156
-
157
- expect(result).not.toBeNull();
158
- // Cognitive: 1 (if) + 2 (nested if) + 3 (deeply nested if) = 6
159
- expect(result!.cognitiveComplexity).toBeGreaterThanOrEqual(6);
160
- } finally {
161
-
162
- }
163
- });
164
-
165
- it("should calculate halstead volume", () => {
166
-
167
- try {
168
- const content = `
145
+ const filePath = createTempFile(tmpDir, "cognitive-test.ts", content);
146
+ const result = client.analyzeFile(filePath);
147
+
148
+ expect(result).not.toBeNull();
149
+ // Cognitive: 1 (if) + 2 (nested if) + 3 (deeply nested if) = 6
150
+ expect(result?.cognitiveComplexity).toBeGreaterThanOrEqual(6);
151
+ } finally {
152
+ }
153
+ });
154
+
155
+ it("should calculate halstead volume", () => {
156
+ try {
157
+ const content = `
169
158
  function add(a: number, b: number): number {
170
159
  return a + b;
171
160
  }
172
161
  `;
173
- const filePath = createTempFile(tmpDir, "halstead-test.ts", content);
174
- const result = client.analyzeFile(filePath);
175
-
176
- expect(result).not.toBeNull();
177
- expect(result!.halsteadVolume).toBeGreaterThan(0);
178
- } finally {
179
-
180
- }
181
- });
182
-
183
- it("should measure function length", () => {
184
-
185
- try {
186
- const shortContent = `function short() { return 1; }`;
187
- const longContent = `
162
+ const filePath = createTempFile(tmpDir, "halstead-test.ts", content);
163
+ const result = client.analyzeFile(filePath);
164
+
165
+ expect(result).not.toBeNull();
166
+ expect(result?.halsteadVolume).toBeGreaterThan(0);
167
+ } finally {
168
+ }
169
+ });
170
+
171
+ it("should measure function length", () => {
172
+ try {
173
+ const shortContent = `function short() { return 1; }`;
174
+ const longContent = `
188
175
  function long(): number {
189
176
  const a = 1;
190
177
  const b = 2;
@@ -199,69 +186,70 @@ function long(): number {
199
186
  return a + b + c + d + e + f + g + h + i + j;
200
187
  }
201
188
  `;
202
- const shortPath = createTempFile(tmpDir, "short.ts", shortContent);
203
- const longPath = createTempFile(tmpDir, "long.ts", longContent);
204
-
205
- const shortResult = client.analyzeFile(shortPath);
206
- const longResult = client.analyzeFile(longPath);
207
-
208
- expect(shortResult!.maxFunctionLength).toBeLessThan(longResult!.maxFunctionLength);
209
- } finally {
210
-
211
- }
212
- });
213
- });
214
-
215
- describe("formatMetrics", () => {
216
- it("should format metrics for display", () => {
217
- const metrics = {
218
- filePath: "test.ts",
219
- maxNestingDepth: 4,
220
- avgFunctionLength: 15,
221
- maxFunctionLength: 30,
222
- functionCount: 3,
223
- cyclomaticComplexity: 4,
224
- maxCyclomaticComplexity: 8,
225
- cognitiveComplexity: 12,
226
- halsteadVolume: 200,
227
- maintainabilityIndex: 75,
228
- linesOfCode: 100,
229
- commentLines: 10,
230
- codeEntropy: 0.5,
231
- maxParamsInFunction: 3,
232
- aiCommentPatterns: 1,
233
- singleUseFunctions: 0,
234
- tryCatchCount: 1,
235
- };
236
-
237
- const formatted = client.formatMetrics(metrics);
238
- expect(formatted).toContain("test.ts");
239
- expect(formatted).toContain("75/100");
240
- });
241
-
242
- it("should warn about low maintainability", () => {
243
- const metrics = {
244
- filePath: "bad.ts",
245
- maxNestingDepth: 8,
246
- avgFunctionLength: 60,
247
- maxFunctionLength: 100,
248
- functionCount: 5,
249
- cyclomaticComplexity: 15,
250
- maxCyclomaticComplexity: 25,
251
- cognitiveComplexity: 50,
252
- halsteadVolume: 800,
253
- maintainabilityIndex: 25,
254
- linesOfCode: 500,
255
- commentLines: 10,
256
- codeEntropy: 0.5,
257
- maxParamsInFunction: 4,
258
- aiCommentPatterns: 2,
259
- singleUseFunctions: 1,
260
- tryCatchCount: 2,
261
- };
262
-
263
- const formatted = client.formatMetrics(metrics);
264
- expect(formatted).toContain("✗"); // Low MI indicator
265
- });
266
- });
189
+ const shortPath = createTempFile(tmpDir, "short.ts", shortContent);
190
+ const longPath = createTempFile(tmpDir, "long.ts", longContent);
191
+
192
+ const shortResult = client.analyzeFile(shortPath);
193
+ const longResult = client.analyzeFile(longPath);
194
+
195
+ expect(shortResult?.maxFunctionLength ?? 0).toBeLessThan(
196
+ longResult?.maxFunctionLength ?? 0,
197
+ );
198
+ } finally {
199
+ }
200
+ });
201
+ });
202
+
203
+ describe("formatMetrics", () => {
204
+ it("should format metrics for display", () => {
205
+ const metrics = {
206
+ filePath: "test.ts",
207
+ maxNestingDepth: 4,
208
+ avgFunctionLength: 15,
209
+ maxFunctionLength: 30,
210
+ functionCount: 3,
211
+ cyclomaticComplexity: 4,
212
+ maxCyclomaticComplexity: 8,
213
+ cognitiveComplexity: 12,
214
+ halsteadVolume: 200,
215
+ maintainabilityIndex: 75,
216
+ linesOfCode: 100,
217
+ commentLines: 10,
218
+ codeEntropy: 0.5,
219
+ maxParamsInFunction: 3,
220
+ aiCommentPatterns: 1,
221
+ singleUseFunctions: 0,
222
+ tryCatchCount: 1,
223
+ };
224
+
225
+ const formatted = client.formatMetrics(metrics);
226
+ expect(formatted).toContain("test.ts");
227
+ expect(formatted).toContain("75/100");
228
+ });
229
+
230
+ it("should warn about low maintainability", () => {
231
+ const metrics = {
232
+ filePath: "bad.ts",
233
+ maxNestingDepth: 8,
234
+ avgFunctionLength: 60,
235
+ maxFunctionLength: 100,
236
+ functionCount: 5,
237
+ cyclomaticComplexity: 15,
238
+ maxCyclomaticComplexity: 25,
239
+ cognitiveComplexity: 50,
240
+ halsteadVolume: 800,
241
+ maintainabilityIndex: 25,
242
+ linesOfCode: 500,
243
+ commentLines: 10,
244
+ codeEntropy: 0.5,
245
+ maxParamsInFunction: 4,
246
+ aiCommentPatterns: 2,
247
+ singleUseFunctions: 1,
248
+ tryCatchCount: 2,
249
+ };
250
+
251
+ const formatted = client.formatMetrics(metrics);
252
+ expect(formatted).toContain("✗"); // Low MI indicator
253
+ });
254
+ });
267
255
  });