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.
- package/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- package/commands/refactor.js +0 -130
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for oxlint runner
|
|
3
|
-
*/
|
|
4
|
-
import * as fs from "node:fs";
|
|
5
|
-
import { createRequire } from "node:module";
|
|
6
|
-
import * as path from "node:path";
|
|
7
|
-
import { describe, expect, it } from "vitest";
|
|
8
|
-
/**
|
|
9
|
-
* Delay helper for Windows file cleanup
|
|
10
|
-
* Windows may hold file handles briefly after process exit
|
|
11
|
-
*/
|
|
12
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
-
function createMockContext(filePath, overrides = {}) {
|
|
14
|
-
return {
|
|
15
|
-
filePath,
|
|
16
|
-
cwd: process.cwd(),
|
|
17
|
-
kind: "jsts",
|
|
18
|
-
autofix: false,
|
|
19
|
-
deltaMode: false,
|
|
20
|
-
baselines: { get: () => [], add: () => { }, save: () => { } },
|
|
21
|
-
pi: { getFlag: () => false, ...overrides.pi },
|
|
22
|
-
hasTool: async () => false,
|
|
23
|
-
log: () => { },
|
|
24
|
-
...overrides,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
describe("oxlint runner", () => {
|
|
28
|
-
const require = createRequire(import.meta.url);
|
|
29
|
-
it("should have correct runner definition", async () => {
|
|
30
|
-
const oxlintModule = await import("./oxlint.js");
|
|
31
|
-
const runner = oxlintModule.default;
|
|
32
|
-
expect(runner.id).toBe("oxlint");
|
|
33
|
-
expect(runner.appliesTo).toEqual(["jsts"]);
|
|
34
|
-
expect(runner.priority).toBe(12);
|
|
35
|
-
expect(runner.enabledByDefault).toBe(false); // Opt-in initially
|
|
36
|
-
expect(runner.skipTestFiles).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
it("should detect oxlint availability", () => {
|
|
39
|
-
const { spawnSync } = require("node:child_process");
|
|
40
|
-
const result = spawnSync("oxlint", ["--version"], {
|
|
41
|
-
encoding: "utf-8",
|
|
42
|
-
timeout: 10000,
|
|
43
|
-
shell: true,
|
|
44
|
-
});
|
|
45
|
-
expect(result.error || result.status !== 0 ? "not available" : "available").toBeTruthy(); // May or may not be installed
|
|
46
|
-
});
|
|
47
|
-
it("should detect common lint issues", async () => {
|
|
48
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_test_${Date.now()}.ts`);
|
|
49
|
-
fs.writeFileSync(tmpFile, `// Test file with issues
|
|
50
|
-
function test() {
|
|
51
|
-
// Double negation
|
|
52
|
-
const flag = !!value;
|
|
53
|
-
|
|
54
|
-
// Unused variable
|
|
55
|
-
const unused = 42;
|
|
56
|
-
|
|
57
|
-
// Console statement
|
|
58
|
-
console.log("test");
|
|
59
|
-
}
|
|
60
|
-
`);
|
|
61
|
-
try {
|
|
62
|
-
const oxlintModule = await import("./oxlint.js");
|
|
63
|
-
const runner = oxlintModule.default;
|
|
64
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
65
|
-
// If oxlint is installed, should detect issues
|
|
66
|
-
// If not installed, will be skipped
|
|
67
|
-
if (result.status !== "skipped") {
|
|
68
|
-
// Should detect at least some issues (console, unused vars, etc.)
|
|
69
|
-
expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
|
|
70
|
-
expect(result.diagnostics.some((d) => d.tool === "oxlint" &&
|
|
71
|
-
(d.message.includes("console") ||
|
|
72
|
-
d.message.includes("unused") ||
|
|
73
|
-
d.message.includes("!!")))).toBe(true);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
finally {
|
|
77
|
-
// Windows may hold file handles briefly - add small delay
|
|
78
|
-
await delay(100);
|
|
79
|
-
if (fs.existsSync(tmpFile)) {
|
|
80
|
-
fs.unlinkSync(tmpFile);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
it("should respect no-oxlint flag", async () => {
|
|
85
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_flag_${Date.now()}.ts`);
|
|
86
|
-
fs.writeFileSync(tmpFile, `function test() { console.log("test"); }`);
|
|
87
|
-
try {
|
|
88
|
-
const oxlintModule = await import("./oxlint.js");
|
|
89
|
-
const runner = oxlintModule.default;
|
|
90
|
-
// Create context with no-oxlint flag set to true
|
|
91
|
-
const ctxWithFlag = createMockContext(tmpFile, {
|
|
92
|
-
pi: { getFlag: (name) => name === "no-oxlint" },
|
|
93
|
-
});
|
|
94
|
-
const result = await runner.run(ctxWithFlag);
|
|
95
|
-
expect(result.status).toBe("skipped");
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
// Windows may hold file handles briefly - add small delay
|
|
99
|
-
await delay(100);
|
|
100
|
-
if (fs.existsSync(tmpFile)) {
|
|
101
|
-
fs.unlinkSync(tmpFile);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
it("should provide fix suggestions when available", async () => {
|
|
106
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_fix_${Date.now()}.ts`);
|
|
107
|
-
fs.writeFileSync(tmpFile, `// File with auto-fixable issues
|
|
108
|
-
const x = !!value;
|
|
109
|
-
`);
|
|
110
|
-
try {
|
|
111
|
-
const oxlintModule = await import("./oxlint.js");
|
|
112
|
-
const runner = oxlintModule.default;
|
|
113
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
114
|
-
if (result.status !== "skipped" && result.diagnostics.length > 0) {
|
|
115
|
-
// Some issues should be fixable
|
|
116
|
-
const fixableDiags = result.diagnostics.filter((d) => d.fixable);
|
|
117
|
-
// At least some diagnostics should have fixes
|
|
118
|
-
expect(fixableDiags.length).toBeGreaterThanOrEqual(0);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
finally {
|
|
122
|
-
// Windows may hold file handles briefly - add small delay
|
|
123
|
-
await delay(100);
|
|
124
|
-
if (fs.existsSync(tmpFile)) {
|
|
125
|
-
fs.unlinkSync(tmpFile);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
it("should pass clean TypeScript files", async () => {
|
|
130
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_ok_${Date.now()}.ts`);
|
|
131
|
-
fs.writeFileSync(tmpFile, `// Clean TypeScript file
|
|
132
|
-
function greet(name: string): string {
|
|
133
|
-
return \`Hello, \${name}!\`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const result = greet("world");
|
|
137
|
-
export { greet };
|
|
138
|
-
`);
|
|
139
|
-
try {
|
|
140
|
-
const oxlintModule = await import("./oxlint.js");
|
|
141
|
-
const runner = oxlintModule.default;
|
|
142
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
143
|
-
if (result.status !== "skipped") {
|
|
144
|
-
// Clean files should have no issues
|
|
145
|
-
expect(result.diagnostics.length).toBe(0);
|
|
146
|
-
expect(result.status).toBe("succeeded");
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
finally {
|
|
150
|
-
// Windows may hold file handles briefly - add small delay
|
|
151
|
-
await delay(100);
|
|
152
|
-
if (fs.existsSync(tmpFile)) {
|
|
153
|
-
fs.unlinkSync(tmpFile);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
it("should handle JSON output correctly", async () => {
|
|
158
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_json_${Date.now()}.ts`);
|
|
159
|
-
fs.writeFileSync(tmpFile, `const unused = 1;`);
|
|
160
|
-
try {
|
|
161
|
-
const oxlintModule = await import("./oxlint.js");
|
|
162
|
-
const runner = oxlintModule.default;
|
|
163
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
164
|
-
if (result.status !== "skipped") {
|
|
165
|
-
// All diagnostics should have required fields
|
|
166
|
-
for (const diag of result.diagnostics) {
|
|
167
|
-
expect(diag.id).toBeDefined();
|
|
168
|
-
expect(diag.message).toBeDefined();
|
|
169
|
-
expect(diag.tool).toBe("oxlint");
|
|
170
|
-
expect(diag.line).toBeGreaterThanOrEqual(1);
|
|
171
|
-
expect(diag.severity).toMatch(/^(error|warning|info)$/);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
finally {
|
|
176
|
-
// Windows may hold file handles briefly - add small delay
|
|
177
|
-
await delay(100);
|
|
178
|
-
if (fs.existsSync(tmpFile)) {
|
|
179
|
-
fs.unlinkSync(tmpFile);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
it("should skip when oxlint is not available", async () => {
|
|
184
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_skip_${Date.now()}.ts`);
|
|
185
|
-
fs.writeFileSync(tmpFile, `const x = 1;`);
|
|
186
|
-
try {
|
|
187
|
-
const oxlintModule = await import("./oxlint.js");
|
|
188
|
-
const runner = oxlintModule.default;
|
|
189
|
-
// Check if oxlint is available
|
|
190
|
-
const { spawnSync } = require("node:child_process");
|
|
191
|
-
const checkResult = spawnSync("oxlint", ["--version"], {
|
|
192
|
-
encoding: "utf-8",
|
|
193
|
-
timeout: 5000,
|
|
194
|
-
shell: true,
|
|
195
|
-
});
|
|
196
|
-
const isAvailable = !checkResult.error && checkResult.status === 0;
|
|
197
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
198
|
-
if (!isAvailable) {
|
|
199
|
-
expect(result.status).toBe("skipped");
|
|
200
|
-
expect(result.diagnostics).toHaveLength(0);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
finally {
|
|
204
|
-
// Windows may hold file handles briefly - add small delay
|
|
205
|
-
await delay(100);
|
|
206
|
-
if (fs.existsSync(tmpFile)) {
|
|
207
|
-
fs.unlinkSync(tmpFile);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
it("should handle parsing errors gracefully", async () => {
|
|
212
|
-
const tmpFile = path.join(process.env.TEMP || "/tmp", `oxlint_parse_${Date.now()}.ts`);
|
|
213
|
-
// Intentionally malformed file
|
|
214
|
-
fs.writeFileSync(tmpFile, `const x = `);
|
|
215
|
-
try {
|
|
216
|
-
const oxlintModule = await import("./oxlint.js");
|
|
217
|
-
const runner = oxlintModule.default;
|
|
218
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
219
|
-
// Should handle parse errors without crashing
|
|
220
|
-
expect(["succeeded", "failed", "skipped"]).toContain(result.status);
|
|
221
|
-
}
|
|
222
|
-
finally {
|
|
223
|
-
// Windows may hold file handles briefly - add small delay
|
|
224
|
-
await delay(100);
|
|
225
|
-
if (fs.existsSync(tmpFile)) {
|
|
226
|
-
fs.unlinkSync(tmpFile);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
});
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for oxlint runner
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import * as fs from "node:fs";
|
|
6
|
-
import { createRequire } from "node:module";
|
|
7
|
-
import * as path from "node:path";
|
|
8
|
-
import { describe, expect, it } from "vitest";
|
|
9
|
-
import type { DispatchContext } from "../types.js";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Delay helper for Windows file cleanup
|
|
13
|
-
* Windows may hold file handles briefly after process exit
|
|
14
|
-
*/
|
|
15
|
-
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
16
|
-
|
|
17
|
-
function createMockContext(
|
|
18
|
-
filePath: string,
|
|
19
|
-
overrides: Partial<DispatchContext> = {},
|
|
20
|
-
): DispatchContext {
|
|
21
|
-
return {
|
|
22
|
-
filePath,
|
|
23
|
-
cwd: process.cwd(),
|
|
24
|
-
kind: "jsts" as any,
|
|
25
|
-
autofix: false,
|
|
26
|
-
deltaMode: false,
|
|
27
|
-
baselines: { get: () => [], add: () => {}, save: () => {} } as any,
|
|
28
|
-
pi: { getFlag: () => false, ...overrides.pi },
|
|
29
|
-
hasTool: async () => false,
|
|
30
|
-
log: () => {},
|
|
31
|
-
...overrides,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe("oxlint runner", () => {
|
|
36
|
-
const require = createRequire(import.meta.url);
|
|
37
|
-
|
|
38
|
-
it("should have correct runner definition", async () => {
|
|
39
|
-
const oxlintModule = await import("./oxlint.js");
|
|
40
|
-
const runner = oxlintModule.default;
|
|
41
|
-
|
|
42
|
-
expect(runner.id).toBe("oxlint");
|
|
43
|
-
expect(runner.appliesTo).toEqual(["jsts"]);
|
|
44
|
-
expect(runner.priority).toBe(12);
|
|
45
|
-
expect(runner.enabledByDefault).toBe(false); // Opt-in initially
|
|
46
|
-
expect(runner.skipTestFiles).toBe(true);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should detect oxlint availability", () => {
|
|
50
|
-
const { spawnSync } =
|
|
51
|
-
require("node:child_process") as typeof import("node:child_process");
|
|
52
|
-
const result = spawnSync("oxlint", ["--version"], {
|
|
53
|
-
encoding: "utf-8",
|
|
54
|
-
timeout: 10000,
|
|
55
|
-
shell: true,
|
|
56
|
-
});
|
|
57
|
-
expect(
|
|
58
|
-
result.error || result.status !== 0 ? "not available" : "available",
|
|
59
|
-
).toBeTruthy(); // May or may not be installed
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("should detect common lint issues", async () => {
|
|
63
|
-
const tmpFile = path.join(
|
|
64
|
-
process.env.TEMP || "/tmp",
|
|
65
|
-
`oxlint_test_${Date.now()}.ts`,
|
|
66
|
-
);
|
|
67
|
-
fs.writeFileSync(
|
|
68
|
-
tmpFile,
|
|
69
|
-
`// Test file with issues
|
|
70
|
-
function test() {
|
|
71
|
-
// Double negation
|
|
72
|
-
const flag = !!value;
|
|
73
|
-
|
|
74
|
-
// Unused variable
|
|
75
|
-
const unused = 42;
|
|
76
|
-
|
|
77
|
-
// Console statement
|
|
78
|
-
console.log("test");
|
|
79
|
-
}
|
|
80
|
-
`,
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
const oxlintModule = await import("./oxlint.js");
|
|
85
|
-
const runner = oxlintModule.default;
|
|
86
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
87
|
-
|
|
88
|
-
// If oxlint is installed, should detect issues
|
|
89
|
-
// If not installed, will be skipped
|
|
90
|
-
if (result.status !== "skipped") {
|
|
91
|
-
// Should detect at least some issues (console, unused vars, etc.)
|
|
92
|
-
expect(result.diagnostics.length).toBeGreaterThanOrEqual(1);
|
|
93
|
-
expect(
|
|
94
|
-
result.diagnostics.some(
|
|
95
|
-
(d) =>
|
|
96
|
-
d.tool === "oxlint" &&
|
|
97
|
-
(d.message.includes("console") ||
|
|
98
|
-
d.message.includes("unused") ||
|
|
99
|
-
d.message.includes("!!")),
|
|
100
|
-
),
|
|
101
|
-
).toBe(true);
|
|
102
|
-
}
|
|
103
|
-
} finally {
|
|
104
|
-
// Windows may hold file handles briefly - add small delay
|
|
105
|
-
await delay(100);
|
|
106
|
-
if (fs.existsSync(tmpFile)) {
|
|
107
|
-
fs.unlinkSync(tmpFile);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("should respect no-oxlint flag", async () => {
|
|
113
|
-
const tmpFile = path.join(
|
|
114
|
-
process.env.TEMP || "/tmp",
|
|
115
|
-
`oxlint_flag_${Date.now()}.ts`,
|
|
116
|
-
);
|
|
117
|
-
fs.writeFileSync(
|
|
118
|
-
tmpFile,
|
|
119
|
-
`function test() { console.log("test"); }`,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const oxlintModule = await import("./oxlint.js");
|
|
124
|
-
const runner = oxlintModule.default;
|
|
125
|
-
|
|
126
|
-
// Create context with no-oxlint flag set to true
|
|
127
|
-
const ctxWithFlag = createMockContext(tmpFile, {
|
|
128
|
-
pi: { getFlag: (name: string) => name === "no-oxlint" },
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const result = await runner.run(ctxWithFlag);
|
|
132
|
-
expect(result.status).toBe("skipped");
|
|
133
|
-
} finally {
|
|
134
|
-
// Windows may hold file handles briefly - add small delay
|
|
135
|
-
await delay(100);
|
|
136
|
-
if (fs.existsSync(tmpFile)) {
|
|
137
|
-
fs.unlinkSync(tmpFile);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should provide fix suggestions when available", async () => {
|
|
143
|
-
const tmpFile = path.join(
|
|
144
|
-
process.env.TEMP || "/tmp",
|
|
145
|
-
`oxlint_fix_${Date.now()}.ts`,
|
|
146
|
-
);
|
|
147
|
-
fs.writeFileSync(
|
|
148
|
-
tmpFile,
|
|
149
|
-
`// File with auto-fixable issues
|
|
150
|
-
const x = !!value;
|
|
151
|
-
`,
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
try {
|
|
155
|
-
const oxlintModule = await import("./oxlint.js");
|
|
156
|
-
const runner = oxlintModule.default;
|
|
157
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
158
|
-
|
|
159
|
-
if (result.status !== "skipped" && result.diagnostics.length > 0) {
|
|
160
|
-
// Some issues should be fixable
|
|
161
|
-
const fixableDiags = result.diagnostics.filter((d) => d.fixable);
|
|
162
|
-
// At least some diagnostics should have fixes
|
|
163
|
-
expect(fixableDiags.length).toBeGreaterThanOrEqual(0);
|
|
164
|
-
}
|
|
165
|
-
} finally {
|
|
166
|
-
// Windows may hold file handles briefly - add small delay
|
|
167
|
-
await delay(100);
|
|
168
|
-
if (fs.existsSync(tmpFile)) {
|
|
169
|
-
fs.unlinkSync(tmpFile);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("should pass clean TypeScript files", async () => {
|
|
175
|
-
const tmpFile = path.join(
|
|
176
|
-
process.env.TEMP || "/tmp",
|
|
177
|
-
`oxlint_ok_${Date.now()}.ts`,
|
|
178
|
-
);
|
|
179
|
-
fs.writeFileSync(
|
|
180
|
-
tmpFile,
|
|
181
|
-
`// Clean TypeScript file
|
|
182
|
-
function greet(name: string): string {
|
|
183
|
-
return \`Hello, \${name}!\`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const result = greet("world");
|
|
187
|
-
export { greet };
|
|
188
|
-
`,
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
const oxlintModule = await import("./oxlint.js");
|
|
193
|
-
const runner = oxlintModule.default;
|
|
194
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
195
|
-
|
|
196
|
-
if (result.status !== "skipped") {
|
|
197
|
-
// Clean files should have no issues
|
|
198
|
-
expect(result.diagnostics.length).toBe(0);
|
|
199
|
-
expect(result.status).toBe("succeeded");
|
|
200
|
-
}
|
|
201
|
-
} finally {
|
|
202
|
-
// Windows may hold file handles briefly - add small delay
|
|
203
|
-
await delay(100);
|
|
204
|
-
if (fs.existsSync(tmpFile)) {
|
|
205
|
-
fs.unlinkSync(tmpFile);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it("should handle JSON output correctly", async () => {
|
|
211
|
-
const tmpFile = path.join(
|
|
212
|
-
process.env.TEMP || "/tmp",
|
|
213
|
-
`oxlint_json_${Date.now()}.ts`,
|
|
214
|
-
);
|
|
215
|
-
fs.writeFileSync(
|
|
216
|
-
tmpFile,
|
|
217
|
-
`const unused = 1;`,
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
const oxlintModule = await import("./oxlint.js");
|
|
222
|
-
const runner = oxlintModule.default;
|
|
223
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
224
|
-
|
|
225
|
-
if (result.status !== "skipped") {
|
|
226
|
-
// All diagnostics should have required fields
|
|
227
|
-
for (const diag of result.diagnostics) {
|
|
228
|
-
expect(diag.id).toBeDefined();
|
|
229
|
-
expect(diag.message).toBeDefined();
|
|
230
|
-
expect(diag.tool).toBe("oxlint");
|
|
231
|
-
expect(diag.line).toBeGreaterThanOrEqual(1);
|
|
232
|
-
expect(diag.severity).toMatch(/^(error|warning|info)$/);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} finally {
|
|
236
|
-
// Windows may hold file handles briefly - add small delay
|
|
237
|
-
await delay(100);
|
|
238
|
-
if (fs.existsSync(tmpFile)) {
|
|
239
|
-
fs.unlinkSync(tmpFile);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("should skip when oxlint is not available", async () => {
|
|
245
|
-
const tmpFile = path.join(
|
|
246
|
-
process.env.TEMP || "/tmp",
|
|
247
|
-
`oxlint_skip_${Date.now()}.ts`,
|
|
248
|
-
);
|
|
249
|
-
fs.writeFileSync(tmpFile, `const x = 1;`);
|
|
250
|
-
|
|
251
|
-
try {
|
|
252
|
-
const oxlintModule = await import("./oxlint.js");
|
|
253
|
-
const runner = oxlintModule.default;
|
|
254
|
-
|
|
255
|
-
// Check if oxlint is available
|
|
256
|
-
const { spawnSync } =
|
|
257
|
-
require("node:child_process") as typeof import("node:child_process");
|
|
258
|
-
const checkResult = spawnSync("oxlint", ["--version"], {
|
|
259
|
-
encoding: "utf-8",
|
|
260
|
-
timeout: 5000,
|
|
261
|
-
shell: true,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const isAvailable = !checkResult.error && checkResult.status === 0;
|
|
265
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
266
|
-
|
|
267
|
-
if (!isAvailable) {
|
|
268
|
-
expect(result.status).toBe("skipped");
|
|
269
|
-
expect(result.diagnostics).toHaveLength(0);
|
|
270
|
-
}
|
|
271
|
-
} finally {
|
|
272
|
-
// Windows may hold file handles briefly - add small delay
|
|
273
|
-
await delay(100);
|
|
274
|
-
if (fs.existsSync(tmpFile)) {
|
|
275
|
-
fs.unlinkSync(tmpFile);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
it("should handle parsing errors gracefully", async () => {
|
|
281
|
-
const tmpFile = path.join(
|
|
282
|
-
process.env.TEMP || "/tmp",
|
|
283
|
-
`oxlint_parse_${Date.now()}.ts`,
|
|
284
|
-
);
|
|
285
|
-
// Intentionally malformed file
|
|
286
|
-
fs.writeFileSync(tmpFile, `const x = `);
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
const oxlintModule = await import("./oxlint.js");
|
|
290
|
-
const runner = oxlintModule.default;
|
|
291
|
-
const result = await runner.run(createMockContext(tmpFile));
|
|
292
|
-
|
|
293
|
-
// Should handle parse errors without crashing
|
|
294
|
-
expect(["succeeded", "failed", "skipped"]).toContain(result.status);
|
|
295
|
-
} finally {
|
|
296
|
-
// Windows may hold file handles briefly - add small delay
|
|
297
|
-
await delay(100);
|
|
298
|
-
if (fs.existsSync(tmpFile)) {
|
|
299
|
-
fs.unlinkSync(tmpFile);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
});
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pyright runner for dispatch system
|
|
3
|
-
*
|
|
4
|
-
* Provides real Python type-checking (not just linting).
|
|
5
|
-
* Catches type errors like: result: str = add(1, 2) # Type "int" not assignable to "str"
|
|
6
|
-
*
|
|
7
|
-
* Requires: pyright (pip install pyright or npm install -g pyright)
|
|
8
|
-
*/
|
|
9
|
-
import { ensureTool } from "../../installer/index.js";
|
|
10
|
-
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
11
|
-
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
12
|
-
const pyright = createAvailabilityChecker("pyright", ".exe");
|
|
13
|
-
const pyrightRunner = {
|
|
14
|
-
id: "pyright",
|
|
15
|
-
appliesTo: ["python"],
|
|
16
|
-
priority: 5, // Higher priority than ruff (10) - type errors are more important
|
|
17
|
-
enabledByDefault: true,
|
|
18
|
-
async run(ctx) {
|
|
19
|
-
const cwd = ctx.cwd || process.cwd();
|
|
20
|
-
// Get pyright command - try multiple strategies
|
|
21
|
-
let cmd = null;
|
|
22
|
-
// Strategy 1: Check cached availability (fast path)
|
|
23
|
-
if (pyright.isAvailable(cwd)) {
|
|
24
|
-
cmd = pyright.getCommand();
|
|
25
|
-
}
|
|
26
|
-
// Strategy 2: Try to find pyright via ensureTool (installs if needed)
|
|
27
|
-
if (!cmd) {
|
|
28
|
-
const installedPath = await ensureTool("pyright");
|
|
29
|
-
if (installedPath)
|
|
30
|
-
cmd = installedPath;
|
|
31
|
-
}
|
|
32
|
-
// Strategy 3: Direct PATH check (handles module cache staleness)
|
|
33
|
-
if (!cmd) {
|
|
34
|
-
const { findCommandAsync } = await import("../../safe-spawn.js");
|
|
35
|
-
const foundCmd = await findCommandAsync("pyright");
|
|
36
|
-
if (foundCmd)
|
|
37
|
-
cmd = foundCmd;
|
|
38
|
-
}
|
|
39
|
-
// If still no pyright, skip this runner
|
|
40
|
-
if (!cmd) {
|
|
41
|
-
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
42
|
-
}
|
|
43
|
-
// Run pyright with JSON output
|
|
44
|
-
const result = await safeSpawnAsync(cmd, ["--outputjson", ctx.filePath], {
|
|
45
|
-
timeout: 60000,
|
|
46
|
-
});
|
|
47
|
-
// Pyright returns non-zero when errors found, that's OK
|
|
48
|
-
if (result.error) {
|
|
49
|
-
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
50
|
-
}
|
|
51
|
-
const output = (result.stdout || "").trim();
|
|
52
|
-
if (!output) {
|
|
53
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
const data = JSON.parse(output);
|
|
57
|
-
const diagnostics = parsePyrightOutput(data, ctx.filePath);
|
|
58
|
-
if (diagnostics.length === 0) {
|
|
59
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
60
|
-
}
|
|
61
|
-
const hasErrors = diagnostics.some((d) => d.severity === "error");
|
|
62
|
-
return {
|
|
63
|
-
status: hasErrors ? "failed" : "succeeded",
|
|
64
|
-
diagnostics,
|
|
65
|
-
semantic: hasErrors ? "blocking" : "warning",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// JSON parse error
|
|
70
|
-
return {
|
|
71
|
-
status: "failed",
|
|
72
|
-
diagnostics: [],
|
|
73
|
-
semantic: "none",
|
|
74
|
-
rawOutput: output.slice(0, 500),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
function parsePyrightOutput(data, _filePath) {
|
|
80
|
-
const diagnostics = [];
|
|
81
|
-
// Pyright JSON output has generalDiagnostics array
|
|
82
|
-
const generalDiags = data.generalDiagnostics || [];
|
|
83
|
-
for (const diag of generalDiags) {
|
|
84
|
-
// Skip if not for this file (pyright may output diagnostics for imports)
|
|
85
|
-
// For now, include all - caller will filter if needed
|
|
86
|
-
diagnostics.push({
|
|
87
|
-
id: `pyright-${diag.rule || diag.start?.line || "unknown"}`,
|
|
88
|
-
message: diag.message || "Type error",
|
|
89
|
-
filePath: diag.file || _filePath,
|
|
90
|
-
line: diag.start?.line || 0,
|
|
91
|
-
column: diag.start?.column || 0,
|
|
92
|
-
severity: diag.severity === "error" ? "error" : "warning",
|
|
93
|
-
semantic: diag.severity === "error" ? "blocking" : "warning",
|
|
94
|
-
tool: "pyright",
|
|
95
|
-
rule: diag.rule,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
return diagnostics;
|
|
99
|
-
}
|
|
100
|
-
export default pyrightRunner;
|