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
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TypeScript LSP runner for dispatch system
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses the new LSP client architecture (Phase 3) when --lens-lsp is enabled.
|
|
5
|
+
* Falls back to built-in TypeScriptClient for backward compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated The built-in TypeScriptClient is deprecated. Use --lens-lsp for full LSP support.
|
|
5
8
|
*/
|
|
6
9
|
import { TypeScriptClient } from "../../typescript-client.js";
|
|
10
|
+
import { getLSPService } from "../../lsp/index.js";
|
|
7
11
|
import { readFileContent } from "./utils.js";
|
|
8
12
|
const tsLspRunner = {
|
|
9
13
|
id: "ts-lsp",
|
|
@@ -15,39 +19,106 @@ const tsLspRunner = {
|
|
|
15
19
|
if (!ctx.filePath.match(/\.tsx?$/)) {
|
|
16
20
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
17
21
|
}
|
|
18
|
-
// Use
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (!content) {
|
|
22
|
-
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
23
|
-
}
|
|
24
|
-
tsClient.updateFile(ctx.filePath, content);
|
|
25
|
-
const diags = tsClient.getDiagnostics(ctx.filePath);
|
|
26
|
-
if (diags.length === 0) {
|
|
27
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
28
|
-
}
|
|
29
|
-
// Convert to diagnostics
|
|
30
|
-
const diagnostics = [];
|
|
31
|
-
for (const d of diags) {
|
|
32
|
-
const severity = d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info";
|
|
33
|
-
diagnostics.push({
|
|
34
|
-
id: `ts-${d.range.start.line}-${d.code}`,
|
|
35
|
-
message: d.message,
|
|
36
|
-
filePath: ctx.filePath,
|
|
37
|
-
line: d.range.start.line + 1,
|
|
38
|
-
severity,
|
|
39
|
-
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
40
|
-
tool: "ts-lsp",
|
|
41
|
-
rule: `TS${d.code}`,
|
|
42
|
-
});
|
|
22
|
+
// Phase 3: Use LSP client if --lens-lsp flag is enabled
|
|
23
|
+
if (ctx.pi.getFlag("lens-lsp")) {
|
|
24
|
+
return runWithLSPClient(ctx);
|
|
43
25
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
: "succeeded",
|
|
48
|
-
diagnostics,
|
|
49
|
-
semantic: "warning",
|
|
50
|
-
};
|
|
26
|
+
// DEPRECATED: Fall back to built-in TypeScriptClient
|
|
27
|
+
// This path is deprecated and will be removed in a future release
|
|
28
|
+
return runWithBuiltinClient(ctx);
|
|
51
29
|
},
|
|
52
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* Run with new LSP client (Phase 3)
|
|
33
|
+
*/
|
|
34
|
+
async function runWithLSPClient(ctx) {
|
|
35
|
+
const lspService = getLSPService();
|
|
36
|
+
// Check if we have LSP available for this file
|
|
37
|
+
const hasLSP = await lspService.hasLSP(ctx.filePath);
|
|
38
|
+
if (!hasLSP) {
|
|
39
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
40
|
+
}
|
|
41
|
+
// Read file content
|
|
42
|
+
const content = readFileContent(ctx.filePath);
|
|
43
|
+
if (!content) {
|
|
44
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
45
|
+
}
|
|
46
|
+
// Open file in LSP and get diagnostics
|
|
47
|
+
await lspService.openFile(ctx.filePath, content);
|
|
48
|
+
// Small delay to let diagnostics propagate
|
|
49
|
+
await new Promise(r => setTimeout(r, 500));
|
|
50
|
+
const lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
51
|
+
// Convert LSP diagnostics to our format
|
|
52
|
+
// Defensive: filter out malformed diagnostics that may lack range
|
|
53
|
+
const diagnostics = lspDiags
|
|
54
|
+
.filter((d) => d.range?.start?.line !== undefined)
|
|
55
|
+
.map((d) => ({
|
|
56
|
+
id: `ts-lsp:${d.code ?? "unknown"}:${d.range.start.line}`,
|
|
57
|
+
message: d.message,
|
|
58
|
+
filePath: ctx.filePath,
|
|
59
|
+
line: d.range.start.line + 1,
|
|
60
|
+
column: d.range.start.character + 1,
|
|
61
|
+
severity: d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
|
|
62
|
+
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
63
|
+
tool: "ts-lsp",
|
|
64
|
+
code: String(d.code ?? ""),
|
|
65
|
+
}));
|
|
66
|
+
return {
|
|
67
|
+
status: "failed",
|
|
68
|
+
diagnostics,
|
|
69
|
+
semantic: "blocking",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run with deprecated built-in TypeScriptClient
|
|
74
|
+
* @deprecated Use runWithLSPClient instead
|
|
75
|
+
*/
|
|
76
|
+
async function runWithBuiltinClient(ctx) {
|
|
77
|
+
const tsClient = new TypeScriptClient();
|
|
78
|
+
const content = readFileContent(ctx.filePath);
|
|
79
|
+
if (!content) {
|
|
80
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
81
|
+
}
|
|
82
|
+
tsClient.updateFile(ctx.filePath, content);
|
|
83
|
+
const diags = tsClient.getDiagnostics(ctx.filePath);
|
|
84
|
+
if (diags.length === 0) {
|
|
85
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
86
|
+
}
|
|
87
|
+
// Get code fixes for all errors
|
|
88
|
+
const allFixes = tsClient.getAllCodeFixes(ctx.filePath);
|
|
89
|
+
// Convert to diagnostics
|
|
90
|
+
const diagnostics = [];
|
|
91
|
+
// The built-in client returns Diagnostic with { range: { start: { line, character } } }
|
|
92
|
+
for (const d of diags) {
|
|
93
|
+
// Safely access nested properties
|
|
94
|
+
if (!d.range?.start)
|
|
95
|
+
continue;
|
|
96
|
+
const line = d.range.start.line;
|
|
97
|
+
const character = d.range.start.character ?? 0;
|
|
98
|
+
const severity = d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info";
|
|
99
|
+
const semantic = d.severity === 1 ? "blocking" : "warning";
|
|
100
|
+
// Find fixes for this line
|
|
101
|
+
const lineFixes = allFixes.get(line);
|
|
102
|
+
const fixDescription = lineFixes?.[0]?.description;
|
|
103
|
+
diagnostics.push({
|
|
104
|
+
id: `ts:${d.code}:${line}`,
|
|
105
|
+
message: fixDescription
|
|
106
|
+
? `${d.message} [💡 ${fixDescription}]`
|
|
107
|
+
: d.message,
|
|
108
|
+
filePath: ctx.filePath,
|
|
109
|
+
line: line + 1,
|
|
110
|
+
column: character + 1,
|
|
111
|
+
severity,
|
|
112
|
+
semantic,
|
|
113
|
+
tool: "ts-lsp",
|
|
114
|
+
fixable: !!lineFixes && lineFixes.length > 0,
|
|
115
|
+
fixSuggestion: fixDescription,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
status: "failed",
|
|
120
|
+
diagnostics,
|
|
121
|
+
semantic: "blocking",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
53
124
|
export default tsLspRunner;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TypeScript LSP runner for dispatch system
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Uses the new LSP client architecture (Phase 3) when --lens-lsp is enabled.
|
|
5
|
+
* Falls back to built-in TypeScriptClient for backward compatibility.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated The built-in TypeScriptClient is deprecated. Use --lens-lsp for full LSP support.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
import { TypeScriptClient } from "../../typescript-client.js";
|
|
11
|
+
import { getLSPService } from "../../lsp/index.js";
|
|
8
12
|
import type {
|
|
9
13
|
Diagnostic,
|
|
10
14
|
DispatchContext,
|
|
@@ -25,47 +29,125 @@ const tsLspRunner: RunnerDefinition = {
|
|
|
25
29
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
// Use
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const content = readFileContent(ctx.filePath);
|
|
32
|
-
if (!content) {
|
|
33
|
-
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
32
|
+
// Phase 3: Use LSP client if --lens-lsp flag is enabled
|
|
33
|
+
if (ctx.pi.getFlag("lens-lsp")) {
|
|
34
|
+
return runWithLSPClient(ctx);
|
|
34
35
|
}
|
|
35
|
-
tsClient.updateFile(ctx.filePath, content);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// DEPRECATED: Fall back to built-in TypeScriptClient
|
|
38
|
+
// This path is deprecated and will be removed in a future release
|
|
39
|
+
return runWithBuiltinClient(ctx);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Run with new LSP client (Phase 3)
|
|
45
|
+
*/
|
|
46
|
+
async function runWithLSPClient(ctx: DispatchContext): Promise<RunnerResult> {
|
|
47
|
+
const lspService = getLSPService();
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info";
|
|
49
|
-
diagnostics.push({
|
|
50
|
-
id: `ts-${d.range.start.line}-${d.code}`,
|
|
51
|
-
message: d.message,
|
|
52
|
-
filePath: ctx.filePath,
|
|
53
|
-
line: d.range.start.line + 1,
|
|
54
|
-
severity,
|
|
55
|
-
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
56
|
-
tool: "ts-lsp",
|
|
57
|
-
rule: `TS${d.code}`,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
49
|
+
// Check if we have LSP available for this file
|
|
50
|
+
const hasLSP = await lspService.hasLSP(ctx.filePath);
|
|
51
|
+
if (!hasLSP) {
|
|
52
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
53
|
+
}
|
|
60
54
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
// Read file content
|
|
56
|
+
const content = readFileContent(ctx.filePath);
|
|
57
|
+
if (!content) {
|
|
58
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Open file in LSP and get diagnostics
|
|
62
|
+
await lspService.openFile(ctx.filePath, content);
|
|
63
|
+
// Small delay to let diagnostics propagate
|
|
64
|
+
await new Promise(r => setTimeout(r, 500));
|
|
65
|
+
const lspDiags = await lspService.getDiagnostics(ctx.filePath);
|
|
66
|
+
|
|
67
|
+
// Convert LSP diagnostics to our format
|
|
68
|
+
// Defensive: filter out malformed diagnostics that may lack range
|
|
69
|
+
const diagnostics: Diagnostic[] = lspDiags
|
|
70
|
+
.filter((d) => d.range?.start?.line !== undefined)
|
|
71
|
+
.map((d) => ({
|
|
72
|
+
id: `ts-lsp:${d.code ?? "unknown"}:${d.range.start.line}`,
|
|
73
|
+
message: d.message,
|
|
74
|
+
filePath: ctx.filePath,
|
|
75
|
+
line: d.range.start.line + 1,
|
|
76
|
+
column: d.range.start.character + 1,
|
|
77
|
+
severity: d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info",
|
|
78
|
+
semantic: d.severity === 1 ? "blocking" : "warning",
|
|
79
|
+
tool: "ts-lsp",
|
|
80
|
+
code: String(d.code ?? ""),
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
status: "failed",
|
|
85
|
+
diagnostics,
|
|
86
|
+
semantic: "blocking",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Run with deprecated built-in TypeScriptClient
|
|
92
|
+
* @deprecated Use runWithLSPClient instead
|
|
93
|
+
*/
|
|
94
|
+
async function runWithBuiltinClient(ctx: DispatchContext): Promise<RunnerResult> {
|
|
95
|
+
const tsClient = new TypeScriptClient();
|
|
96
|
+
|
|
97
|
+
const content = readFileContent(ctx.filePath);
|
|
98
|
+
if (!content) {
|
|
99
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
100
|
+
}
|
|
101
|
+
tsClient.updateFile(ctx.filePath, content);
|
|
102
|
+
|
|
103
|
+
const diags = tsClient.getDiagnostics(ctx.filePath);
|
|
104
|
+
|
|
105
|
+
if (diags.length === 0) {
|
|
106
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Get code fixes for all errors
|
|
110
|
+
const allFixes = tsClient.getAllCodeFixes(ctx.filePath);
|
|
111
|
+
|
|
112
|
+
// Convert to diagnostics
|
|
113
|
+
const diagnostics: Diagnostic[] = [];
|
|
114
|
+
|
|
115
|
+
// The built-in client returns Diagnostic with { range: { start: { line, character } } }
|
|
116
|
+
for (const d of diags) {
|
|
117
|
+
// Safely access nested properties
|
|
118
|
+
if (!d.range?.start) continue;
|
|
119
|
+
|
|
120
|
+
const line = d.range.start.line;
|
|
121
|
+
const character = d.range.start.character ?? 0;
|
|
122
|
+
const severity =
|
|
123
|
+
d.severity === 1 ? "error" : d.severity === 2 ? "warning" : "info";
|
|
124
|
+
const semantic = d.severity === 1 ? "blocking" : "warning";
|
|
125
|
+
|
|
126
|
+
// Find fixes for this line
|
|
127
|
+
const lineFixes = allFixes.get(line);
|
|
128
|
+
const fixDescription = lineFixes?.[0]?.description;
|
|
129
|
+
|
|
130
|
+
diagnostics.push({
|
|
131
|
+
id: `ts:${d.code}:${line}`,
|
|
132
|
+
message: fixDescription
|
|
133
|
+
? `${d.message} [💡 ${fixDescription}]`
|
|
134
|
+
: d.message,
|
|
135
|
+
filePath: ctx.filePath,
|
|
136
|
+
line: line + 1,
|
|
137
|
+
column: character + 1,
|
|
138
|
+
severity,
|
|
139
|
+
semantic,
|
|
140
|
+
tool: "ts-lsp",
|
|
141
|
+
fixable: !!lineFixes && lineFixes.length > 0,
|
|
142
|
+
fixSuggestion: fixDescription,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
status: "failed",
|
|
148
|
+
diagnostics,
|
|
149
|
+
semantic: "blocking",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
70
152
|
|
|
71
153
|
export default tsLspRunner;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript Slop runner for dispatch system
|
|
3
|
+
*
|
|
4
|
+
* Detects "slop" patterns in TypeScript/JavaScript code:
|
|
5
|
+
* - Verbose patterns (ceremony that adds no value)
|
|
6
|
+
* - Defensive over-checking (excessive guards)
|
|
7
|
+
* - Manual reimplementation of builtins
|
|
8
|
+
* - Unnecessary object allocations
|
|
9
|
+
*
|
|
10
|
+
* Based on slop-code-bench patterns adapted for TypeScript
|
|
11
|
+
*/
|
|
12
|
+
import { safeSpawn } from "../../safe-spawn.js";
|
|
13
|
+
import { createConfigFinder, isSgAvailable, } from "./utils/runner-helpers.js";
|
|
14
|
+
const findSlopConfig = createConfigFinder("ts-slop-rules");
|
|
15
|
+
const tsSlopRunner = {
|
|
16
|
+
id: "ts-slop",
|
|
17
|
+
// NOTE: TypeScript/JavaScript slop detection is now handled by ast-grep-napi
|
|
18
|
+
// This CLI runner is kept as fallback for edge cases but disabled by default
|
|
19
|
+
appliesTo: [], // Disabled - use ast-grep-napi instead
|
|
20
|
+
priority: 20,
|
|
21
|
+
enabledByDefault: false,
|
|
22
|
+
skipTestFiles: true,
|
|
23
|
+
async run(ctx) {
|
|
24
|
+
// Check if ast-grep is available
|
|
25
|
+
if (!isSgAvailable()) {
|
|
26
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
27
|
+
}
|
|
28
|
+
// Find slop config
|
|
29
|
+
const configPath = findSlopConfig(ctx.cwd);
|
|
30
|
+
if (!configPath) {
|
|
31
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
32
|
+
}
|
|
33
|
+
// Run ast-grep scan
|
|
34
|
+
const args = ["sg", "scan", "--config", configPath, "--json", ctx.filePath];
|
|
35
|
+
const result = safeSpawn("npx", args, {
|
|
36
|
+
timeout: 30000,
|
|
37
|
+
});
|
|
38
|
+
const raw = result.stdout + result.stderr;
|
|
39
|
+
if (result.status === 0 && !raw.trim()) {
|
|
40
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
41
|
+
}
|
|
42
|
+
// Parse results
|
|
43
|
+
const diagnostics = parseSlopOutput(raw, ctx.filePath);
|
|
44
|
+
if (diagnostics.length === 0) {
|
|
45
|
+
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
status: "failed",
|
|
49
|
+
diagnostics,
|
|
50
|
+
semantic: "warning",
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
function parseSlopOutput(raw, filePath) {
|
|
55
|
+
const diagnostics = [];
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(raw);
|
|
58
|
+
if (Array.isArray(parsed)) {
|
|
59
|
+
for (const item of parsed) {
|
|
60
|
+
const line = item.range?.start?.line || 1;
|
|
61
|
+
const ruleId = item.rule || "unknown";
|
|
62
|
+
const message = item.message || "";
|
|
63
|
+
// Categorize by severity based on weight from metadata
|
|
64
|
+
const weight = item.metadata?.weight || 3;
|
|
65
|
+
const severity = weight >= 4 ? "error" : "warning";
|
|
66
|
+
const category = item.metadata?.category || "slop";
|
|
67
|
+
// Add slop category indicator to message
|
|
68
|
+
let enhancedMessage = `[${category}] ${message}`;
|
|
69
|
+
if (item.replacement) {
|
|
70
|
+
const preview = item.replacement.length > 40
|
|
71
|
+
? `${item.replacement.substring(0, 40)}...`
|
|
72
|
+
: item.replacement;
|
|
73
|
+
enhancedMessage += `\n💡 Suggested fix: → "${preview}"`;
|
|
74
|
+
}
|
|
75
|
+
diagnostics.push({
|
|
76
|
+
id: `ts-slop-${line}-${ruleId}`,
|
|
77
|
+
message: enhancedMessage,
|
|
78
|
+
filePath,
|
|
79
|
+
line,
|
|
80
|
+
column: item.range?.start?.column || 0,
|
|
81
|
+
severity,
|
|
82
|
+
semantic: severity === "error" ? "blocking" : "warning",
|
|
83
|
+
tool: "ts-slop",
|
|
84
|
+
rule: ruleId,
|
|
85
|
+
fixable: !!item.replacement,
|
|
86
|
+
fixSuggestion: item.replacement,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// JSON parse failed, try line-by-line
|
|
93
|
+
const lines = raw.split("\n");
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
if (line.includes(":") && line.includes("L")) {
|
|
96
|
+
const match = line.match(/L(\d+):?\s*(.+)/);
|
|
97
|
+
if (match) {
|
|
98
|
+
diagnostics.push({
|
|
99
|
+
id: `ts-slop-${match[1]}-line`,
|
|
100
|
+
message: `[slop] ${match[2].trim()}`,
|
|
101
|
+
filePath,
|
|
102
|
+
line: parseInt(match[1], 10),
|
|
103
|
+
severity: "warning",
|
|
104
|
+
semantic: "warning",
|
|
105
|
+
tool: "ts-slop",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return diagnostics;
|
|
112
|
+
}
|
|
113
|
+
export default tsSlopRunner;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
function createMockContext(filePath) {
|
|
6
|
+
return {
|
|
7
|
+
filePath,
|
|
8
|
+
cwd: process.cwd(),
|
|
9
|
+
kind: "jsts",
|
|
10
|
+
autofix: false,
|
|
11
|
+
deltaMode: false,
|
|
12
|
+
baselines: { get: () => [], add: () => { }, save: () => { } },
|
|
13
|
+
pi: {},
|
|
14
|
+
hasTool: async () => false,
|
|
15
|
+
log: () => { },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// Helper for safe file cleanup
|
|
19
|
+
function safeUnlink(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
if (fs.existsSync(filePath)) {
|
|
22
|
+
fs.unlinkSync(filePath);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Ignore cleanup errors on Windows
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
describe("ts-slop runner", () => {
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
it("should have correct runner definition", async () => {
|
|
32
|
+
const slopModule = await import("./ts-slop.js");
|
|
33
|
+
const runner = slopModule.default;
|
|
34
|
+
expect(runner.id).toBe("ts-slop");
|
|
35
|
+
// NOTE: TS/JS slop is now handled by ast-grep-napi
|
|
36
|
+
// This CLI runner is disabled by default as fallback
|
|
37
|
+
expect(runner.appliesTo).toEqual([]); // Disabled - use ast-grep-napi
|
|
38
|
+
expect(runner.priority).toBe(20);
|
|
39
|
+
expect(runner.enabledByDefault).toBe(false);
|
|
40
|
+
expect(runner.skipTestFiles).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it("should detect ast-grep availability", () => {
|
|
43
|
+
const { spawnSync } = require("node:child_process");
|
|
44
|
+
const result = spawnSync("npx", ["sg", "--version"], {
|
|
45
|
+
encoding: "utf-8",
|
|
46
|
+
timeout: 10000,
|
|
47
|
+
shell: true,
|
|
48
|
+
});
|
|
49
|
+
expect(result.error || result.status !== 0 ? "not available" : "available").toBe("available");
|
|
50
|
+
});
|
|
51
|
+
it("should detect for-index-length pattern (or other slop)", async () => {
|
|
52
|
+
const tmpFile = path.join(process.env.TEMP || "/tmp", `ts_slop_test_for_${Date.now()}.ts`);
|
|
53
|
+
fs.writeFileSync(tmpFile, `// Slop: using index loop instead of for-of
|
|
54
|
+
function processItems(items: string[]) {
|
|
55
|
+
for (let i = 0; i < items.length; i++) {
|
|
56
|
+
console.log(items[i]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`);
|
|
60
|
+
try {
|
|
61
|
+
const slopModule = await import("./ts-slop.js");
|
|
62
|
+
const runner = slopModule.default;
|
|
63
|
+
const result = await runner.run(createMockContext(tmpFile));
|
|
64
|
+
// Should detect at least some slop patterns
|
|
65
|
+
// (specific patterns may vary based on ast-grep rule accuracy)
|
|
66
|
+
expect(result.status).not.toBe("skipped");
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
safeUnlink(tmpFile);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
it("should detect manual Math min/max pattern (or other slop)", async () => {
|
|
73
|
+
const tmpFile = path.join(process.env.TEMP || "/tmp", `ts_slop_test_minmax_${Date.now()}.ts`);
|
|
74
|
+
fs.writeFileSync(tmpFile, `// Slop: manual min/max instead of Math
|
|
75
|
+
function getMax(a: number, b: number): number {
|
|
76
|
+
if (a > b) {
|
|
77
|
+
const m = a;
|
|
78
|
+
} else {
|
|
79
|
+
const m = b;
|
|
80
|
+
}
|
|
81
|
+
return m;
|
|
82
|
+
}
|
|
83
|
+
`);
|
|
84
|
+
try {
|
|
85
|
+
const slopModule = await import("./ts-slop.js");
|
|
86
|
+
const runner = slopModule.default;
|
|
87
|
+
const result = await runner.run(createMockContext(tmpFile));
|
|
88
|
+
// Should detect at least some slop patterns
|
|
89
|
+
// (specific patterns may vary based on ast-grep rule accuracy)
|
|
90
|
+
expect(result.status).not.toBe("skipped");
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
safeUnlink(tmpFile);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
it("should detect indexOf !== -1 pattern (or other slop)", async () => {
|
|
97
|
+
const tmpFile = path.join(process.env.TEMP || "/tmp", `ts_slop_test_indexof_${Date.now()}.ts`);
|
|
98
|
+
fs.writeFileSync(tmpFile, `// Slop: indexOf check instead of includes
|
|
99
|
+
function hasItem(arr: string[], item: string): boolean {
|
|
100
|
+
if (arr.indexOf(item) !== -1) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
`);
|
|
106
|
+
try {
|
|
107
|
+
const slopModule = await import("./ts-slop.js");
|
|
108
|
+
const runner = slopModule.default;
|
|
109
|
+
const result = await runner.run(createMockContext(tmpFile));
|
|
110
|
+
// Should detect at least some slop patterns
|
|
111
|
+
// (specific patterns may vary based on ast-grep rule accuracy)
|
|
112
|
+
expect(result.status).not.toBe("skipped");
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
safeUnlink(tmpFile);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
it("should detect array length > 0 pattern", async () => {
|
|
119
|
+
const tmpFile = path.join(process.env.TEMP || "/tmp", `ts_slop_test_length_${Date.now()}.ts`);
|
|
120
|
+
fs.writeFileSync(tmpFile, `// Slop: length check instead of truthiness
|
|
121
|
+
function processItems(arr: string[]): void {
|
|
122
|
+
if (arr.length > 0) {
|
|
123
|
+
console.log("has items");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
`);
|
|
127
|
+
try {
|
|
128
|
+
const slopModule = await import("./ts-slop.js");
|
|
129
|
+
const runner = slopModule.default;
|
|
130
|
+
const result = await runner.run(createMockContext(tmpFile));
|
|
131
|
+
// This pattern may or may not be detected depending on rule specificity
|
|
132
|
+
// Just verify the scan ran without errors
|
|
133
|
+
expect(result.status).toBe("succeeded");
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
safeUnlink(tmpFile);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
it("should pass clean TypeScript files", async () => {
|
|
140
|
+
const tmpFile = path.join(process.env.TEMP || "/tmp", `ts_slop_test_ok_${Date.now()}.ts`);
|
|
141
|
+
fs.writeFileSync(tmpFile, `// Clean TypeScript code
|
|
142
|
+
function processItems(items: string[]): void {
|
|
143
|
+
for (const item of items) {
|
|
144
|
+
console.log(item);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function findMax(a: number, b: number): number {
|
|
149
|
+
return Math.max(a, b);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function contains(arr: string[], item: string): boolean {
|
|
153
|
+
return arr.includes(item);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function hasItems(arr: string[]): boolean {
|
|
157
|
+
return arr.length > 0; // This is actually OK, but let's see
|
|
158
|
+
}
|
|
159
|
+
`);
|
|
160
|
+
try {
|
|
161
|
+
const slopModule = await import("./ts-slop.js");
|
|
162
|
+
const runner = slopModule.default;
|
|
163
|
+
const result = await runner.run(createMockContext(tmpFile));
|
|
164
|
+
// Should have minimal or no slop issues for clean code
|
|
165
|
+
const slopIssues = result.diagnostics.filter((d) => d.tool === "ts-slop");
|
|
166
|
+
// Allow for minor issues - the length check might still trigger
|
|
167
|
+
expect(slopIssues.length).toBeLessThanOrEqual(1);
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
try {
|
|
171
|
+
if (fs.existsSync(tmpFile)) {
|
|
172
|
+
safeUnlink(tmpFile);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Ignore cleanup errors
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|