pi-lens 3.6.3 → 3.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/package.json +3 -3
  2. package/tsconfig.json +1 -1
  3. package/clients/__tests__/file-time.test.js +0 -216
  4. package/clients/__tests__/file-time.test.ts +0 -276
  5. package/clients/__tests__/format-service.test.js +0 -245
  6. package/clients/__tests__/format-service.test.ts +0 -339
  7. package/clients/__tests__/formatters.test.js +0 -271
  8. package/clients/__tests__/formatters.test.ts +0 -401
  9. package/clients/agent-behavior-client.js +0 -110
  10. package/clients/agent-behavior-client.test.js +0 -94
  11. package/clients/agent-behavior-client.test.ts +0 -116
  12. package/clients/amain-types.js +0 -164
  13. package/clients/architect-client.js +0 -291
  14. package/clients/ast-grep-client.js +0 -253
  15. package/clients/ast-grep-parser.js +0 -84
  16. package/clients/ast-grep-rule-manager.js +0 -89
  17. package/clients/ast-grep-types.js +0 -9
  18. package/clients/auto-loop.js +0 -131
  19. package/clients/biome-client.js +0 -420
  20. package/clients/biome-client.test.js +0 -144
  21. package/clients/biome-client.test.ts +0 -163
  22. package/clients/cache/rule-cache.js +0 -72
  23. package/clients/cache-manager.js +0 -245
  24. package/clients/cache-manager.test.js +0 -197
  25. package/clients/cache-manager.test.ts +0 -299
  26. package/clients/complexity-client.js +0 -675
  27. package/clients/complexity-client.test.js +0 -234
  28. package/clients/complexity-client.test.ts +0 -255
  29. package/clients/config-validator.js +0 -465
  30. package/clients/dependency-checker.js +0 -325
  31. package/clients/dependency-checker.test.js +0 -60
  32. package/clients/dependency-checker.test.ts +0 -71
  33. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  34. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  35. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  36. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  37. package/clients/dispatch/debug.log +0 -1
  38. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  39. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  40. package/clients/dispatch/dispatcher.format.test.js +0 -46
  41. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  42. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  43. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  44. package/clients/dispatch/dispatcher.js +0 -381
  45. package/clients/dispatch/dispatcher.test.js +0 -116
  46. package/clients/dispatch/dispatcher.test.ts +0 -149
  47. package/clients/dispatch/integration.js +0 -108
  48. package/clients/dispatch/plan.js +0 -183
  49. package/clients/dispatch/runners/architect.js +0 -83
  50. package/clients/dispatch/runners/architect.test.js +0 -138
  51. package/clients/dispatch/runners/architect.test.ts +0 -162
  52. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  53. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  54. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  55. package/clients/dispatch/runners/ast-grep.js +0 -157
  56. package/clients/dispatch/runners/biome.js +0 -55
  57. package/clients/dispatch/runners/config-validation.js +0 -67
  58. package/clients/dispatch/runners/go-vet.js +0 -48
  59. package/clients/dispatch/runners/index.js +0 -47
  60. package/clients/dispatch/runners/lsp.js +0 -102
  61. package/clients/dispatch/runners/oxlint.js +0 -67
  62. package/clients/dispatch/runners/oxlint.test.js +0 -230
  63. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  64. package/clients/dispatch/runners/pyright.js +0 -100
  65. package/clients/dispatch/runners/pyright.test.js +0 -98
  66. package/clients/dispatch/runners/pyright.test.ts +0 -121
  67. package/clients/dispatch/runners/python-slop.js +0 -97
  68. package/clients/dispatch/runners/python-slop.test.js +0 -203
  69. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  70. package/clients/dispatch/runners/ruff.js +0 -48
  71. package/clients/dispatch/runners/rust-clippy.js +0 -102
  72. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  73. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  74. package/clients/dispatch/runners/shellcheck.js +0 -147
  75. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  76. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  77. package/clients/dispatch/runners/similarity.js +0 -230
  78. package/clients/dispatch/runners/spellcheck.js +0 -106
  79. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  80. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  81. package/clients/dispatch/runners/tree-sitter.js +0 -246
  82. package/clients/dispatch/runners/ts-lsp.js +0 -125
  83. package/clients/dispatch/runners/ts-slop.js +0 -113
  84. package/clients/dispatch/runners/type-safety.js +0 -142
  85. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  86. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  87. package/clients/dispatch/runners/utils.js +0 -51
  88. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  89. package/clients/dispatch/types.js +0 -16
  90. package/clients/dispatch/utils/format-utils.js +0 -44
  91. package/clients/dogfood.test.js +0 -201
  92. package/clients/dogfood.test.ts +0 -269
  93. package/clients/file-kinds.js +0 -177
  94. package/clients/file-kinds.test.js +0 -169
  95. package/clients/file-kinds.test.ts +0 -210
  96. package/clients/file-time.js +0 -152
  97. package/clients/file-utils.js +0 -40
  98. package/clients/fix-scanners.js +0 -204
  99. package/clients/format-service.js +0 -184
  100. package/clients/formatters.js +0 -488
  101. package/clients/go-client.js +0 -203
  102. package/clients/go-client.test.js +0 -127
  103. package/clients/go-client.test.ts +0 -143
  104. package/clients/installer/index.js +0 -403
  105. package/clients/interviewer-templates.js +0 -75
  106. package/clients/interviewer.js +0 -173
  107. package/clients/jscpd-client.js +0 -196
  108. package/clients/jscpd-client.test.js +0 -127
  109. package/clients/jscpd-client.test.ts +0 -145
  110. package/clients/knip-client.js +0 -239
  111. package/clients/knip-client.test.js +0 -112
  112. package/clients/knip-client.test.ts +0 -128
  113. package/clients/latency-logger.js +0 -40
  114. package/clients/lsp/__tests__/client.test.js +0 -310
  115. package/clients/lsp/__tests__/client.test.ts +0 -412
  116. package/clients/lsp/__tests__/config.test.js +0 -167
  117. package/clients/lsp/__tests__/config.test.ts +0 -217
  118. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  119. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  120. package/clients/lsp/__tests__/integration.test.js +0 -127
  121. package/clients/lsp/__tests__/integration.test.ts +0 -160
  122. package/clients/lsp/__tests__/launch.test.js +0 -313
  123. package/clients/lsp/__tests__/launch.test.ts +0 -394
  124. package/clients/lsp/__tests__/server.test.js +0 -259
  125. package/clients/lsp/__tests__/server.test.ts +0 -332
  126. package/clients/lsp/__tests__/service.test.js +0 -438
  127. package/clients/lsp/__tests__/service.test.ts +0 -530
  128. package/clients/lsp/client.js +0 -350
  129. package/clients/lsp/config.js +0 -112
  130. package/clients/lsp/index.js +0 -318
  131. package/clients/lsp/installer/index.js +0 -391
  132. package/clients/lsp/interactive-install.js +0 -221
  133. package/clients/lsp/language.js +0 -170
  134. package/clients/lsp/launch.js +0 -329
  135. package/clients/lsp/lsp/launch.js +0 -116
  136. package/clients/lsp/lsp/server.js +0 -532
  137. package/clients/lsp/lsp-index.js +0 -10
  138. package/clients/lsp/path-utils.js +0 -5
  139. package/clients/lsp/server.js +0 -725
  140. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  141. package/clients/lsp/test-py-spawn/test.py +0 -3
  142. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  143. package/clients/lsp/test-py-svc/test.py +0 -3
  144. package/clients/lsp/test-python-project/requirements.txt +0 -1
  145. package/clients/lsp/test-python-project/test.py +0 -5
  146. package/clients/metrics-client.js +0 -107
  147. package/clients/metrics-client.test.js +0 -128
  148. package/clients/metrics-client.test.ts +0 -163
  149. package/clients/metrics-history.js +0 -367
  150. package/clients/path-utils.js +0 -142
  151. package/clients/pipeline.js +0 -272
  152. package/clients/production-readiness.js +0 -522
  153. package/clients/project-index.js +0 -255
  154. package/clients/project-metadata.js +0 -531
  155. package/clients/ruff-client.js +0 -325
  156. package/clients/ruff-client.test.js +0 -132
  157. package/clients/ruff-client.test.ts +0 -153
  158. package/clients/rules-scanner.js +0 -97
  159. package/clients/runner-tracker.js +0 -152
  160. package/clients/rust-client.js +0 -205
  161. package/clients/rust-client.test.js +0 -108
  162. package/clients/rust-client.test.ts +0 -130
  163. package/clients/safe-spawn-async.js +0 -163
  164. package/clients/safe-spawn.js +0 -241
  165. package/clients/sanitize.js +0 -291
  166. package/clients/sanitize.test.js +0 -177
  167. package/clients/sanitize.test.ts +0 -223
  168. package/clients/scan-architectural-debt.js +0 -167
  169. package/clients/scan-utils.js +0 -83
  170. package/clients/secrets-scanner.js +0 -119
  171. package/clients/secrets-scanner.test.js +0 -100
  172. package/clients/secrets-scanner.test.ts +0 -113
  173. package/clients/sg-runner.js +0 -292
  174. package/clients/state-matrix.js +0 -160
  175. package/clients/subprocess-client.js +0 -65
  176. package/clients/symbol-types.js +0 -5
  177. package/clients/test-runner-client.js +0 -523
  178. package/clients/test-runner-client.test.js +0 -192
  179. package/clients/test-runner-client.test.ts +0 -253
  180. package/clients/test-utils.js +0 -27
  181. package/clients/test-utils.ts +0 -36
  182. package/clients/todo-scanner.js +0 -200
  183. package/clients/todo-scanner.test.js +0 -301
  184. package/clients/todo-scanner.test.ts +0 -352
  185. package/clients/tool-availability.js +0 -207
  186. package/clients/tree-sitter-client.js +0 -601
  187. package/clients/tree-sitter-query-loader.js +0 -355
  188. package/clients/tree-sitter-symbol-extractor.js +0 -289
  189. package/clients/ts-service.js +0 -129
  190. package/clients/type-coverage-client.js +0 -127
  191. package/clients/type-coverage-client.test.js +0 -105
  192. package/clients/type-coverage-client.test.ts +0 -125
  193. package/clients/type-safety-client.js +0 -138
  194. package/clients/types.js +0 -11
  195. package/clients/typescript-client.codefix.test.js +0 -157
  196. package/clients/typescript-client.codefix.test.ts +0 -186
  197. package/clients/typescript-client.js +0 -509
  198. package/clients/typescript-client.test.js +0 -105
  199. package/clients/typescript-client.test.ts +0 -126
  200. package/commands/booboo.js +0 -1007
  201. package/commands/fix-from-booboo.js +0 -398
  202. package/commands/fix-simplified.js +0 -618
  203. package/commands/rate.js +0 -281
  204. package/commands/rate.test.js +0 -119
  205. package/commands/rate.test.ts +0 -131
  206. package/commands/refactor.js +0 -130
