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,558 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Validation via Tree-sitter
|
|
3
|
+
*
|
|
4
|
+
* Detects config/environment variable access in code and validates against
|
|
5
|
+
* actual config files (INI, YAML, JSON, .env).
|
|
6
|
+
*
|
|
7
|
+
* Catches:
|
|
8
|
+
* - Undefined config keys
|
|
9
|
+
* - Typos in config keys
|
|
10
|
+
* - Missing environment variables
|
|
11
|
+
* - Deprecated/renamed keys
|
|
12
|
+
*
|
|
13
|
+
* Supported patterns:
|
|
14
|
+
* - Python: config.get("section.key"), os.environ.get("VAR")
|
|
15
|
+
* - JS/TS: process.env.VAR, config.get("key")
|
|
16
|
+
* - Go: os.Getenv("VAR")
|
|
17
|
+
* - Rust: env::var("VAR")
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import * as fs from "node:fs/promises";
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import { TreeSitterClient } from "./tree-sitter-client.js";
|
|
23
|
+
|
|
24
|
+
// --- Types ---
|
|
25
|
+
|
|
26
|
+
export interface ConfigKey {
|
|
27
|
+
key: string;
|
|
28
|
+
file: string;
|
|
29
|
+
line: number;
|
|
30
|
+
value?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ConfigAccess {
|
|
34
|
+
key: string;
|
|
35
|
+
file: string;
|
|
36
|
+
line: number;
|
|
37
|
+
column: number;
|
|
38
|
+
pattern: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ConfigValidationResult {
|
|
42
|
+
undefined: ConfigAccess[];
|
|
43
|
+
typos: Array<{ access: ConfigAccess; suggestion: string }>;
|
|
44
|
+
available: ConfigKey[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// --- Tree-sitter Queries for Config Access Patterns ---
|
|
48
|
+
|
|
49
|
+
const CONFIG_QUERIES: Record<string, string> = {
|
|
50
|
+
// Python: config.get("section.key") or os.environ.get("VAR")
|
|
51
|
+
python: `
|
|
52
|
+
; Config object access: config.get("key")
|
|
53
|
+
(call
|
|
54
|
+
function: (attribute
|
|
55
|
+
object: (identifier) @config_obj
|
|
56
|
+
attribute: (identifier) @method (#eq? @method "get") )
|
|
57
|
+
arguments: (argument_list
|
|
58
|
+
(string
|
|
59
|
+
(string_content) @config_key
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
(#match? @config_obj "^(config|cfg|settings|conf)$")
|
|
64
|
+
|
|
65
|
+
; os.environ.get("VAR")
|
|
66
|
+
(call
|
|
67
|
+
function: (attribute
|
|
68
|
+
object: (attribute
|
|
69
|
+
object: (identifier) @os (#eq? @os "os")
|
|
70
|
+
attribute: (identifier) @environ (#eq? @environ "environ")
|
|
71
|
+
)
|
|
72
|
+
attribute: (identifier) @method (#eq? @method "get")
|
|
73
|
+
)
|
|
74
|
+
arguments: (argument_list
|
|
75
|
+
(string (string_content) @env_var)
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
; os.getenv("VAR")
|
|
80
|
+
(call
|
|
81
|
+
function: (attribute
|
|
82
|
+
object: (identifier) @os (#eq? @os "os")
|
|
83
|
+
attribute: (identifier) @getenv (#eq? @getenv "getenv")
|
|
84
|
+
)
|
|
85
|
+
arguments: (argument_list
|
|
86
|
+
(string (string_content) @env_var)
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
`,
|
|
90
|
+
|
|
91
|
+
// JavaScript/TypeScript: process.env.VAR or config.get("key")
|
|
92
|
+
javascript: `
|
|
93
|
+
; process.env.VAR or process.env["VAR"]
|
|
94
|
+
(member_expression
|
|
95
|
+
object: (member_expression
|
|
96
|
+
object: (identifier) @process (#eq? @process "process")
|
|
97
|
+
property: (property_identifier) @env (#eq? @env "env")
|
|
98
|
+
)
|
|
99
|
+
property: (property_identifier) @env_var
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
; process.env["VAR"]
|
|
103
|
+
(member_expression
|
|
104
|
+
object: (member_expression
|
|
105
|
+
object: (identifier) @process (#eq? @process "process")
|
|
106
|
+
property: (property_identifier) @env (#eq? @env "env")
|
|
107
|
+
)
|
|
108
|
+
property: (computed_property_name
|
|
109
|
+
(string (string_fragment) @env_var)
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
; config.get("key") or cfg.get("key")
|
|
114
|
+
(call_expression
|
|
115
|
+
function: (member_expression
|
|
116
|
+
object: (identifier) @config_obj
|
|
117
|
+
property: (property_identifier) @method (#eq? @method "get")
|
|
118
|
+
)
|
|
119
|
+
arguments: (arguments
|
|
120
|
+
(string (string_fragment) @config_key)
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
(#match? @config_obj "^(config|cfg|settings|conf)$")
|
|
124
|
+
`,
|
|
125
|
+
|
|
126
|
+
// Same for TypeScript (tsx)
|
|
127
|
+
tsx: `
|
|
128
|
+
; process.env.VAR
|
|
129
|
+
(member_expression
|
|
130
|
+
object: (member_expression
|
|
131
|
+
object: (identifier) @process (#eq? @process "process")
|
|
132
|
+
property: (property_identifier) @env (#eq? @env "env")
|
|
133
|
+
)
|
|
134
|
+
property: (property_identifier) @env_var
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
; config.get("key")
|
|
138
|
+
(call_expression
|
|
139
|
+
function: (member_expression
|
|
140
|
+
object: (identifier) @config_obj
|
|
141
|
+
property: (property_identifier) @method (#eq? @method "get")
|
|
142
|
+
)
|
|
143
|
+
arguments: (arguments
|
|
144
|
+
(string (string_fragment) @config_key)
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
(#match? @config_obj "^(config|cfg|settings|conf)$")
|
|
148
|
+
`,
|
|
149
|
+
|
|
150
|
+
// Go: os.Getenv("VAR")
|
|
151
|
+
go: `
|
|
152
|
+
(call_expression
|
|
153
|
+
function: (selector_expression
|
|
154
|
+
operand: (identifier) @os (#eq? @os "os")
|
|
155
|
+
field: (field_identifier) @getenv (#eq? @getenv "Getenv")
|
|
156
|
+
)
|
|
157
|
+
arguments: (argument_list
|
|
158
|
+
(raw_string_literal) @env_var
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
`,
|
|
162
|
+
|
|
163
|
+
// Rust: env::var("VAR") or std::env::var("VAR")
|
|
164
|
+
rust: `
|
|
165
|
+
(call_expression
|
|
166
|
+
function: (scoped_identifier
|
|
167
|
+
path: (identifier) @env (#eq? @env "env")
|
|
168
|
+
name: (identifier) @var (#eq? @var "var")
|
|
169
|
+
)
|
|
170
|
+
arguments: (arguments
|
|
171
|
+
(string_literal) @env_var
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
`,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// --- Config File Parsers ---
|
|
178
|
+
|
|
179
|
+
async function parseEnvFile(filePath: string): Promise<ConfigKey[]> {
|
|
180
|
+
const keys: ConfigKey[] = [];
|
|
181
|
+
try {
|
|
182
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
183
|
+
const lines = content.split("\n");
|
|
184
|
+
for (let i = 0; i < lines.length; i++) {
|
|
185
|
+
const line = lines[i].trim();
|
|
186
|
+
// Skip comments and empty lines
|
|
187
|
+
if (line.startsWith("#") || line.startsWith("//") || !line) continue;
|
|
188
|
+
|
|
189
|
+
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
190
|
+
if (match) {
|
|
191
|
+
keys.push({
|
|
192
|
+
key: match[1],
|
|
193
|
+
file: filePath,
|
|
194
|
+
line: i + 1,
|
|
195
|
+
value: match[2].trim(),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// File doesn't exist or can't be read
|
|
201
|
+
}
|
|
202
|
+
return keys;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function parseIniFile(filePath: string): Promise<ConfigKey[]> {
|
|
206
|
+
const keys: ConfigKey[] = [];
|
|
207
|
+
try {
|
|
208
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
209
|
+
const lines = content.split("\n");
|
|
210
|
+
let currentSection = "";
|
|
211
|
+
|
|
212
|
+
for (let i = 0; i < lines.length; i++) {
|
|
213
|
+
const line = lines[i].trim();
|
|
214
|
+
if (!line || line.startsWith(";") || line.startsWith("#")) continue;
|
|
215
|
+
|
|
216
|
+
// Section header: [section]
|
|
217
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
218
|
+
if (sectionMatch) {
|
|
219
|
+
currentSection = sectionMatch[1];
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Key = value
|
|
224
|
+
const keyMatch = line.match(/^([^=]+)\s*=\s*(.*)$/);
|
|
225
|
+
if (keyMatch) {
|
|
226
|
+
const key = keyMatch[1].trim();
|
|
227
|
+
const fullKey = currentSection ? `${currentSection}.${key}` : key;
|
|
228
|
+
keys.push({
|
|
229
|
+
key: fullKey,
|
|
230
|
+
file: filePath,
|
|
231
|
+
line: i + 1,
|
|
232
|
+
value: keyMatch[2].trim(),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch {
|
|
237
|
+
// File doesn't exist or can't be read
|
|
238
|
+
}
|
|
239
|
+
return keys;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async function parseYamlConfig(filePath: string): Promise<ConfigKey[]> {
|
|
243
|
+
const keys: ConfigKey[] = [];
|
|
244
|
+
try {
|
|
245
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
246
|
+
// Simple YAML parser for flat key: value or section.key format
|
|
247
|
+
const lines = content.split("\n");
|
|
248
|
+
let indentStack: { indent: number; key: string }[] = [];
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < lines.length; i++) {
|
|
251
|
+
const line = lines[i];
|
|
252
|
+
const trimmed = line.trim();
|
|
253
|
+
|
|
254
|
+
// Skip comments and empty lines
|
|
255
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
256
|
+
|
|
257
|
+
// Calculate indent
|
|
258
|
+
const indent = line.search(/\S/);
|
|
259
|
+
const _indentMatch = indentStack.find((s) => s.indent === indent);
|
|
260
|
+
|
|
261
|
+
// key: value pattern
|
|
262
|
+
const match = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*):\s*(.*)$/);
|
|
263
|
+
if (match) {
|
|
264
|
+
const key = match[1];
|
|
265
|
+
const value = match[2].trim();
|
|
266
|
+
|
|
267
|
+
// Build full key path
|
|
268
|
+
const parentKeys = indentStack
|
|
269
|
+
.filter((s) => s.indent < indent)
|
|
270
|
+
.map((s) => s.key);
|
|
271
|
+
const fullKey = [...parentKeys, key].join(".");
|
|
272
|
+
|
|
273
|
+
// If has value, it's a config key
|
|
274
|
+
if (value && !value.endsWith(":")) {
|
|
275
|
+
keys.push({
|
|
276
|
+
key: fullKey,
|
|
277
|
+
file: filePath,
|
|
278
|
+
line: i + 1,
|
|
279
|
+
value: value,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Update indent stack
|
|
284
|
+
indentStack = indentStack.filter((s) => s.indent < indent);
|
|
285
|
+
indentStack.push({ indent, key });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
// File doesn't exist or can't be read
|
|
290
|
+
}
|
|
291
|
+
return keys;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function parseJsonConfig(filePath: string): Promise<ConfigKey[]> {
|
|
295
|
+
const keys: ConfigKey[] = [];
|
|
296
|
+
try {
|
|
297
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
298
|
+
const obj = JSON.parse(content);
|
|
299
|
+
|
|
300
|
+
function traverse(obj: unknown, path: string[] = []) {
|
|
301
|
+
if (typeof obj === "object" && obj !== null) {
|
|
302
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
303
|
+
const newPath = [...path, key];
|
|
304
|
+
if (
|
|
305
|
+
typeof value === "string" ||
|
|
306
|
+
typeof value === "number" ||
|
|
307
|
+
typeof value === "boolean"
|
|
308
|
+
) {
|
|
309
|
+
keys.push({
|
|
310
|
+
key: newPath.join("."),
|
|
311
|
+
file: filePath,
|
|
312
|
+
line: 0, // JSON doesn't preserve line numbers easily
|
|
313
|
+
value: String(value),
|
|
314
|
+
});
|
|
315
|
+
} else if (typeof value === "object") {
|
|
316
|
+
traverse(value, newPath);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
traverse(obj);
|
|
323
|
+
} catch {
|
|
324
|
+
// File doesn't exist or invalid JSON
|
|
325
|
+
}
|
|
326
|
+
return keys;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// --- Main Config Validator ---
|
|
330
|
+
|
|
331
|
+
export class ConfigValidator {
|
|
332
|
+
private client: TreeSitterClient;
|
|
333
|
+
private availableKeys: Map<string, ConfigKey[]> = new Map();
|
|
334
|
+
|
|
335
|
+
constructor() {
|
|
336
|
+
this.client = new TreeSitterClient();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async init(): Promise<void> {
|
|
340
|
+
await this.client.init();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Scan project for config files
|
|
345
|
+
*/
|
|
346
|
+
async scanConfigFiles(cwd: string): Promise<void> {
|
|
347
|
+
const configFiles = [
|
|
348
|
+
{ pattern: ".env", parser: parseEnvFile },
|
|
349
|
+
{ pattern: ".env.local", parser: parseEnvFile },
|
|
350
|
+
{ pattern: ".env.development", parser: parseEnvFile },
|
|
351
|
+
{ pattern: ".env.production", parser: parseEnvFile },
|
|
352
|
+
{ pattern: "config.ini", parser: parseIniFile },
|
|
353
|
+
{ pattern: "config.yaml", parser: parseYamlConfig },
|
|
354
|
+
{ pattern: "config.yml", parser: parseYamlConfig },
|
|
355
|
+
{ pattern: "config.json", parser: parseJsonConfig },
|
|
356
|
+
{ pattern: "pyproject.toml", parser: parseIniFile }, // Simplified
|
|
357
|
+
{ pattern: "package.json", parser: parseJsonConfig },
|
|
358
|
+
{ pattern: "app.yaml", parser: parseYamlConfig },
|
|
359
|
+
{ pattern: "application.yaml", parser: parseYamlConfig },
|
|
360
|
+
];
|
|
361
|
+
|
|
362
|
+
for (const { pattern, parser } of configFiles) {
|
|
363
|
+
const filePath = path.join(cwd, pattern);
|
|
364
|
+
const keys = await parser(filePath);
|
|
365
|
+
if (keys.length > 0) {
|
|
366
|
+
this.availableKeys.set(pattern, keys);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Also scan for any .env.* files
|
|
371
|
+
try {
|
|
372
|
+
const entries = await fs.readdir(cwd);
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
if (entry.startsWith(".env.")) {
|
|
375
|
+
const filePath = path.join(cwd, entry);
|
|
376
|
+
const keys = await parseEnvFile(filePath);
|
|
377
|
+
if (keys.length > 0) {
|
|
378
|
+
this.availableKeys.set(entry, keys);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
} catch {
|
|
383
|
+
// Can't read directory
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Validate config access in a source file
|
|
389
|
+
*/
|
|
390
|
+
async validateFile(filePath: string): Promise<ConfigValidationResult> {
|
|
391
|
+
const languageId = this.getLanguageId(filePath);
|
|
392
|
+
if (!languageId || !CONFIG_QUERIES[languageId]) {
|
|
393
|
+
return { undefined: [], typos: [], available: [] };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Get all config accesses in the file
|
|
397
|
+
const accesses = await this.findConfigAccesses(filePath, languageId);
|
|
398
|
+
|
|
399
|
+
// Get all available keys
|
|
400
|
+
const allAvailable: ConfigKey[] = [];
|
|
401
|
+
for (const keys of this.availableKeys.values()) {
|
|
402
|
+
allAvailable.push(...keys);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const undefined: ConfigAccess[] = [];
|
|
406
|
+
const typos: Array<{ access: ConfigAccess; suggestion: string }> = [];
|
|
407
|
+
|
|
408
|
+
for (const access of accesses) {
|
|
409
|
+
// Check if key exists
|
|
410
|
+
const exactMatch = allAvailable.find(
|
|
411
|
+
(k) => k.key.toLowerCase() === access.key.toLowerCase(),
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
if (!exactMatch) {
|
|
415
|
+
// Check for typos using fuzzy matching
|
|
416
|
+
const suggestion = this.findClosestMatch(
|
|
417
|
+
access.key,
|
|
418
|
+
allAvailable.map((k) => k.key),
|
|
419
|
+
);
|
|
420
|
+
if (
|
|
421
|
+
suggestion &&
|
|
422
|
+
this.calculateSimilarity(access.key, suggestion) > 0.7
|
|
423
|
+
) {
|
|
424
|
+
typos.push({ access, suggestion });
|
|
425
|
+
} else {
|
|
426
|
+
undefined.push(access);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return { undefined, typos, available: allAvailable };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Find all config accesses in a file using tree-sitter
|
|
436
|
+
*/
|
|
437
|
+
private async findConfigAccesses(
|
|
438
|
+
filePath: string,
|
|
439
|
+
languageId: string,
|
|
440
|
+
): Promise<ConfigAccess[]> {
|
|
441
|
+
const query = CONFIG_QUERIES[languageId];
|
|
442
|
+
const matches = await this.client.structuralSearch(
|
|
443
|
+
query,
|
|
444
|
+
languageId,
|
|
445
|
+
path.dirname(filePath),
|
|
446
|
+
{ fileFilter: (f) => f === filePath },
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
const accesses: ConfigAccess[] = [];
|
|
450
|
+
|
|
451
|
+
for (const match of matches) {
|
|
452
|
+
const configKeyCapture =
|
|
453
|
+
match.captures.config_key || match.captures.env_var;
|
|
454
|
+
if (configKeyCapture) {
|
|
455
|
+
// Clean up the key (remove quotes, etc.)
|
|
456
|
+
const key = configKeyCapture.replace(/^["'`]|["'`]$/g, "");
|
|
457
|
+
|
|
458
|
+
accesses.push({
|
|
459
|
+
key,
|
|
460
|
+
file: filePath,
|
|
461
|
+
line: match.line,
|
|
462
|
+
column: match.column,
|
|
463
|
+
pattern: match.matchedText,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return accesses;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Calculate string similarity (Levenshtein-based)
|
|
473
|
+
*/
|
|
474
|
+
private calculateSimilarity(a: string, b: string): number {
|
|
475
|
+
const matrix: number[][] = [];
|
|
476
|
+
|
|
477
|
+
for (let i = 0; i <= b.length; i++) {
|
|
478
|
+
matrix[i] = [i];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
for (let j = 0; j <= a.length; j++) {
|
|
482
|
+
matrix[0][j] = j;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
for (let i = 1; i <= b.length; i++) {
|
|
486
|
+
for (let j = 1; j <= a.length; j++) {
|
|
487
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
488
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
489
|
+
} else {
|
|
490
|
+
matrix[i][j] = Math.min(
|
|
491
|
+
matrix[i - 1][j - 1] + 1, // substitution
|
|
492
|
+
matrix[i][j - 1] + 1, // insertion
|
|
493
|
+
matrix[i - 1][j] + 1, // deletion
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const distance = matrix[b.length][a.length];
|
|
500
|
+
const maxLength = Math.max(a.length, b.length);
|
|
501
|
+
return 1 - distance / maxLength;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Find the closest matching key
|
|
506
|
+
*/
|
|
507
|
+
private findClosestMatch(
|
|
508
|
+
key: string,
|
|
509
|
+
candidates: string[],
|
|
510
|
+
): string | undefined {
|
|
511
|
+
let bestMatch: string | undefined;
|
|
512
|
+
let bestScore = 0;
|
|
513
|
+
|
|
514
|
+
for (const candidate of candidates) {
|
|
515
|
+
const score = this.calculateSimilarity(key, candidate);
|
|
516
|
+
if (score > bestScore && score > 0.5) {
|
|
517
|
+
bestScore = score;
|
|
518
|
+
bestMatch = candidate;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return bestMatch;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Map file extension to language ID
|
|
527
|
+
*/
|
|
528
|
+
private getLanguageId(filePath: string): string | undefined {
|
|
529
|
+
const ext = path.extname(filePath);
|
|
530
|
+
switch (ext) {
|
|
531
|
+
case ".py":
|
|
532
|
+
return "python";
|
|
533
|
+
case ".js":
|
|
534
|
+
return "javascript";
|
|
535
|
+
case ".ts":
|
|
536
|
+
return "typescript";
|
|
537
|
+
case ".tsx":
|
|
538
|
+
return "tsx";
|
|
539
|
+
case ".go":
|
|
540
|
+
return "go";
|
|
541
|
+
case ".rs":
|
|
542
|
+
return "rust";
|
|
543
|
+
default:
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// --- Simple factory function ---
|
|
550
|
+
|
|
551
|
+
export async function createConfigValidator(
|
|
552
|
+
cwd: string,
|
|
553
|
+
): Promise<ConfigValidator> {
|
|
554
|
+
const validator = new ConfigValidator();
|
|
555
|
+
await validator.init();
|
|
556
|
+
await validator.scanConfigFiles(cwd);
|
|
557
|
+
return validator;
|
|
558
|
+
}
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* Requires: npm install -D madge
|
|
9
9
|
* Docs: https://github.com/pahen/madge
|
|
10
10
|
*/
|
|
11
|
-
import { spawnSync } from "node:child_process";
|
|
12
11
|
import * as fs from "node:fs";
|
|
13
12
|
import * as path from "node:path";
|
|
13
|
+
import { safeSpawn } from "./safe-spawn.js";
|
|
14
14
|
// --- Client ---
|
|
15
15
|
export class DependencyChecker {
|
|
16
16
|
constructor(verbose = false) {
|
|
@@ -31,10 +31,8 @@ export class DependencyChecker {
|
|
|
31
31
|
isAvailable() {
|
|
32
32
|
if (this.available !== null)
|
|
33
33
|
return this.available;
|
|
34
|
-
const result =
|
|
35
|
-
encoding: "utf-8",
|
|
34
|
+
const result = safeSpawn("npx", ["madge", "--version"], {
|
|
36
35
|
timeout: 5000,
|
|
37
|
-
shell: true,
|
|
38
36
|
});
|
|
39
37
|
this.available = !result.error && result.status === 0;
|
|
40
38
|
if (this.available) {
|
|
@@ -164,7 +162,7 @@ export class DependencyChecker {
|
|
|
164
162
|
this.log(`Imports changed for ${path.basename(filePath)}, checking dependencies...`);
|
|
165
163
|
// Run madge on the specific file (fast)
|
|
166
164
|
try {
|
|
167
|
-
const result =
|
|
165
|
+
const result = safeSpawn("npx", [
|
|
168
166
|
"madge",
|
|
169
167
|
"--circular",
|
|
170
168
|
"--extensions",
|
|
@@ -172,10 +170,8 @@ export class DependencyChecker {
|
|
|
172
170
|
"--json",
|
|
173
171
|
normalized,
|
|
174
172
|
], {
|
|
175
|
-
encoding: "utf-8",
|
|
176
173
|
timeout: 15000,
|
|
177
174
|
cwd: projectRoot,
|
|
178
|
-
shell: true,
|
|
179
175
|
});
|
|
180
176
|
const output = result.stdout || "[]";
|
|
181
177
|
const parsed = JSON.parse(output);
|
|
@@ -243,7 +239,7 @@ export class DependencyChecker {
|
|
|
243
239
|
return { circular: [], count: 0 };
|
|
244
240
|
}
|
|
245
241
|
try {
|
|
246
|
-
const result =
|
|
242
|
+
const result = safeSpawn("npx", [
|
|
247
243
|
"madge",
|
|
248
244
|
"--circular",
|
|
249
245
|
"--extensions",
|
|
@@ -251,10 +247,8 @@ export class DependencyChecker {
|
|
|
251
247
|
"--json",
|
|
252
248
|
projectRoot,
|
|
253
249
|
], {
|
|
254
|
-
encoding: "utf-8",
|
|
255
250
|
timeout: 30000,
|
|
256
251
|
cwd: projectRoot,
|
|
257
|
-
shell: true,
|
|
258
252
|
});
|
|
259
253
|
const output = result.stdout || "{}";
|
|
260
254
|
const data = JSON.parse(output);
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* Docs: https://github.com/pahen/madge
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { spawnSync } from "node:child_process";
|
|
13
12
|
import * as fs from "node:fs";
|
|
14
13
|
import * as path from "node:path";
|
|
14
|
+
import { safeSpawn } from "./safe-spawn.js";
|
|
15
15
|
|
|
16
16
|
// --- Types ---
|
|
17
17
|
|
|
@@ -61,10 +61,8 @@ export class DependencyChecker {
|
|
|
61
61
|
isAvailable(): boolean {
|
|
62
62
|
if (this.available !== null) return this.available;
|
|
63
63
|
|
|
64
|
-
const result =
|
|
65
|
-
encoding: "utf-8",
|
|
64
|
+
const result = safeSpawn("npx", ["madge", "--version"], {
|
|
66
65
|
timeout: 5000,
|
|
67
|
-
shell: true,
|
|
68
66
|
});
|
|
69
67
|
|
|
70
68
|
this.available = !result.error && result.status === 0;
|
|
@@ -223,7 +221,7 @@ export class DependencyChecker {
|
|
|
223
221
|
|
|
224
222
|
// Run madge on the specific file (fast)
|
|
225
223
|
try {
|
|
226
|
-
const result =
|
|
224
|
+
const result = safeSpawn(
|
|
227
225
|
"npx",
|
|
228
226
|
[
|
|
229
227
|
"madge",
|
|
@@ -234,10 +232,8 @@ export class DependencyChecker {
|
|
|
234
232
|
normalized,
|
|
235
233
|
],
|
|
236
234
|
{
|
|
237
|
-
encoding: "utf-8",
|
|
238
235
|
timeout: 15000,
|
|
239
236
|
cwd: projectRoot,
|
|
240
|
-
shell: true,
|
|
241
237
|
},
|
|
242
238
|
);
|
|
243
239
|
|
|
@@ -323,7 +319,7 @@ export class DependencyChecker {
|
|
|
323
319
|
}
|
|
324
320
|
|
|
325
321
|
try {
|
|
326
|
-
const result =
|
|
322
|
+
const result = safeSpawn(
|
|
327
323
|
"npx",
|
|
328
324
|
[
|
|
329
325
|
"madge",
|
|
@@ -334,10 +330,8 @@ export class DependencyChecker {
|
|
|
334
330
|
projectRoot,
|
|
335
331
|
],
|
|
336
332
|
{
|
|
337
|
-
encoding: "utf-8",
|
|
338
333
|
timeout: 30000,
|
|
339
334
|
cwd: projectRoot,
|
|
340
|
-
shell: true,
|
|
341
335
|
},
|
|
342
336
|
);
|
|
343
337
|
|