pi-lens 3.6.2 → 3.6.4

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 (207) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/package.json +4 -4
  3. package/tsconfig.json +1 -1
  4. package/clients/__tests__/file-time.test.js +0 -216
  5. package/clients/__tests__/file-time.test.ts +0 -276
  6. package/clients/__tests__/format-service.test.js +0 -245
  7. package/clients/__tests__/format-service.test.ts +0 -339
  8. package/clients/__tests__/formatters.test.js +0 -271
  9. package/clients/__tests__/formatters.test.ts +0 -401
  10. package/clients/agent-behavior-client.js +0 -110
  11. package/clients/agent-behavior-client.test.js +0 -94
  12. package/clients/agent-behavior-client.test.ts +0 -116
  13. package/clients/amain-types.js +0 -164
  14. package/clients/architect-client.js +0 -291
  15. package/clients/ast-grep-client.js +0 -253
  16. package/clients/ast-grep-parser.js +0 -84
  17. package/clients/ast-grep-rule-manager.js +0 -89
  18. package/clients/ast-grep-types.js +0 -9
  19. package/clients/auto-loop.js +0 -131
  20. package/clients/biome-client.js +0 -420
  21. package/clients/biome-client.test.js +0 -144
  22. package/clients/biome-client.test.ts +0 -163
  23. package/clients/cache/rule-cache.js +0 -72
  24. package/clients/cache-manager.js +0 -245
  25. package/clients/cache-manager.test.js +0 -197
  26. package/clients/cache-manager.test.ts +0 -299
  27. package/clients/complexity-client.js +0 -675
  28. package/clients/complexity-client.test.js +0 -234
  29. package/clients/complexity-client.test.ts +0 -255
  30. package/clients/config-validator.js +0 -465
  31. package/clients/dependency-checker.js +0 -325
  32. package/clients/dependency-checker.test.js +0 -60
  33. package/clients/dependency-checker.test.ts +0 -71
  34. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  35. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  36. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  37. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  38. package/clients/dispatch/debug.log +0 -1
  39. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  40. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  41. package/clients/dispatch/dispatcher.format.test.js +0 -46
  42. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  43. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  44. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  45. package/clients/dispatch/dispatcher.js +0 -381
  46. package/clients/dispatch/dispatcher.test.js +0 -116
  47. package/clients/dispatch/dispatcher.test.ts +0 -149
  48. package/clients/dispatch/integration.js +0 -108
  49. package/clients/dispatch/plan.js +0 -183
  50. package/clients/dispatch/runners/architect.js +0 -83
  51. package/clients/dispatch/runners/architect.test.js +0 -138
  52. package/clients/dispatch/runners/architect.test.ts +0 -162
  53. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  54. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  55. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  56. package/clients/dispatch/runners/ast-grep.js +0 -157
  57. package/clients/dispatch/runners/biome.js +0 -55
  58. package/clients/dispatch/runners/config-validation.js +0 -67
  59. package/clients/dispatch/runners/go-vet.js +0 -48
  60. package/clients/dispatch/runners/index.js +0 -47
  61. package/clients/dispatch/runners/lsp.js +0 -102
  62. package/clients/dispatch/runners/oxlint.js +0 -67
  63. package/clients/dispatch/runners/oxlint.test.js +0 -230
  64. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  65. package/clients/dispatch/runners/pyright.js +0 -100
  66. package/clients/dispatch/runners/pyright.test.js +0 -98
  67. package/clients/dispatch/runners/pyright.test.ts +0 -121
  68. package/clients/dispatch/runners/python-slop.js +0 -97
  69. package/clients/dispatch/runners/python-slop.test.js +0 -203
  70. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  71. package/clients/dispatch/runners/ruff.js +0 -48
  72. package/clients/dispatch/runners/rust-clippy.js +0 -102
  73. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  74. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  75. package/clients/dispatch/runners/shellcheck.js +0 -147
  76. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  77. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  78. package/clients/dispatch/runners/similarity.js +0 -230
  79. package/clients/dispatch/runners/spellcheck.js +0 -106
  80. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  81. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  82. package/clients/dispatch/runners/tree-sitter.js +0 -246
  83. package/clients/dispatch/runners/ts-lsp.js +0 -125
  84. package/clients/dispatch/runners/ts-slop.js +0 -113
  85. package/clients/dispatch/runners/type-safety.js +0 -142
  86. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  87. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  88. package/clients/dispatch/runners/utils.js +0 -51
  89. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  90. package/clients/dispatch/types.js +0 -16
  91. package/clients/dispatch/utils/format-utils.js +0 -44
  92. package/clients/dogfood.test.js +0 -201
  93. package/clients/dogfood.test.ts +0 -269
  94. package/clients/file-kinds.js +0 -177
  95. package/clients/file-kinds.test.js +0 -169
  96. package/clients/file-kinds.test.ts +0 -210
  97. package/clients/file-time.js +0 -152
  98. package/clients/file-utils.js +0 -40
  99. package/clients/fix-scanners.js +0 -204
  100. package/clients/format-service.js +0 -184
  101. package/clients/formatters.js +0 -488
  102. package/clients/go-client.js +0 -203
  103. package/clients/go-client.test.js +0 -127
  104. package/clients/go-client.test.ts +0 -143
  105. package/clients/installer/index.js +0 -403
  106. package/clients/interviewer-templates.js +0 -75
  107. package/clients/interviewer.js +0 -173
  108. package/clients/jscpd-client.js +0 -196
  109. package/clients/jscpd-client.test.js +0 -127
  110. package/clients/jscpd-client.test.ts +0 -145
  111. package/clients/knip-client.js +0 -239
  112. package/clients/knip-client.test.js +0 -112
  113. package/clients/knip-client.test.ts +0 -128
  114. package/clients/latency-logger.js +0 -40
  115. package/clients/lsp/__tests__/client.test.js +0 -310
  116. package/clients/lsp/__tests__/client.test.ts +0 -412
  117. package/clients/lsp/__tests__/config.test.js +0 -167
  118. package/clients/lsp/__tests__/config.test.ts +0 -217
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  121. package/clients/lsp/__tests__/integration.test.js +0 -127
  122. package/clients/lsp/__tests__/integration.test.ts +0 -160
  123. package/clients/lsp/__tests__/launch.test.js +0 -313
  124. package/clients/lsp/__tests__/launch.test.ts +0 -394
  125. package/clients/lsp/__tests__/server.test.js +0 -259
  126. package/clients/lsp/__tests__/server.test.ts +0 -332
  127. package/clients/lsp/__tests__/service.test.js +0 -438
  128. package/clients/lsp/__tests__/service.test.ts +0 -530
  129. package/clients/lsp/client.js +0 -350
  130. package/clients/lsp/config.js +0 -112
  131. package/clients/lsp/index.js +0 -318
  132. package/clients/lsp/installer/index.js +0 -391
  133. package/clients/lsp/interactive-install.js +0 -221
  134. package/clients/lsp/language.js +0 -170
  135. package/clients/lsp/launch.js +0 -329
  136. package/clients/lsp/lsp/launch.js +0 -116
  137. package/clients/lsp/lsp/server.js +0 -532
  138. package/clients/lsp/lsp-index.js +0 -10
  139. package/clients/lsp/path-utils.js +0 -5
  140. package/clients/lsp/server.js +0 -725
  141. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  142. package/clients/lsp/test-py-spawn/test.py +0 -3
  143. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  144. package/clients/lsp/test-py-svc/test.py +0 -3
  145. package/clients/lsp/test-python-project/requirements.txt +0 -1
  146. package/clients/lsp/test-python-project/test.py +0 -5
  147. package/clients/metrics-client.js +0 -107
  148. package/clients/metrics-client.test.js +0 -128
  149. package/clients/metrics-client.test.ts +0 -163
  150. package/clients/metrics-history.js +0 -367
  151. package/clients/path-utils.js +0 -142
  152. package/clients/pipeline.js +0 -272
  153. package/clients/production-readiness.js +0 -522
  154. package/clients/project-index.js +0 -255
  155. package/clients/project-metadata.js +0 -531
  156. package/clients/ruff-client.js +0 -325
  157. package/clients/ruff-client.test.js +0 -132
  158. package/clients/ruff-client.test.ts +0 -153
  159. package/clients/rules-scanner.js +0 -97
  160. package/clients/runner-tracker.js +0 -152
  161. package/clients/rust-client.js +0 -205
  162. package/clients/rust-client.test.js +0 -108
  163. package/clients/rust-client.test.ts +0 -130
  164. package/clients/safe-spawn-async.js +0 -163
  165. package/clients/safe-spawn.js +0 -241
  166. package/clients/sanitize.js +0 -291
  167. package/clients/sanitize.test.js +0 -177
  168. package/clients/sanitize.test.ts +0 -223
  169. package/clients/scan-architectural-debt.js +0 -167
  170. package/clients/scan-utils.js +0 -83
  171. package/clients/secrets-scanner.js +0 -119
  172. package/clients/secrets-scanner.test.js +0 -100
  173. package/clients/secrets-scanner.test.ts +0 -113
  174. package/clients/sg-runner.js +0 -292
  175. package/clients/state-matrix.js +0 -160
  176. package/clients/subprocess-client.js +0 -65
  177. package/clients/symbol-types.js +0 -5
  178. package/clients/test-runner-client.js +0 -523
  179. package/clients/test-runner-client.test.js +0 -192
  180. package/clients/test-runner-client.test.ts +0 -253
  181. package/clients/test-utils.js +0 -27
  182. package/clients/test-utils.ts +0 -36
  183. package/clients/todo-scanner.js +0 -200
  184. package/clients/todo-scanner.test.js +0 -301
  185. package/clients/todo-scanner.test.ts +0 -352
  186. package/clients/tool-availability.js +0 -207
  187. package/clients/tree-sitter-client.js +0 -601
  188. package/clients/tree-sitter-query-loader.js +0 -355
  189. package/clients/tree-sitter-symbol-extractor.js +0 -289
  190. package/clients/ts-service.js +0 -129
  191. package/clients/type-coverage-client.js +0 -127
  192. package/clients/type-coverage-client.test.js +0 -105
  193. package/clients/type-coverage-client.test.ts +0 -125
  194. package/clients/type-safety-client.js +0 -138
  195. package/clients/types.js +0 -11
  196. package/clients/typescript-client.codefix.test.js +0 -157
  197. package/clients/typescript-client.codefix.test.ts +0 -186
  198. package/clients/typescript-client.js +0 -509
  199. package/clients/typescript-client.test.js +0 -105
  200. package/clients/typescript-client.test.ts +0 -126
  201. package/commands/booboo.js +0 -1007
  202. package/commands/fix-from-booboo.js +0 -398
  203. package/commands/fix-simplified.js +0 -618
  204. package/commands/rate.js +0 -281
  205. package/commands/rate.test.js +0 -119
  206. package/commands/rate.test.ts +0 -131
  207. package/commands/refactor.js +0 -130