@@ -1,405 +0,0 @@
1
- /**
2
- * ast-grep NAPI runner for dispatch system
3
- *
4
- * Uses @ast-grep/napi for programmatic parsing instead of CLI.
5
- * Handles TypeScript/JavaScript/CSS/HTML files with YAML rule support.
6
- *
7
- * Replaces CLI-based runners for faster performance (100x speedup).
8
- */
9
- import * as fs from "node:fs";
10
- import * as path from "node:path";
11
- import { calculateRuleComplexity, hasUnsupportedConditions, isOverlyBroadPattern, isStructuredRule, loadYamlRules, MAX_BLOCKING_RULE_COMPLEXITY, } from "./yaml-rule-parser.js";
12
- // Lazy load the napi package
13
- let sg;
14
- let sgLoadAttempted = false;
15
- async function loadSg() {
16
- if (sg)
17
- return sg;
18
- if (sgLoadAttempted)
19
- return undefined; // Don't retry if already failed
20
- sgLoadAttempted = true;
21
- try {
22
- sg = await import("@ast-grep/napi");
23
- return sg;
24
- }
25
- catch {
26
- return undefined;
27
- }
28
- }
29
- // Supported extensions for NAPI
30
- const SUPPORTED_EXTS = [".ts", ".tsx", ".js", ".jsx", ".css", ".html", ".htm"];
31
- /** Maximum matches per rule to prevent excessive false positives */
32
- const MAX_MATCHES_PER_RULE = 10;
33
- /** Maximum total diagnostics per file to prevent output spam */
34
- const MAX_TOTAL_DIAGNOSTICS = 50;
35
- /** Rules already covered by tree-sitter runner (priority 14, runs first) */
36
- const TREE_SITTER_OVERLAP = new Set([
37
- "constructor-super",
38
- "empty-catch",
39
- "long-parameter-list",
40
- "nested-ternary",
41
- "no-dupe-class-members",
42
- ]);
43
- /** Maximum AST depth to traverse to prevent stack overflow on deeply nested files */
44
- const MAX_AST_DEPTH = 50;
45
- /** Maximum recursion depth for structured rule execution */
46
- const MAX_RULE_DEPTH = 5;
47
- function canHandle(filePath) {
48
- return SUPPORTED_EXTS.includes(path.extname(filePath).toLowerCase());
49
- }
50
- function getLang(filePath, sgModule) {
51
- const ext = path.extname(filePath).toLowerCase();
52
- switch (ext) {
53
- case ".ts":
54
- return sgModule.ts;
55
- case ".tsx":
56
- return sgModule.tsx;
57
- case ".js":
58
- case ".jsx":
59
- return sgModule.js;
60
- case ".css":
61
- return sgModule.css;
62
- case ".html":
63
- case ".htm":
64
- return sgModule.html;
65
- default:
66
- return undefined;
67
- }
68
- }
69
- /**
70
- * Check if a single node matches a condition (without searching descendants).
71
- * In ast-grep semantics:
72
- * - pattern/kind/regex: check the node itself
73
- * - all: node must match ALL sub-conditions
74
- * - any: node must match at least ONE sub-condition
75
- * - not: node must NOT match the sub-condition
76
- * - has: node must have a DESCENDANT matching the sub-condition
77
- */
78
- function nodeMatchesCondition(node, condition, depth = 0) {
79
- if (depth > MAX_RULE_DEPTH)
80
- return false;
81
- // Check kind constraint
82
- if (condition.kind && node.kind() !== condition.kind)
83
- return false;
84
- // Check pattern constraint (node itself must match)
85
- if (condition.pattern) {
86
- try {
87
- const matches = node.findAll(condition.pattern);
88
- // Check if the node itself is among the matches (same start position)
89
- const nodeRange = node.range();
90
- let selfMatch = false;
91
- for (const m of matches) {
92
- const mr = m.range();
93
- if (mr.start.line === nodeRange.start.line &&
94
- mr.start.column === nodeRange.start.column &&
95
- mr.end.line === nodeRange.end.line &&
96
- mr.end.column === nodeRange.end.column) {
97
- selfMatch = true;
98
- break;
99
- }
100
- }
101
- if (!selfMatch)
102
- return false;
103
- }
104
- catch {
105
- return false;
106
- }
107
- }
108
- // Check regex constraint
109
- if (condition.regex) {
110
- try {
111
- const text = node.text();
112
- if (!new RegExp(condition.regex).test(text))
113
- return false;
114
- }
115
- catch {
116
- return false;
117
- }
118
- }
119
- // Check has (descendant must match)
120
- if (condition.has) {
121
- const descendants = findMatchingNodes(node, condition.has, depth + 1);
122
- if (descendants.length === 0)
123
- return false;
124
- }
125
- // Check not (node must NOT match this condition)
126
- if (condition.not) {
127
- if (nodeMatchesCondition(node, condition.not, depth + 1))
128
- return false;
129
- }
130
- // Check all (node must match ALL sub-conditions)
131
- if (condition.all) {
132
- for (const sub of condition.all) {
133
- if (!nodeMatchesCondition(node, sub, depth + 1))
134
- return false;
135
- }
136
- }
137
- // Check any (node must match at least one sub-condition)
138
- if (condition.any) {
139
- let anyMatch = false;
140
- for (const sub of condition.any) {
141
- if (nodeMatchesCondition(node, sub, depth + 1)) {
142
- anyMatch = true;
143
- break;
144
- }
145
- }
146
- if (!anyMatch)
147
- return false;
148
- }
149
- return true;
150
- }
151
- /**
152
- * Find all nodes in the tree that match a condition.
153
- * This is the "search" function - traverses the tree and checks each node.
154
- */
155
- function findMatchingNodes(rootNode, condition, depth = 0) {
156
- if (depth > MAX_RULE_DEPTH)
157
- return [];
158
- const matches = [];
159
- // Optimization: if the condition has a kind, only check nodes of that kind
160
- // If it has a pattern, use findAll for initial candidates
161
- let candidates;
162
- if (condition.pattern && !condition.all && !condition.any) {
163
- // Use findAll for pattern-only conditions (fast path)
164
- try {
165
- candidates = rootNode.findAll(condition.pattern);
166
- }
167
- catch {
168
- return [];
169
- }
170
- }
171
- else if (condition.kind && !condition.all && !condition.any) {
172
- // Use findByKind for kind-only conditions (fast path)
173
- candidates = findByKind(rootNode, condition.kind, 0);
174
- }
175
- else if (condition.all) {
176
- // For `all`, find the narrowest sub-condition to generate candidates
177
- candidates = getCandidatesForAll(rootNode, condition.all);
178
- }
179
- else if (condition.any) {
180
- // For `any`, union candidates from all sub-conditions
181
- const seen = new Set();
182
- candidates = [];
183
- for (const sub of condition.any) {
184
- const subMatches = findMatchingNodes(rootNode, sub, depth + 1);
185
- for (const m of subMatches) {
186
- const r = m.range();
187
- const key = `${r.start.line}:${r.start.column}`;
188
- if (!seen.has(key)) {
189
- seen.add(key);
190
- candidates.push(m);
191
- }
192
- }
193
- }
194
- }
195
- else {
196
- // Fallback: traverse all nodes
197
- candidates = getAllNodes(rootNode, 0);
198
- }
199
- for (const candidate of candidates) {
200
- if (nodeMatchesCondition(candidate, condition, depth)) {
201
- matches.push(candidate);
202
- }
203
- }
204
- return matches;
205
- }
206
- /**
207
- * For an `all` condition, find the narrowest sub-condition to generate
208
- * initial candidates. This avoids scanning all nodes when one sub-condition
209
- * has a specific kind or pattern.
210
- */
211
- function getCandidatesForAll(rootNode, subs) {
212
- // Prefer kind-based narrowing first, then pattern-based
213
- for (const sub of subs) {
214
- if (sub.kind) {
215
- return findByKind(rootNode, sub.kind, 0);
216
- }
217
- }
218
- for (const sub of subs) {
219
- if (sub.pattern) {
220
- try {
221
- return rootNode.findAll(sub.pattern);
222
- }
223
- catch { }
224
- }
225
- }
226
- // No narrowing possible, scan all
227
- return getAllNodes(rootNode, 0);
228
- }
229
- /**
230
- * Legacy wrapper - execute a structured rule using the new two-phase approach.
231
- */
232
- function executeStructuredRule(rootNode, condition, matches = [], depth = 0) {
233
- return findMatchingNodes(rootNode, condition, depth);
234
- }
235
- /**
236
- * Find all nodes of a specific kind with depth limit
237
- */
238
- function findByKind(node, kind, currentDepth) {
239
- if (currentDepth > MAX_AST_DEPTH)
240
- return [];
241
- const results = [];
242
- if (node.kind() === kind)
243
- results.push(node);
244
- for (const child of node.children()) {
245
- results.push(...findByKind(child, kind, currentDepth + 1));
246
- }
247
- return results;
248
- }
249
- /**
250
- * Get all nodes with depth limit to prevent stack overflow
251
- */
252
- function getAllNodes(node, currentDepth) {
253
- if (currentDepth > MAX_AST_DEPTH)
254
- return [];
255
- const results = [node];
256
- for (const child of node.children()) {
257
- results.push(...getAllNodes(child, currentDepth + 1));
258
- }
259
- return results;
260
- }
261
- // --- Runner Definition ---
262
- const astGrepNapiRunner = {
263
- id: "ast-grep-napi",
264
- appliesTo: ["jsts"],
265
- priority: 15,
266
- enabledByDefault: true,
267
- skipTestFiles: true,
268
- async run(ctx) {
269
- if (!canHandle(ctx.filePath)) {
270
- return { status: "skipped", diagnostics: [], semantic: "none" };
271
- }
272
- const sgModule = await loadSg();
273
- if (!sgModule) {
274
- return { status: "skipped", diagnostics: [], semantic: "none" };
275
- }
276
- if (!fs.existsSync(ctx.filePath)) {
277
- return { status: "skipped", diagnostics: [], semantic: "none" };
278
- }
279
- const lang = getLang(ctx.filePath, sgModule);
280
- if (!lang) {
281
- return { status: "skipped", diagnostics: [], semantic: "none" };
282
- }
283
- const stats = fs.statSync(ctx.filePath);
284
- if (stats.size > 1024 * 1024) {
285
- return { status: "skipped", diagnostics: [], semantic: "none" };
286
- }
287
- let content;
288
- try {
289
- content = fs.readFileSync(ctx.filePath, "utf-8");
290
- }
291
- catch {
292
- return { status: "skipped", diagnostics: [], semantic: "none" };
293
- }
294
- let root;
295
- try {
296
- root = lang.parse(content);
297
- }
298
- catch {
299
- return { status: "skipped", diagnostics: [], semantic: "none" };
300
- }
301
- let rootNode;
302
- try {
303
- rootNode = root.root();
304
- }
305
- catch {
306
- return { status: "skipped", diagnostics: [], semantic: "none" };
307
- }
308
- const diagnostics = [];
309
- const ruleDirs = [
310
- path.join(process.cwd(), "rules/ast-grep-rules/rules"),
311
- path.join(process.cwd(), "rules/ast-grep-rules"),
312
- ];
313
- for (const ruleDir of ruleDirs) {
314
- let rules;
315
- try {
316
- rules = loadYamlRules(ruleDir, ctx.blockingOnly ? "error" : undefined);
317
- }
318
- catch {
319
- continue;
320
- }
321
- for (const rule of rules) {
322
- // Skip rules already handled by tree-sitter runner (priority 14)
323
- if (TREE_SITTER_OVERLAP.has(rule.id))
324
- continue;
325
- // Skip rules using conditions we can't execute (inside, follows,
326
- // precedes, stopBy, field, nthChild, constraints). Running these
327
- // with only partial condition evaluation causes false positives.
328
- if (hasUnsupportedConditions(rule))
329
- continue;
330
- // Skip rules whose top-level pattern is overly broad ($NAME, $X, etc.)
331
- // without additional structural constraints to narrow matches.
332
- if (rule.rule &&
333
- isOverlyBroadPattern(rule.rule.pattern) &&
334
- !isStructuredRule(rule)) {
335
- continue;
336
- }
337
- const lang = rule.language?.toLowerCase();
338
- if (lang && lang !== "typescript" && lang !== "javascript") {
339
- continue;
340
- }
341
- if (ctx.blockingOnly && rule.rule) {
342
- const complexity = calculateRuleComplexity(rule.rule);
343
- if (complexity > MAX_BLOCKING_RULE_COMPLEXITY) {
344
- continue;
345
- }
346
- }
347
- try {
348
- let matches = [];
349
- if (isStructuredRule(rule) && rule.rule) {
350
- matches = executeStructuredRule(rootNode, rule.rule, []);
351
- }
352
- else if (rule.rule?.pattern || rule.rule?.kind) {
353
- const pattern = rule.rule.pattern || rule.rule.kind;
354
- if (pattern) {
355
- try {
356
- matches = rootNode.findAll(pattern);
357
- }
358
- catch {
359
- if (rule.rule.kind) {
360
- matches = findByKind(rootNode, rule.rule.kind, 0);
361
- }
362
- }
363
- }
364
- }
365
- const limitedMatches = matches.slice(0, MAX_MATCHES_PER_RULE);
366
- for (const match of limitedMatches) {
367
- if (diagnostics.length >= MAX_TOTAL_DIAGNOSTICS)
368
- break;
369
- const node = match;
370
- const range = node.range();
371
- const severity = rule.severity === "error" ? "error" : "warning";
372
- diagnostics.push({
373
- id: `ast-grep-napi-${range.start.line}-${rule.id}`,
374
- message: `[${rule.metadata?.category || "slop"}] ${rule.message || rule.id}`,
375
- filePath: ctx.filePath,
376
- line: range.start.line + 1,
377
- column: range.start.column + 1,
378
- severity,
379
- semantic: severity === "error" ? "blocking" : "warning",
380
- tool: "ast-grep-napi",
381
- rule: rule.id,
382
- fixable: false,
383
- });
384
- }
385
- if (diagnostics.length >= MAX_TOTAL_DIAGNOSTICS)
386
- break;
387
- }
388
- catch {
389
- // Rule failed, skip
390
- }
391
- }
392
- }
393
- const hasBlocking = diagnostics.some((d) => d.semantic === "blocking");
394
- return {
395
- status: "succeeded",
396
- diagnostics,
397
- semantic: hasBlocking
398
- ? "blocking"
399
- : diagnostics.length > 0
400
- ? "warning"
401
- : "none",
402
- };
403
- },
404
- };
405
- export default astGrepNapiRunner;
@@ -1,107 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { describe, expect, it } from "vitest";
4
- function createMockContext(filePath, kind = "jsts") {
5
- return {
6
- filePath,
7
- cwd: process.cwd(),
8
- kind,
9
- autofix: false,
10
- deltaMode: false,
11
- baselines: { get: () => [], add: () => { }, save: () => { } },
12
- pi: {},
13
- hasTool: async () => false,
14
- log: () => { },
15
- };
16
- }
17
- describe("ast-grep-napi vs CLI comparison", () => {
18
- it("should load the napi module", async () => {
19
- const napiModule = await import("./ast-grep-napi.js");
20
- expect(napiModule.default.id).toBe("ast-grep-napi");
21
- expect(napiModule.default.appliesTo).toEqual(["jsts"]);
22
- });
23
- it("should scan TypeScript file and return succeeded status", async () => {
24
- const tmpFile = path.join(process.env.TEMP || "/tmp", `napi_test_${Date.now()}.ts`);
25
- fs.writeFileSync(tmpFile, `// Test file with various patterns
26
- function test(items: string[]) {
27
- for (let i = 0; i < items.length; i++) {
28
- console.log(items[i]);
29
- }
30
-
31
- try {
32
- riskyOperation();
33
- } catch (e) {
34
- // empty catch
35
- }
36
-
37
- return await fetchData();
38
- }
39
-
40
- async function fetchData() {
41
- return await Promise.resolve(42);
42
- }
43
-
44
- function riskyOperation() {
45
- debugger;
46
- }
47
- `);
48
- try {
49
- // Test NAPI version
50
- const napiModule = await import("./ast-grep-napi.js");
51
- const napiRunner = napiModule.default;
52
- console.time("napi");
53
- let napiResult;
54
- try {
55
- napiResult = await napiRunner.run(createMockContext(tmpFile));
56
- }
57
- catch (error) {
58
- console.error("NAPI runner threw error:", error);
59
- throw error;
60
- }
61
- console.timeEnd("napi");
62
- console.log("NAPI result status:", napiResult.status);
63
- console.log("NAPI result semantic:", napiResult.semantic);
64
- console.log("NAPI result diagnostics count:", napiResult.diagnostics?.length);
65
- // Should complete successfully (not skipped, not failed)
66
- expect(napiResult.status).toBe("succeeded");
67
- // Semantic reflects the highest severity: "blocking" if any error-severity rules matched
68
- expect(["warning", "blocking"]).toContain(napiResult.semantic);
69
- // Log findings
70
- console.log("NAPI found:", napiResult.diagnostics.length, "issues");
71
- console.log("\n=== NAPI FINDINGS ===");
72
- napiResult.diagnostics.forEach((d, i) => {
73
- console.log(`${i + 1}. Line ${d.line}: ${d.rule}`);
74
- });
75
- }
76
- finally {
77
- try {
78
- if (fs.existsSync(tmpFile)) {
79
- fs.unlinkSync(tmpFile);
80
- }
81
- }
82
- catch {
83
- // Ignore cleanup errors
84
- }
85
- }
86
- });
87
- it("should skip non-TS/JS files", async () => {
88
- const tmpFile = path.join(process.env.TEMP || "/tmp", `napi_test_py_${Date.now()}.py`);
89
- fs.writeFileSync(tmpFile, "# Python file\nprint('hello')");
90
- try {
91
- const napiModule = await import("./ast-grep-napi.js");
92
- const napiRunner = napiModule.default;
93
- const result = await napiRunner.run(createMockContext(tmpFile, "python"));
94
- expect(result.status).toBe("skipped");
95
- }
96
- finally {
97
- try {
98
- if (fs.existsSync(tmpFile)) {
99
- fs.unlinkSync(tmpFile);
100
- }
101
- }
102
- catch {
103
- // Ignore cleanup errors
104
- }
105
- }
106
- });
107
- });
@@ -1,129 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { describe, expect, it } from "vitest";
4
- import type { DispatchContext } from "../types.js";
5
-
6
- function createMockContext(
7
- filePath: string,
8
- kind: any = "jsts",
9
- ): DispatchContext {
10
- return {
11
- filePath,
12
- cwd: process.cwd(),
13
- kind,
14
- autofix: false,
15
- deltaMode: false,
16
- baselines: { get: () => [], add: () => {}, save: () => {} } as any,
17
- pi: {} as any,
18
- hasTool: async () => false,
19
- log: () => {},
20
- };
21
- }
22
-
23
- describe("ast-grep-napi vs CLI comparison", () => {
24
- it("should load the napi module", async () => {
25
- const napiModule = await import("./ast-grep-napi.js");
26
- expect(napiModule.default.id).toBe("ast-grep-napi");
27
- expect(napiModule.default.appliesTo).toEqual(["jsts"]);
28
- });
29
-
30
- it("should scan TypeScript file and return succeeded status", async () => {
31
- const tmpFile = path.join(
32
- process.env.TEMP || "/tmp",
33
- `napi_test_${Date.now()}.ts`,
34
- );
35
- fs.writeFileSync(
36
- tmpFile,
37
- `// Test file with various patterns
38
- function test(items: string[]) {
39
- for (let i = 0; i < items.length; i++) {
40
- console.log(items[i]);
41
- }
42
-
43
- try {
44
- riskyOperation();
45
- } catch (e) {
46
- // empty catch
47
- }
48
-
49
- return await fetchData();
50
- }
51
-
52
- async function fetchData() {
53
- return await Promise.resolve(42);
54
- }
55
-
56
- function riskyOperation() {
57
- debugger;
58
- }
59
- `,
60
- );
61
-
62
- try {
63
- // Test NAPI version
64
- const napiModule = await import("./ast-grep-napi.js");
65
- const napiRunner = napiModule.default;
66
-
67
- console.time("napi");
68
- let napiResult;
69
- try {
70
- napiResult = await napiRunner.run(createMockContext(tmpFile));
71
- } catch (error) {
72
- console.error("NAPI runner threw error:", error);
73
- throw error;
74
- }
75
- console.timeEnd("napi");
76
-
77
- console.log("NAPI result status:", napiResult.status);
78
- console.log("NAPI result semantic:", napiResult.semantic);
79
- console.log(
80
- "NAPI result diagnostics count:",
81
- napiResult.diagnostics?.length,
82
- );
83
-
84
- // Should complete successfully (not skipped, not failed)
85
- expect(napiResult.status).toBe("succeeded");
86
- // Semantic reflects the highest severity: "blocking" if any error-severity rules matched
87
- expect(["warning", "blocking"]).toContain(napiResult.semantic);
88
-
89
- // Log findings
90
- console.log("NAPI found:", napiResult.diagnostics.length, "issues");
91
- console.log("\n=== NAPI FINDINGS ===");
92
- napiResult.diagnostics.forEach((d, i) => {
93
- console.log(`${i + 1}. Line ${d.line}: ${d.rule}`);
94
- });
95
- } finally {
96
- try {
97
- if (fs.existsSync(tmpFile)) {
98
- fs.unlinkSync(tmpFile);
99
- }
100
- } catch {
101
- // Ignore cleanup errors
102
- }
103
- }
104
- });
105
-
106
- it("should skip non-TS/JS files", async () => {
107
- const tmpFile = path.join(
108
- process.env.TEMP || "/tmp",
109
- `napi_test_py_${Date.now()}.py`,
110
- );
111
- fs.writeFileSync(tmpFile, "# Python file\nprint('hello')");
112
-
113
- try {
114
- const napiModule = await import("./ast-grep-napi.js");
115
- const napiRunner = napiModule.default;
116
-
117
- const result = await napiRunner.run(createMockContext(tmpFile, "python"));
118
- expect(result.status).toBe("skipped");
119
- } finally {
120
- try {
121
- if (fs.existsSync(tmpFile)) {
122
- fs.unlinkSync(tmpFile);
123
- }
124
- } catch {
125
- // Ignore cleanup errors
126
- }
127
- }
128
- });
129
- });