pi-lens 2.2.9 → 3.0.0
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 +198 -0
- package/README.md +709 -519
- package/clients/__tests__/file-time.test.js +216 -0
- package/clients/__tests__/file-time.test.ts +276 -0
- package/clients/__tests__/format-service.test.js +245 -0
- package/clients/__tests__/format-service.test.ts +339 -0
- package/clients/__tests__/formatters.test.js +271 -0
- package/clients/__tests__/formatters.test.ts +401 -0
- package/clients/amain-types.js +164 -0
- package/clients/amain-types.ts +165 -0
- package/clients/architect-client.js +56 -12
- package/clients/architect-client.ts +81 -16
- package/clients/ast-grep-client.js +2 -2
- package/clients/ast-grep-client.ts +14 -39
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.js +8 -0
- package/clients/ast-grep-rule-manager.ts +10 -1
- package/clients/ast-grep-types.js +9 -0
- package/clients/ast-grep-types.ts +106 -0
- package/clients/auto-loop.js +10 -0
- package/clients/auto-loop.ts +14 -1
- package/clients/biome-client.js +81 -19
- package/clients/biome-client.ts +103 -22
- package/clients/bus/bus.js +191 -0
- package/clients/bus/bus.ts +251 -0
- package/clients/bus/events.js +214 -0
- package/clients/bus/events.ts +279 -0
- package/clients/bus/index.js +8 -0
- package/clients/bus/index.ts +9 -0
- package/clients/bus/integration.js +158 -0
- package/clients/bus/integration.ts +214 -0
- package/clients/complexity-client.js +13 -7
- package/clients/complexity-client.ts +13 -7
- package/clients/config-validator.js +465 -0
- package/clients/config-validator.ts +558 -0
- package/clients/dependency-checker.js +4 -10
- package/clients/dependency-checker.ts +4 -10
- package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
- package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
- package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
- package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
- package/clients/dispatch/bus-dispatcher.js +177 -0
- package/clients/dispatch/bus-dispatcher.ts +251 -0
- package/clients/dispatch/dispatcher.edge.test.js +82 -0
- package/clients/dispatch/dispatcher.edge.test.ts +100 -0
- package/clients/dispatch/dispatcher.format.test.js +46 -0
- package/clients/dispatch/dispatcher.format.test.ts +58 -0
- package/clients/dispatch/dispatcher.inline.test.js +74 -0
- package/clients/dispatch/dispatcher.inline.test.ts +93 -0
- package/clients/dispatch/dispatcher.js +19 -53
- package/clients/dispatch/dispatcher.ts +20 -67
- package/clients/dispatch/plan.js +9 -4
- package/clients/dispatch/plan.ts +9 -4
- package/clients/dispatch/runners/architect.js +21 -7
- package/clients/dispatch/runners/architect.test.js +138 -0
- package/clients/dispatch/runners/architect.test.ts +162 -0
- package/clients/dispatch/runners/architect.ts +22 -7
- package/clients/dispatch/runners/ast-grep-napi.js +462 -0
- package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
- package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
- package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
- package/clients/dispatch/runners/ast-grep.js +62 -19
- package/clients/dispatch/runners/ast-grep.ts +70 -18
- package/clients/dispatch/runners/biome.js +29 -53
- package/clients/dispatch/runners/biome.ts +29 -63
- package/clients/dispatch/runners/config-validation.js +67 -0
- package/clients/dispatch/runners/config-validation.ts +82 -0
- package/clients/dispatch/runners/go-vet.js +4 -28
- package/clients/dispatch/runners/go-vet.ts +4 -32
- package/clients/dispatch/runners/index.js +30 -10
- package/clients/dispatch/runners/index.ts +30 -10
- package/clients/dispatch/runners/oxlint.js +141 -0
- package/clients/dispatch/runners/oxlint.test.js +230 -0
- package/clients/dispatch/runners/oxlint.test.ts +303 -0
- package/clients/dispatch/runners/oxlint.ts +175 -0
- package/clients/dispatch/runners/pyright.js +40 -70
- package/clients/dispatch/runners/pyright.test.js +16 -2
- package/clients/dispatch/runners/pyright.test.ts +14 -2
- package/clients/dispatch/runners/pyright.ts +48 -91
- package/clients/dispatch/runners/python-slop.js +97 -0
- package/clients/dispatch/runners/python-slop.test.js +203 -0
- package/clients/dispatch/runners/python-slop.test.ts +298 -0
- package/clients/dispatch/runners/python-slop.ts +124 -0
- package/clients/dispatch/runners/ruff.js +18 -71
- package/clients/dispatch/runners/ruff.ts +19 -79
- package/clients/dispatch/runners/rust-clippy.js +28 -32
- package/clients/dispatch/runners/rust-clippy.ts +29 -31
- package/clients/dispatch/runners/scan_codebase.test.js +89 -0
- package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
- package/clients/dispatch/runners/shellcheck.js +147 -0
- package/clients/dispatch/runners/shellcheck.test.js +98 -0
- package/clients/dispatch/runners/shellcheck.test.ts +129 -0
- package/clients/dispatch/runners/shellcheck.ts +188 -0
- package/clients/dispatch/runners/similarity.js +230 -0
- package/clients/dispatch/runners/similarity.ts +339 -0
- package/clients/dispatch/runners/spellcheck.js +106 -0
- package/clients/dispatch/runners/spellcheck.test.js +158 -0
- package/clients/dispatch/runners/spellcheck.test.ts +214 -0
- package/clients/dispatch/runners/spellcheck.ts +136 -0
- package/clients/dispatch/runners/tree-sitter.js +107 -0
- package/clients/dispatch/runners/tree-sitter.ts +135 -0
- package/clients/dispatch/runners/ts-lsp.js +104 -33
- package/clients/dispatch/runners/ts-lsp.ts +120 -38
- package/clients/dispatch/runners/ts-slop.js +113 -0
- package/clients/dispatch/runners/ts-slop.test.js +180 -0
- package/clients/dispatch/runners/ts-slop.test.ts +230 -0
- package/clients/dispatch/runners/ts-slop.ts +142 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
- package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
- package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
- package/clients/dispatch/runners/utils.js +2 -4
- package/clients/dispatch/runners/utils.ts +2 -4
- package/clients/dispatch/types.ts +1 -1
- package/clients/dispatch/utils/format-utils.js +49 -0
- package/clients/dispatch/utils/format-utils.ts +60 -0
- package/clients/dogfood.test.js +201 -0
- package/clients/dogfood.test.ts +269 -0
- package/clients/file-time.js +152 -0
- package/clients/file-time.ts +208 -0
- package/clients/file-utils.js +40 -0
- package/clients/file-utils.ts +44 -0
- package/clients/fix-scanners.js +10 -20
- package/clients/fix-scanners.ts +10 -22
- package/clients/format-service.js +172 -0
- package/clients/format-service.ts +254 -0
- package/clients/formatters.js +435 -0
- package/clients/formatters.ts +508 -0
- package/clients/go-client.js +5 -14
- package/clients/go-client.ts +5 -13
- package/clients/installer/index.js +356 -0
- package/clients/installer/index.ts +426 -0
- package/clients/jscpd-client.js +11 -9
- package/clients/jscpd-client.ts +12 -8
- package/clients/knip-client.js +3 -7
- package/clients/knip-client.ts +3 -6
- package/clients/lsp/__tests__/client.test.js +325 -0
- package/clients/lsp/__tests__/client.test.ts +434 -0
- package/clients/lsp/__tests__/config.test.js +166 -0
- package/clients/lsp/__tests__/config.test.ts +209 -0
- package/clients/lsp/__tests__/error-recovery.test.js +213 -0
- package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
- package/clients/lsp/__tests__/integration.test.js +127 -0
- package/clients/lsp/__tests__/integration.test.ts +160 -0
- package/clients/lsp/__tests__/launch.test.js +260 -0
- package/clients/lsp/__tests__/launch.test.ts +329 -0
- package/clients/lsp/__tests__/server.test.js +259 -0
- package/clients/lsp/__tests__/server.test.ts +332 -0
- package/clients/lsp/__tests__/service.test.js +417 -0
- package/clients/lsp/__tests__/service.test.ts +499 -0
- package/clients/lsp/client.js +235 -0
- package/clients/lsp/client.ts +328 -0
- package/clients/lsp/config.js +115 -0
- package/clients/lsp/config.ts +149 -0
- package/clients/lsp/index.js +222 -0
- package/clients/lsp/index.ts +280 -0
- package/clients/lsp/installer/index.js +391 -0
- package/clients/lsp/interactive-install.js +210 -0
- package/clients/lsp/interactive-install.ts +251 -0
- package/clients/lsp/language.js +170 -0
- package/clients/lsp/language.ts +216 -0
- package/clients/lsp/launch.js +174 -0
- package/clients/lsp/launch.ts +240 -0
- package/clients/lsp/lsp/launch.js +116 -0
- package/clients/lsp/lsp/server.js +532 -0
- package/clients/lsp/lsp-index.js +10 -0
- package/clients/lsp/lsp-index.ts +11 -0
- package/clients/lsp/path-utils.js +48 -0
- package/clients/lsp/path-utils.ts +52 -0
- package/clients/lsp/server.js +615 -0
- package/clients/lsp/server.ts +800 -0
- package/clients/lsp/test-py-spawn/requirements.txt +1 -0
- package/clients/lsp/test-py-spawn/test.py +3 -0
- package/clients/lsp/test-py-svc/requirements.txt +1 -0
- package/clients/lsp/test-py-svc/test.py +3 -0
- package/clients/lsp/test-python-project/requirements.txt +1 -0
- package/clients/lsp/test-python-project/test.py +5 -0
- package/clients/metrics-history.js +2 -2
- package/clients/metrics-history.ts +2 -2
- package/clients/production-readiness.js +522 -0
- package/clients/production-readiness.ts +556 -0
- package/clients/project-index.js +255 -0
- package/clients/project-index.ts +383 -0
- package/clients/project-metadata.js +531 -0
- package/clients/project-metadata.ts +624 -0
- package/clients/ruff-client.js +56 -16
- package/clients/ruff-client.ts +72 -15
- package/clients/runner-tracker.js +152 -0
- package/clients/runner-tracker.ts +213 -0
- package/clients/rust-client.js +4 -11
- package/clients/rust-client.ts +5 -11
- package/clients/safe-spawn.js +96 -0
- package/clients/safe-spawn.ts +128 -0
- package/clients/scan-architectural-debt.js +3 -6
- package/clients/scan-architectural-debt.ts +3 -6
- package/clients/scan-utils.js +5 -20
- package/clients/scan-utils.ts +5 -29
- package/clients/secrets-scanner.js +3 -17
- package/clients/secrets-scanner.ts +4 -20
- package/clients/services/__tests__/effect-integration.test.js +86 -0
- package/clients/services/__tests__/effect-integration.test.ts +111 -0
- package/clients/services/effect-integration.js +194 -0
- package/clients/services/effect-integration.ts +268 -0
- package/clients/services/index.js +7 -0
- package/clients/services/index.ts +8 -0
- package/clients/services/runner-service.js +105 -0
- package/clients/services/runner-service.ts +179 -0
- package/clients/sg-runner.js +87 -13
- package/clients/sg-runner.ts +97 -13
- package/clients/state-matrix.js +160 -0
- package/clients/state-matrix.ts +202 -0
- package/clients/subprocess-client.js +10 -9
- package/clients/subprocess-client.ts +10 -8
- package/clients/test-runner-client.js +3 -7
- package/clients/test-runner-client.ts +3 -6
- package/clients/tool-availability.js +4 -10
- package/clients/tool-availability.ts +4 -9
- package/clients/tree-sitter-client.js +564 -0
- package/clients/tree-sitter-client.ts +797 -0
- package/clients/tree-sitter-query-loader.js +355 -0
- package/clients/tree-sitter-query-loader.ts +425 -0
- package/clients/type-coverage-client.js +3 -7
- package/clients/type-coverage-client.ts +3 -6
- package/clients/typescript-client.codefix.test.js +157 -0
- package/clients/typescript-client.codefix.test.ts +186 -0
- package/clients/typescript-client.js +43 -0
- package/clients/typescript-client.ts +98 -0
- package/commands/booboo.js +799 -219
- package/commands/booboo.ts +1004 -225
- package/commands/clients/ast-grep-client.js +250 -0
- package/commands/clients/ast-grep-parser.js +86 -0
- package/commands/clients/ast-grep-rule-manager.js +91 -0
- package/commands/clients/ast-grep-types.js +9 -0
- package/commands/clients/biome-client.js +380 -0
- package/commands/clients/complexity-client.js +667 -0
- package/commands/clients/file-kinds.js +177 -0
- package/commands/clients/file-utils.js +40 -0
- package/commands/clients/jscpd-client.js +169 -0
- package/commands/clients/knip-client.js +211 -0
- package/commands/clients/ruff-client.js +297 -0
- package/commands/clients/safe-spawn.js +88 -0
- package/commands/clients/scan-utils.js +83 -0
- package/commands/clients/sg-runner.js +190 -0
- package/commands/clients/types.js +11 -0
- package/commands/clients/typescript-client.js +505 -0
- package/commands/fix-from-booboo.js +398 -0
- package/commands/fix-from-booboo.ts +485 -0
- package/commands/fix-simplified.js +618 -0
- package/commands/fix-simplified.ts +768 -0
- package/commands/rate.js +10 -14
- package/commands/rate.ts +9 -16
- package/default-architect.yaml +59 -15
- package/index.ts +342 -429
- package/package.json +16 -3
- package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
- package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
- package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
- package/rules/python-slop-rules/.sgconfig.yml +4 -0
- package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
- package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
- package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
- package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
- package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
- package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
- package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
- package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
- package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
- package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
- package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
- package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
- package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
- package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
- package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
- package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
- package/rules/ts-slop-rules/.sgconfig.yml +4 -0
- package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
- package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
- package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
- package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
- package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
- package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
- package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
- package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
- package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
- package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
- package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
- package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
- package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
- package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
- package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
- package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
- package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
- package/skills/ast-grep/SKILL.md +182 -0
- package/clients/dispatch/runners/secrets.js +0 -109
- package/commands/fix.js +0 -244
- package/commands/fix.ts +0 -373
- package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
|
@@ -0,0 +1,303 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oxlint runner for dispatch system
|
|
3
|
+
*
|
|
4
|
+
* Fast Rust-based JavaScript/TypeScript linter from the Oxc project.
|
|
5
|
+
* Zero-config by default, compatible with ESLint rules.
|
|
6
|
+
*
|
|
7
|
+
* Why oxlint?
|
|
8
|
+
* - ~100x faster than ESLint (Rust-based)
|
|
9
|
+
* - Zero-config (works out of the box)
|
|
10
|
+
* - Growing rule set (eslint, typescript, react, unicorn, etc.)
|
|
11
|
+
* - JSON output for programmatic use
|
|
12
|
+
*
|
|
13
|
+
* Comparison:
|
|
14
|
+
* - vs Biome: Similar performance, different rule philosophy
|
|
15
|
+
* - vs ESLint: Much faster, fewer rules but catching up
|
|
16
|
+
*
|
|
17
|
+
* Install: npm install -D oxlint
|
|
18
|
+
* Or: cargo install oxlint
|
|
19
|
+
*
|
|
20
|
+
* Config: .oxlintrc.json (optional, zero-config works)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { safeSpawn } from "../../safe-spawn.js";
|
|
24
|
+
import { createAvailabilityChecker, createConfigFinder } from "./utils/runner-helpers.js";
|
|
25
|
+
import type {
|
|
26
|
+
Diagnostic,
|
|
27
|
+
DispatchContext,
|
|
28
|
+
RunnerDefinition,
|
|
29
|
+
RunnerResult,
|
|
30
|
+
} from "../types.js";
|
|
31
|
+
|
|
32
|
+
const oxlint = createAvailabilityChecker("oxlint", ".exe");
|
|
33
|
+
const findOxlintConfig = createConfigFinder(".oxlintrc.json");
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Parse oxlint JSON output
|
|
37
|
+
*
|
|
38
|
+
* Format: Array of diagnostic objects
|
|
39
|
+
* [{
|
|
40
|
+
* "ruleId": "no-unused-vars",
|
|
41
|
+
* "severity": 2,
|
|
42
|
+
* "message": "'foo' is assigned a value but never used.",
|
|
43
|
+
* "line": 10,
|
|
44
|
+
* "column": 7,
|
|
45
|
+
* "nodeType": "Identifier",
|
|
46
|
+
* "messageId": "unusedVar",
|
|
47
|
+
* "endLine": 10,
|
|
48
|
+
* "endColumn": 10,
|
|
49
|
+
* "fix": { "range": [95, 108], "text": "" }
|
|
50
|
+
* }]
|
|
51
|
+
*/
|
|
52
|
+
function parseOxlintOutput(raw: string, filePath: string): Diagnostic[] {
|
|
53
|
+
const diagnostics: Diagnostic[] = [];
|
|
54
|
+
|
|
55
|
+
if (!raw.trim()) {
|
|
56
|
+
return diagnostics;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(raw) as Array<{
|
|
61
|
+
ruleId?: string;
|
|
62
|
+
severity?: number; // 1 = warning, 2 = error
|
|
63
|
+
message?: string;
|
|
64
|
+
line?: number;
|
|
65
|
+
column?: number;
|
|
66
|
+
messageId?: string;
|
|
67
|
+
fix?: { range: number[]; text: string };
|
|
68
|
+
}>;
|
|
69
|
+
|
|
70
|
+
if (!Array.isArray(parsed)) {
|
|
71
|
+
return diagnostics;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const item of parsed) {
|
|
75
|
+
if (!item.message || !item.line) continue;
|
|
76
|
+
|
|
77
|
+
const severity = item.severity === 2 ? "error" : "warning";
|
|
78
|
+
|
|
79
|
+
diagnostics.push({
|
|
80
|
+
id: `oxlint-${item.line}-${item.ruleId || "unknown"}`,
|
|
81
|
+
message: item.message,
|
|
82
|
+
filePath,
|
|
83
|
+
line: item.line,
|
|
84
|
+
column: item.column || 1,
|
|
85
|
+
severity,
|
|
86
|
+
semantic: severity === "error" ? "blocking" : "warning",
|
|
87
|
+
tool: "oxlint",
|
|
88
|
+
rule: item.ruleId,
|
|
89
|
+
fixable: !!item.fix,
|
|
90
|
+
fixSuggestion: item.fix?.text,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// If JSON parsing fails, try line-based parsing for CLI output
|
|
95
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
// Try to match: file.ts:10:7: Error message [ruleId]
|
|
98
|
+
const match = line.match(/^(\d+):(\d+)\s+(.+?)\s*\[(\w+)\]$/);
|
|
99
|
+
if (match) {
|
|
100
|
+
diagnostics.push({
|
|
101
|
+
id: `oxlint-${match[1]}-${match[4]}`,
|
|
102
|
+
message: `${match[4]}: ${match[3]}`,
|
|
103
|
+
filePath,
|
|
104
|
+
line: parseInt(match[1], 10),
|
|
105
|
+
column: parseInt(match[2], 10),
|
|
106
|
+
severity: "warning",
|
|
107
|
+
semantic: "warning",
|
|
108
|
+
tool: "oxlint",
|
|
109
|
+
rule: match[4],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return diagnostics;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const oxlintRunner: RunnerDefinition = {
|
|
119
|
+
id: "oxlint",
|
|
120
|
+
appliesTo: ["jsts"],
|
|
121
|
+
priority: 12, // Between biome (10) and slop (25)
|
|
122
|
+
enabledByDefault: false, // Opt-in initially - let users choose between biome/oxlint
|
|
123
|
+
skipTestFiles: true, // Test files often use patterns that trigger false positives
|
|
124
|
+
|
|
125
|
+
async run(ctx: DispatchContext): Promise<RunnerResult> {
|
|
126
|
+
// Skip if oxlint is not installed
|
|
127
|
+
if (!oxlint.isAvailable(ctx.cwd || process.cwd())) {
|
|
128
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check if user explicitly disabled oxlint (keep biome as primary)
|
|
132
|
+
if (ctx.pi.getFlag("no-oxlint")) {
|
|
133
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Build args
|
|
137
|
+
// --format json: JSON output
|
|
138
|
+
// --config: Only if config file exists (zero-config otherwise)
|
|
139
|
+
const args: string[] = ["--format", "json"];
|
|
140
|
+
|
|
141
|
+
// Check for config file
|
|
142
|
+
const configPath = findOxlintConfig(ctx.cwd);
|
|
143
|
+
if (configPath) {
|
|
144
|
+
args.push("--config", configPath);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Add file path
|
|
148
|
+
args.push(ctx.filePath);
|
|
149
|
+
|
|
150
|
+
const result = safeSpawn(oxlint.getCommand()!, args, {
|
|
151
|
+
timeout: 10000, // Fast - should complete quickly
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// oxlint exits with code 1 if issues found, 0 if clean
|
|
155
|
+
if (result.status === 0 && !result.stdout?.trim()) {
|
|
156
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Parse diagnostics
|
|
160
|
+
const raw = result.stdout + result.stderr;
|
|
161
|
+
const diagnostics = parseOxlintOutput(raw, ctx.filePath);
|
|
162
|
+
|
|
163
|
+
if (diagnostics.length === 0) {
|
|
164
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
status: "failed",
|
|
169
|
+
diagnostics,
|
|
170
|
+
semantic: "warning",
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
export default oxlintRunner;
|
|
@@ -6,62 +6,27 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Requires: pyright (pip install pyright or npm install -g pyright)
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
let pyrightAvailable = null;
|
|
14
|
-
let pyrightCommand = null;
|
|
15
|
-
/**
|
|
16
|
-
* Find pyright command, checking venv first, then global.
|
|
17
|
-
* Looks in .venv/bin, venv/bin (Unix), .venv/Scripts, venv/Scripts (Windows)
|
|
18
|
-
*/
|
|
19
|
-
function findPyrightCommand(cwd) {
|
|
20
|
-
// Check common venv locations
|
|
21
|
-
const venvPaths = [
|
|
22
|
-
".venv/bin/pyright",
|
|
23
|
-
"venv/bin/pyright",
|
|
24
|
-
".venv/Scripts/pyright.exe",
|
|
25
|
-
"venv/Scripts/pyright.exe",
|
|
26
|
-
];
|
|
27
|
-
for (const venvPath of venvPaths) {
|
|
28
|
-
const fullPath = path.join(cwd, venvPath);
|
|
29
|
-
if (fs.existsSync(fullPath)) {
|
|
30
|
-
return `"${fullPath}"`; // Quote for Windows paths with spaces
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Fall back to global
|
|
34
|
-
return "pyright";
|
|
35
|
-
}
|
|
36
|
-
function isPyrightAvailable(cwd) {
|
|
37
|
-
if (pyrightAvailable !== null)
|
|
38
|
-
return pyrightAvailable;
|
|
39
|
-
const command = findPyrightCommand(cwd || process.cwd());
|
|
40
|
-
const check = spawnSync(command, ["--version"], {
|
|
41
|
-
encoding: "utf-8",
|
|
42
|
-
timeout: 5000,
|
|
43
|
-
shell: true,
|
|
44
|
-
});
|
|
45
|
-
pyrightAvailable = !check.error && check.status === 0;
|
|
46
|
-
if (pyrightAvailable)
|
|
47
|
-
pyrightCommand = command;
|
|
48
|
-
return pyrightAvailable;
|
|
49
|
-
}
|
|
9
|
+
import { ensureTool } from "../../installer/index.js";
|
|
10
|
+
import { safeSpawn } from "../../safe-spawn.js";
|
|
11
|
+
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
12
|
+
const pyright = createAvailabilityChecker("pyright", ".exe");
|
|
50
13
|
const pyrightRunner = {
|
|
51
14
|
id: "pyright",
|
|
52
15
|
appliesTo: ["python"],
|
|
53
16
|
priority: 5, // Higher priority than ruff (10) - type errors are more important
|
|
54
17
|
enabledByDefault: true,
|
|
55
18
|
async run(ctx) {
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
19
|
+
const cwd = ctx.cwd || process.cwd();
|
|
20
|
+
// Auto-install pyright if not available (it's one of the 4 auto-install tools)
|
|
21
|
+
if (!pyright.isAvailable(cwd)) {
|
|
22
|
+
const installed = await ensureTool("pyright");
|
|
23
|
+
if (!installed) {
|
|
24
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
25
|
+
}
|
|
59
26
|
}
|
|
60
27
|
// Run pyright with JSON output (use venv-local or global command)
|
|
61
|
-
const result =
|
|
62
|
-
encoding: "utf-8",
|
|
28
|
+
const result = safeSpawn(pyright.getCommand(), ["--outputjson", ctx.filePath], {
|
|
63
29
|
timeout: 60000,
|
|
64
|
-
shell: true,
|
|
65
30
|
});
|
|
66
31
|
// Pyright returns non-zero when errors found, that's OK
|
|
67
32
|
if (result.error) {
|
|
@@ -81,34 +46,39 @@ const pyrightRunner = {
|
|
|
81
46
|
return {
|
|
82
47
|
status: hasErrors ? "failed" : "succeeded",
|
|
83
48
|
diagnostics,
|
|
84
|
-
semantic: "warning",
|
|
49
|
+
semantic: hasErrors ? "blocking" : "warning",
|
|
85
50
|
};
|
|
86
51
|
}
|
|
87
52
|
catch {
|
|
88
|
-
// JSON parse
|
|
89
|
-
return {
|
|
53
|
+
// JSON parse error
|
|
54
|
+
return {
|
|
55
|
+
status: "failed",
|
|
56
|
+
diagnostics: [],
|
|
57
|
+
semantic: "none",
|
|
58
|
+
rawOutput: output.slice(0, 500),
|
|
59
|
+
};
|
|
90
60
|
}
|
|
91
61
|
},
|
|
92
62
|
};
|
|
93
|
-
function parsePyrightOutput(data,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
63
|
+
function parsePyrightOutput(data, _filePath) {
|
|
64
|
+
const diagnostics = [];
|
|
65
|
+
// Pyright JSON output has generalDiagnostics array
|
|
66
|
+
const generalDiags = data.generalDiagnostics || [];
|
|
67
|
+
for (const diag of generalDiags) {
|
|
68
|
+
// Skip if not for this file (pyright may output diagnostics for imports)
|
|
69
|
+
// For now, include all - caller will filter if needed
|
|
70
|
+
diagnostics.push({
|
|
71
|
+
id: `pyright-${diag.rule || diag.start?.line || "unknown"}`,
|
|
72
|
+
message: diag.message || "Type error",
|
|
73
|
+
filePath: diag.file || _filePath,
|
|
74
|
+
line: diag.start?.line || 0,
|
|
75
|
+
column: diag.start?.column || 0,
|
|
76
|
+
severity: diag.severity === "error" ? "error" : "warning",
|
|
77
|
+
semantic: diag.severity === "error" ? "blocking" : "warning",
|
|
78
|
+
tool: "pyright",
|
|
79
|
+
rule: diag.rule,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return diagnostics;
|
|
113
83
|
}
|
|
114
84
|
export default pyrightRunner;
|
|
@@ -55,7 +55,14 @@ greet(123)
|
|
|
55
55
|
expect(result.diagnostics.some((d) => d.severity === "error")).toBe(true);
|
|
56
56
|
}
|
|
57
57
|
finally {
|
|
58
|
-
|
|
58
|
+
try {
|
|
59
|
+
if (fs.existsSync(tmpFile)) {
|
|
60
|
+
fs.unlinkSync(tmpFile);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore cleanup errors
|
|
65
|
+
}
|
|
59
66
|
}
|
|
60
67
|
});
|
|
61
68
|
it("should pass valid Python files", async () => {
|
|
@@ -78,7 +85,14 @@ greet("world")
|
|
|
78
85
|
expect(result.diagnostics.length).toBe(0);
|
|
79
86
|
}
|
|
80
87
|
finally {
|
|
81
|
-
|
|
88
|
+
try {
|
|
89
|
+
if (fs.existsSync(tmpFile)) {
|
|
90
|
+
fs.unlinkSync(tmpFile);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Ignore cleanup errors
|
|
95
|
+
}
|
|
82
96
|
}
|
|
83
97
|
});
|
|
84
98
|
});
|
|
@@ -72,7 +72,13 @@ greet(123)
|
|
|
72
72
|
expect(result.diagnostics.some((d) => d.tool === "pyright")).toBe(true);
|
|
73
73
|
expect(result.diagnostics.some((d) => d.severity === "error")).toBe(true);
|
|
74
74
|
} finally {
|
|
75
|
-
|
|
75
|
+
try {
|
|
76
|
+
if (fs.existsSync(tmpFile)) {
|
|
77
|
+
fs.unlinkSync(tmpFile);
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Ignore cleanup errors
|
|
81
|
+
}
|
|
76
82
|
}
|
|
77
83
|
});
|
|
78
84
|
|
|
@@ -103,7 +109,13 @@ greet("world")
|
|
|
103
109
|
expect(result.status).toBe("succeeded");
|
|
104
110
|
expect(result.diagnostics.length).toBe(0);
|
|
105
111
|
} finally {
|
|
106
|
-
|
|
112
|
+
try {
|
|
113
|
+
if (fs.existsSync(tmpFile)) {
|
|
114
|
+
fs.unlinkSync(tmpFile);
|
|
115
|
+
}
|
|
116
|
+
} catch {
|
|
117
|
+
// Ignore cleanup errors
|
|
118
|
+
}
|
|
107
119
|
}
|
|
108
120
|
});
|
|
109
121
|
});
|