@@ -1,129 +0,0 @@
1
- /**
2
- * Shared TypeScript Service for pi-lens
3
- *
4
- * Creates a single ts.Program per session that is shared across all clients
5
- * (complexity-client, type-safety-client). Avoids creating a new program per file.
6
- */
7
- import * as fs from "node:fs";
8
- import * as ts from "typescript";
9
- export class TypeScriptService {
10
- constructor(verbose = false) {
11
- this.program = null;
12
- this.checker = null;
13
- this.files = new Map();
14
- this.log = verbose
15
- ? (msg) => console.error(`[ts-service] ${msg}`)
16
- : () => { };
17
- }
18
- /**
19
- * Update a file's content in the service
20
- */
21
- updateFile(filePath, content) {
22
- this.files.set(filePath, content);
23
- this.invalidate();
24
- }
25
- /**
26
- * Invalidate the program (rebuild on next access)
27
- */
28
- invalidate() {
29
- this.program = null;
30
- this.checker = null;
31
- }
32
- /**
33
- * Get the shared type checker (rebuilds program if needed)
34
- */
35
- getChecker() {
36
- if (this.checker)
37
- return this.checker;
38
- if (this.files.size === 0)
39
- return null;
40
- try {
41
- const compilerOptions = {
42
- target: ts.ScriptTarget.Latest,
43
- module: ts.ModuleKind.ESNext,
44
- strict: true,
45
- noEmit: true,
46
- skipLibCheck: true,
47
- lib: ["es2020"],
48
- };
49
- const host = ts.createCompilerHost(compilerOptions);
50
- // Override getSourceFile to return our cached files
51
- const originalGetSourceFile = host.getSourceFile;
52
- host.getSourceFile = (fileName, languageVersion) => {
53
- // Check if we have this file cached
54
- const cachedContent = this.files.get(fileName);
55
- if (cachedContent !== undefined) {
56
- return ts.createSourceFile(fileName, cachedContent, languageVersion, true);
57
- }
58
- // Fall back to default (for lib files, etc.)
59
- return originalGetSourceFile(fileName, languageVersion);
60
- };
61
- // Override fileExists and readFile
62
- const originalFileExists = host.fileExists;
63
- host.fileExists = (fileName) => {
64
- if (this.files.has(fileName))
65
- return true;
66
- return originalFileExists(fileName);
67
- };
68
- const originalReadFile = host.readFile;
69
- host.readFile = (fileName) => {
70
- const cached = this.files.get(fileName);
71
- if (cached !== undefined)
72
- return cached;
73
- return originalReadFile(fileName);
74
- };
75
- const fileNames = [...this.files.keys()];
76
- this.program = ts.createProgram(fileNames, compilerOptions, host);
77
- this.checker = this.program.getTypeChecker();
78
- this.log(`Program created with ${fileNames.length} files`);
79
- return this.checker;
80
- }
81
- catch (error) {
82
- this.log(`Error creating program: ${error}`);
83
- return null;
84
- }
85
- }
86
- /**
87
- * Get source file for a path
88
- */
89
- getSourceFile(filePath) {
90
- const content = this.files.get(filePath);
91
- if (!content) {
92
- // Try to read from disk
93
- try {
94
- const diskContent = fs.readFileSync(filePath, "utf-8");
95
- this.files.set(filePath, diskContent);
96
- return ts.createSourceFile(filePath, diskContent, ts.ScriptTarget.Latest, true);
97
- }
98
- catch {
99
- return null;
100
- }
101
- }
102
- return ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
103
- }
104
- /**
105
- * Get source file from program (for type checking)
106
- */
107
- getSourceFileFromProgram(filePath) {
108
- // Ensure program is built
109
- const checker = this.getChecker();
110
- if (!checker || !this.program)
111
- return null;
112
- return this.program.getSourceFile(filePath) ?? null;
113
- }
114
- /**
115
- * Clear all cached files
116
- */
117
- clear() {
118
- this.files.clear();
119
- this.invalidate();
120
- }
121
- }
122
- // --- Singleton ---
123
- let instance = null;
124
- export function getTypeScriptService(verbose = false) {
125
- if (!instance) {
126
- instance = new TypeScriptService(verbose);
127
- }
128
- return instance;
129
- }
@@ -1,127 +0,0 @@
1
- /**
2
- * TypeCoverage Client for pi-lens
3
- *
4
- * Measures what percentage of TypeScript identifiers are properly typed
5
- * (i.e. not implicitly or explicitly `any`). Complements the LSP — the LSP
6
- * catches hard errors, this catches type weakness.
7
- *
8
- * Requires: npm install -D type-coverage
9
- * Docs: https://github.com/plantain-00/type-coverage
10
- */
11
- import * as path from "node:path";
12
- import { safeSpawn } from "./safe-spawn.js";
13
- // --- Client ---
14
- export class TypeCoverageClient {
15
- constructor(verbose = false) {
16
- this.available = null;
17
- this.log = verbose
18
- ? (msg) => console.error(`[type-coverage] ${msg}`)
19
- : () => { };
20
- }
21
- isAvailable() {
22
- if (this.available !== null)
23
- return this.available;
24
- const result = safeSpawn("npx", ["type-coverage", "--version"], {
25
- timeout: 10000,
26
- });
27
- this.available = !result.error && result.status === 0;
28
- return this.available;
29
- }
30
- /**
31
- * Run type-coverage on the project at cwd.
32
- * Uses --detail to get per-identifier locations for untyped names.
33
- * Uses --strict to count `any` casts as untyped.
34
- */
35
- scan(cwd) {
36
- if (!this.isAvailable()) {
37
- return {
38
- success: false,
39
- percentage: 0,
40
- typed: 0,
41
- total: 0,
42
- untypedLocations: [],
43
- };
44
- }
45
- try {
46
- const result = safeSpawn("npx", [
47
- "type-coverage",
48
- "--detail",
49
- "--strict",
50
- "--ignore-files",
51
- "**/*.d.ts",
52
- ], {
53
- timeout: 30000,
54
- cwd,
55
- });
56
- const output = (result.stdout ?? "") + (result.stderr ?? "");
57
- return this.parseOutput(output, cwd);
58
- }
59
- catch (err) {
60
- this.log(`Scan error: ${err.message}`);
61
- return {
62
- success: false,
63
- percentage: 0,
64
- typed: 0,
65
- total: 0,
66
- untypedLocations: [],
67
- };
68
- }
69
- }
70
- formatResult(result, maxLocations = 10) {
71
- if (!result.success)
72
- return "";
73
- const pct = result.percentage.toFixed(1);
74
- let icon = "✗";
75
- if (result.percentage >= 95)
76
- icon = "✓";
77
- else if (result.percentage >= 80)
78
- icon = "⚠";
79
- let output = `[type-coverage] ${icon} ${pct}% typed (${result.typed}/${result.total} identifiers)`;
80
- if (result.untypedLocations.length === 0) {
81
- output += " — fully typed\n";
82
- return output;
83
- }
84
- output += `:\n`;
85
- for (const loc of result.untypedLocations.slice(0, maxLocations)) {
86
- output += ` ${path.basename(loc.file)}:${loc.line}:${loc.column} — ${loc.name}\n`;
87
- }
88
- if (result.untypedLocations.length > maxLocations) {
89
- output += ` ... and ${result.untypedLocations.length - maxLocations} more\n`;
90
- }
91
- return output;
92
- }
93
- // --- Internal ---
94
- parseOutput(output, cwd) {
95
- const untypedLocations = [];
96
- // Parse detail lines: "path/to/file.ts:line:col: name"
97
- const detailPattern = /^(.+):(\d+):(\d+):\s+(.+)$/gm;
98
- let match;
99
- while ((match = detailPattern.exec(output)) !== null) {
100
- const [, file, line, col, name] = match;
101
- // Skip the summary line which also matches the pattern
102
- if (name.includes("%") || name.includes("/"))
103
- continue;
104
- untypedLocations.push({
105
- file: path.resolve(cwd, file),
106
- line: parseInt(line, 10),
107
- column: parseInt(col, 10),
108
- name: name.trim(),
109
- });
110
- }
111
- // Parse summary: "(3979 / 4100) 97.04%"
112
- const summaryMatch = output.match(/\((\d+)\s*\/\s*(\d+)\)\s*([\d.]+)%/);
113
- if (!summaryMatch) {
114
- return {
115
- success: false,
116
- percentage: 0,
117
- typed: 0,
118
- total: 0,
119
- untypedLocations: [],
120
- };
121
- }
122
- const typed = parseInt(summaryMatch[1], 10);
123
- const total = parseInt(summaryMatch[2], 10);
124
- const percentage = parseFloat(summaryMatch[3]);
125
- return { success: true, percentage, typed, total, untypedLocations };
126
- }
127
- }
@@ -1,105 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { setupTestEnvironment } from "./test-utils.js";
3
- import { TypeCoverageClient } from "./type-coverage-client.js";
4
- describe("TypeCoverageClient", () => {
5
- let client;
6
- let _tmpDir;
7
- let cleanup;
8
- beforeEach(() => {
9
- client = new TypeCoverageClient();
10
- ({ tmpDir: _tmpDir, cleanup } = setupTestEnvironment("pi-lens-typecoverage-test-"));
11
- });
12
- afterEach(() => {
13
- cleanup();
14
- });
15
- afterEach(() => {
16
- cleanup();
17
- });
18
- describe("isAvailable", () => {
19
- it("should check type-coverage availability", () => {
20
- const available = client.isAvailable();
21
- expect(typeof available).toBe("boolean");
22
- });
23
- });
24
- describe("formatResult", () => {
25
- it("should return empty string when not successful", () => {
26
- const result = {
27
- success: false,
28
- percentage: 0,
29
- typed: 0,
30
- total: 0,
31
- untypedLocations: [],
32
- };
33
- expect(client.formatResult(result)).toBe("");
34
- });
35
- it("should show coverage percentage", () => {
36
- const result = {
37
- success: true,
38
- percentage: 95,
39
- typed: 95,
40
- total: 100,
41
- untypedLocations: [],
42
- };
43
- const formatted = client.formatResult(result);
44
- expect(formatted).toContain("95.0%");
45
- expect(formatted).toContain("95/100");
46
- });
47
- it("should show warning for low coverage", () => {
48
- const result = {
49
- success: true,
50
- percentage: 80,
51
- typed: 80,
52
- total: 100,
53
- untypedLocations: [],
54
- };
55
- const formatted = client.formatResult(result);
56
- expect(formatted).toContain("⚠");
57
- });
58
- it("should show checkmark for high coverage", () => {
59
- const result = {
60
- success: true,
61
- percentage: 100,
62
- typed: 100,
63
- total: 100,
64
- untypedLocations: [],
65
- };
66
- const formatted = client.formatResult(result);
67
- expect(formatted).toContain("✓");
68
- });
69
- it("should show untyped locations", () => {
70
- const result = {
71
- success: true,
72
- percentage: 90,
73
- typed: 90,
74
- total: 100,
75
- untypedLocations: [
76
- { file: "test.ts", line: 10, column: 5, name: "x" },
77
- { file: "test.ts", line: 20, column: 8, name: "y" },
78
- ],
79
- };
80
- const formatted = client.formatResult(result);
81
- expect(formatted).toContain("test.ts:10");
82
- expect(formatted).toContain("test.ts:20");
83
- expect(formatted).toContain("x");
84
- expect(formatted).toContain("y");
85
- });
86
- it("should truncate long untyped location lists", () => {
87
- const locations = Array.from({ length: 20 }, (_, i) => ({
88
- file: `file${i}.ts`,
89
- line: i + 1,
90
- column: 0,
91
- name: `var${i}`,
92
- }));
93
- const result = {
94
- success: true,
95
- percentage: 80,
96
- typed: 80,
97
- total: 100,
98
- untypedLocations: locations,
99
- };
100
- const formatted = client.formatResult(result, 10);
101
- expect(formatted).toContain("...");
102
- expect(formatted).toContain("10 more");
103
- });
104
- });
105
- });
@@ -1,125 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { setupTestEnvironment } from "./test-utils.js";
3
- import { TypeCoverageClient } from "./type-coverage-client.js";
4
-
5
- describe("TypeCoverageClient", () => {
6
- let client: TypeCoverageClient;
7
- let _tmpDir: string;
8
- let cleanup: () => void;
9
-
10
- beforeEach(() => {
11
- client = new TypeCoverageClient();
12
- ({ tmpDir: _tmpDir, cleanup } = setupTestEnvironment(
13
- "pi-lens-typecoverage-test-",
14
- ));
15
- });
16
-
17
- afterEach(() => {
18
- cleanup();
19
- });
20
-
21
- afterEach(() => {
22
- cleanup();
23
- });
24
-
25
- describe("isAvailable", () => {
26
- it("should check type-coverage availability", () => {
27
- const available = client.isAvailable();
28
- expect(typeof available).toBe("boolean");
29
- });
30
- });
31
-
32
- describe("formatResult", () => {
33
- it("should return empty string when not successful", () => {
34
- const result = {
35
- success: false,
36
- percentage: 0,
37
- typed: 0,
38
- total: 0,
39
- untypedLocations: [],
40
- };
41
-
42
- expect(client.formatResult(result)).toBe("");
43
- });
44
-
45
- it("should show coverage percentage", () => {
46
- const result = {
47
- success: true,
48
- percentage: 95,
49
- typed: 95,
50
- total: 100,
51
- untypedLocations: [],
52
- };
53
-
54
- const formatted = client.formatResult(result);
55
- expect(formatted).toContain("95.0%");
56
- expect(formatted).toContain("95/100");
57
- });
58
-
59
- it("should show warning for low coverage", () => {
60
- const result = {
61
- success: true,
62
- percentage: 80,
63
- typed: 80,
64
- total: 100,
65
- untypedLocations: [],
66
- };
67
-
68
- const formatted = client.formatResult(result);
69
- expect(formatted).toContain("⚠");
70
- });
71
-
72
- it("should show checkmark for high coverage", () => {
73
- const result = {
74
- success: true,
75
- percentage: 100,
76
- typed: 100,
77
- total: 100,
78
- untypedLocations: [],
79
- };
80
-
81
- const formatted = client.formatResult(result);
82
- expect(formatted).toContain("✓");
83
- });
84
-
85
- it("should show untyped locations", () => {
86
- const result = {
87
- success: true,
88
- percentage: 90,
89
- typed: 90,
90
- total: 100,
91
- untypedLocations: [
92
- { file: "test.ts", line: 10, column: 5, name: "x" },
93
- { file: "test.ts", line: 20, column: 8, name: "y" },
94
- ],
95
- };
96
-
97
- const formatted = client.formatResult(result);
98
- expect(formatted).toContain("test.ts:10");
99
- expect(formatted).toContain("test.ts:20");
100
- expect(formatted).toContain("x");
101
- expect(formatted).toContain("y");
102
- });
103
-
104
- it("should truncate long untyped location lists", () => {
105
- const locations = Array.from({ length: 20 }, (_, i) => ({
106
- file: `file${i}.ts`,
107
- line: i + 1,
108
- column: 0,
109
- name: `var${i}`,
110
- }));
111
-
112
- const result = {
113
- success: true,
114
- percentage: 80,
115
- typed: 80,
116
- total: 100,
117
- untypedLocations: locations,
118
- };
119
-
120
- const formatted = client.formatResult(result, 10);
121
- expect(formatted).toContain("...");
122
- expect(formatted).toContain("10 more");
123
- });
124
- });
125
- });
@@ -1,138 +0,0 @@
1
- /**
2
- * Type Safety Client for pi-lens
3
- *
4
- * Detects type safety violations that can cause runtime bugs.
5
- * Uses the shared TypeScriptService for efficient type checking.
6
- *
7
- * Checks:
8
- * - Switch Exhaustiveness: Missing cases in union type switches
9
- * - Null Safety: Potential null/undefined dereferences (future)
10
- * - Exhaustive Type Guards: Incomplete instanceof checks (future)
11
- */
12
- import * as fs from "node:fs";
13
- import * as path from "node:path";
14
- import * as ts from "typescript";
15
- import { getTypeScriptService } from "./ts-service.js";
16
- // --- Client ---
17
- export class TypeSafetyClient {
18
- constructor(verbose = false) {
19
- this.log = verbose
20
- ? (msg) => console.error(`[type-safety] ${msg}`)
21
- : () => { };
22
- }
23
- /**
24
- * Check if file is supported (TS/JS)
25
- */
26
- isSupportedFile(filePath) {
27
- const ext = path.extname(filePath).toLowerCase();
28
- return [".ts", ".tsx", ".js", ".jsx"].includes(ext);
29
- }
30
- /**
31
- * Analyze type safety issues for a file
32
- */
33
- analyzeFile(filePath) {
34
- const absolutePath = path.resolve(filePath);
35
- if (!fs.existsSync(absolutePath))
36
- return null;
37
- try {
38
- const content = fs.readFileSync(absolutePath, "utf-8");
39
- const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
40
- const issues = [];
41
- // Check switch exhaustiveness
42
- this.checkSwitchExhaustiveness(sourceFile, issues);
43
- return { filePath: absolutePath, issues };
44
- }
45
- catch (error) {
46
- this.log(`Error analyzing ${filePath}: ${error}`);
47
- return null;
48
- }
49
- }
50
- /**
51
- * Check for switch statements that don't exhaust all union cases
52
- */
53
- checkSwitchExhaustiveness(sourceFile, issues) {
54
- const checker = this.getTypeChecker(sourceFile);
55
- if (!checker)
56
- return;
57
- // Use the source file from the program (has proper type information)
58
- const tsService = getTypeScriptService();
59
- const programSourceFile = tsService.getSourceFileFromProgram(sourceFile.fileName);
60
- if (!programSourceFile)
61
- return;
62
- const visit = (node) => {
63
- if (ts.isSwitchStatement(node)) {
64
- const exprType = checker.getTypeAtLocation(node.expression);
65
- // Only check union types (literal unions and object unions)
66
- if (exprType.isUnion()) {
67
- const unionTypes = exprType.types;
68
- // Get all literal values from the union
69
- const literalValues = unionTypes
70
- .filter((t) => t.isLiteral() || t.flags & ts.TypeFlags.BooleanLiteral)
71
- .map((t) => {
72
- if (t.isLiteral()) {
73
- return String(t.value);
74
- }
75
- // Boolean literals
76
- if (t.flags & ts.TypeFlags.BooleanLiteral) {
77
- return checker.typeToString(t);
78
- }
79
- return null;
80
- })
81
- .filter((v) => v !== null);
82
- // Skip if no literal union (e.g., string | number)
83
- if (literalValues.length === 0)
84
- return;
85
- // Get all case clauses
86
- const coveredCases = new Set();
87
- for (const clause of node.caseBlock.clauses) {
88
- if (ts.isCaseClause(clause)) {
89
- const caseType = checker.getTypeAtLocation(clause.expression);
90
- if (caseType.isLiteral()) {
91
- coveredCases.add(String(caseType.value));
92
- }
93
- else if (caseType.flags & ts.TypeFlags.BooleanLiteral) {
94
- coveredCases.add(checker.typeToString(caseType));
95
- }
96
- }
97
- }
98
- // Check for hasDefault
99
- const hasDefault = node.caseBlock.clauses.some((c) => ts.isDefaultClause(c));
100
- // Find missing cases
101
- const missingCases = literalValues.filter((v) => !coveredCases.has(v));
102
- if (missingCases.length > 0 && !hasDefault) {
103
- const line = programSourceFile.getLineAndCharacterOfPosition(node.getStart())
104
- .line + 1;
105
- const exprText = node.expression.getText(programSourceFile);
106
- const typeStr = missingCases.map((c) => `'${c}'`).join(", ");
107
- issues.push({
108
- filePath: programSourceFile.fileName,
109
- rule: "switch-exhaustiveness",
110
- line,
111
- message: `Switch on '${exprText}' is not exhaustive. Missing cases: ${typeStr}`,
112
- severity: "error",
113
- context: `Type has ${literalValues.length} cases, ${coveredCases.size} covered, ${missingCases.length} missing`,
114
- });
115
- }
116
- }
117
- }
118
- ts.forEachChild(node, visit);
119
- };
120
- ts.forEachChild(programSourceFile, visit);
121
- }
122
- /**
123
- * Get type checker from shared service
124
- */
125
- getTypeChecker(sourceFile) {
126
- const tsService = getTypeScriptService();
127
- // Update the file in the shared service
128
- const content = fs.readFileSync(sourceFile.fileName, "utf-8");
129
- tsService.updateFile(sourceFile.fileName, content);
130
- const checker = tsService.getChecker();
131
- if (!checker) {
132
- this.log("Could not get type checker, skipping exhaustiveness check");
133
- }
134
- return checker;
135
- }
136
- }
137
- // --- Singleton ---
138
- const _instance = null;
package/clients/types.js DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * LSP Types - Core types for the pi-local LSP client
3
- * Simplified from the full LSP spec to what we actually need
4
- */
5
- export var DiagnosticSeverity;
6
- (function (DiagnosticSeverity) {
7
- DiagnosticSeverity[DiagnosticSeverity["Error"] = 1] = "Error";
8
- DiagnosticSeverity[DiagnosticSeverity["Warning"] = 2] = "Warning";
9
- DiagnosticSeverity[DiagnosticSeverity["Information"] = 3] = "Information";
10
- DiagnosticSeverity[DiagnosticSeverity["Hint"] = 4] = "Hint";
11
- })(DiagnosticSeverity || (DiagnosticSeverity = {}));