pi-lens 3.6.2 → 3.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/package.json +4 -4
  3. package/tsconfig.json +1 -1
  4. package/clients/__tests__/file-time.test.js +0 -216
  5. package/clients/__tests__/file-time.test.ts +0 -276
  6. package/clients/__tests__/format-service.test.js +0 -245
  7. package/clients/__tests__/format-service.test.ts +0 -339
  8. package/clients/__tests__/formatters.test.js +0 -271
  9. package/clients/__tests__/formatters.test.ts +0 -401
  10. package/clients/agent-behavior-client.js +0 -110
  11. package/clients/agent-behavior-client.test.js +0 -94
  12. package/clients/agent-behavior-client.test.ts +0 -116
  13. package/clients/amain-types.js +0 -164
  14. package/clients/architect-client.js +0 -291
  15. package/clients/ast-grep-client.js +0 -253
  16. package/clients/ast-grep-parser.js +0 -84
  17. package/clients/ast-grep-rule-manager.js +0 -89
  18. package/clients/ast-grep-types.js +0 -9
  19. package/clients/auto-loop.js +0 -131
  20. package/clients/biome-client.js +0 -420
  21. package/clients/biome-client.test.js +0 -144
  22. package/clients/biome-client.test.ts +0 -163
  23. package/clients/cache/rule-cache.js +0 -72
  24. package/clients/cache-manager.js +0 -245
  25. package/clients/cache-manager.test.js +0 -197
  26. package/clients/cache-manager.test.ts +0 -299
  27. package/clients/complexity-client.js +0 -675
  28. package/clients/complexity-client.test.js +0 -234
  29. package/clients/complexity-client.test.ts +0 -255
  30. package/clients/config-validator.js +0 -465
  31. package/clients/dependency-checker.js +0 -325
  32. package/clients/dependency-checker.test.js +0 -60
  33. package/clients/dependency-checker.test.ts +0 -71
  34. package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
  35. package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
  36. package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
  37. package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
  38. package/clients/dispatch/debug.log +0 -1
  39. package/clients/dispatch/dispatcher.edge.test.js +0 -82
  40. package/clients/dispatch/dispatcher.edge.test.ts +0 -100
  41. package/clients/dispatch/dispatcher.format.test.js +0 -46
  42. package/clients/dispatch/dispatcher.format.test.ts +0 -58
  43. package/clients/dispatch/dispatcher.inline.test.js +0 -74
  44. package/clients/dispatch/dispatcher.inline.test.ts +0 -93
  45. package/clients/dispatch/dispatcher.js +0 -381
  46. package/clients/dispatch/dispatcher.test.js +0 -116
  47. package/clients/dispatch/dispatcher.test.ts +0 -149
  48. package/clients/dispatch/integration.js +0 -108
  49. package/clients/dispatch/plan.js +0 -183
  50. package/clients/dispatch/runners/architect.js +0 -83
  51. package/clients/dispatch/runners/architect.test.js +0 -138
  52. package/clients/dispatch/runners/architect.test.ts +0 -162
  53. package/clients/dispatch/runners/ast-grep-napi.js +0 -405
  54. package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
  55. package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
  56. package/clients/dispatch/runners/ast-grep.js +0 -157
  57. package/clients/dispatch/runners/biome.js +0 -55
  58. package/clients/dispatch/runners/config-validation.js +0 -67
  59. package/clients/dispatch/runners/go-vet.js +0 -48
  60. package/clients/dispatch/runners/index.js +0 -47
  61. package/clients/dispatch/runners/lsp.js +0 -102
  62. package/clients/dispatch/runners/oxlint.js +0 -67
  63. package/clients/dispatch/runners/oxlint.test.js +0 -230
  64. package/clients/dispatch/runners/oxlint.test.ts +0 -303
  65. package/clients/dispatch/runners/pyright.js +0 -100
  66. package/clients/dispatch/runners/pyright.test.js +0 -98
  67. package/clients/dispatch/runners/pyright.test.ts +0 -121
  68. package/clients/dispatch/runners/python-slop.js +0 -97
  69. package/clients/dispatch/runners/python-slop.test.js +0 -203
  70. package/clients/dispatch/runners/python-slop.test.ts +0 -298
  71. package/clients/dispatch/runners/ruff.js +0 -48
  72. package/clients/dispatch/runners/rust-clippy.js +0 -102
  73. package/clients/dispatch/runners/scan_codebase.test.js +0 -89
  74. package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
  75. package/clients/dispatch/runners/shellcheck.js +0 -147
  76. package/clients/dispatch/runners/shellcheck.test.js +0 -98
  77. package/clients/dispatch/runners/shellcheck.test.ts +0 -129
  78. package/clients/dispatch/runners/similarity.js +0 -230
  79. package/clients/dispatch/runners/spellcheck.js +0 -106
  80. package/clients/dispatch/runners/spellcheck.test.js +0 -158
  81. package/clients/dispatch/runners/spellcheck.test.ts +0 -214
  82. package/clients/dispatch/runners/tree-sitter.js +0 -246
  83. package/clients/dispatch/runners/ts-lsp.js +0 -125
  84. package/clients/dispatch/runners/ts-slop.js +0 -113
  85. package/clients/dispatch/runners/type-safety.js +0 -142
  86. package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
  87. package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
  88. package/clients/dispatch/runners/utils.js +0 -51
  89. package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
  90. package/clients/dispatch/types.js +0 -16
  91. package/clients/dispatch/utils/format-utils.js +0 -44
  92. package/clients/dogfood.test.js +0 -201
  93. package/clients/dogfood.test.ts +0 -269
  94. package/clients/file-kinds.js +0 -177
  95. package/clients/file-kinds.test.js +0 -169
  96. package/clients/file-kinds.test.ts +0 -210
  97. package/clients/file-time.js +0 -152
  98. package/clients/file-utils.js +0 -40
  99. package/clients/fix-scanners.js +0 -204
  100. package/clients/format-service.js +0 -184
  101. package/clients/formatters.js +0 -488
  102. package/clients/go-client.js +0 -203
  103. package/clients/go-client.test.js +0 -127
  104. package/clients/go-client.test.ts +0 -143
  105. package/clients/installer/index.js +0 -403
  106. package/clients/interviewer-templates.js +0 -75
  107. package/clients/interviewer.js +0 -173
  108. package/clients/jscpd-client.js +0 -196
  109. package/clients/jscpd-client.test.js +0 -127
  110. package/clients/jscpd-client.test.ts +0 -145
  111. package/clients/knip-client.js +0 -239
  112. package/clients/knip-client.test.js +0 -112
  113. package/clients/knip-client.test.ts +0 -128
  114. package/clients/latency-logger.js +0 -40
  115. package/clients/lsp/__tests__/client.test.js +0 -310
  116. package/clients/lsp/__tests__/client.test.ts +0 -412
  117. package/clients/lsp/__tests__/config.test.js +0 -167
  118. package/clients/lsp/__tests__/config.test.ts +0 -217
  119. package/clients/lsp/__tests__/error-recovery.test.js +0 -213
  120. package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
  121. package/clients/lsp/__tests__/integration.test.js +0 -127
  122. package/clients/lsp/__tests__/integration.test.ts +0 -160
  123. package/clients/lsp/__tests__/launch.test.js +0 -313
  124. package/clients/lsp/__tests__/launch.test.ts +0 -394
  125. package/clients/lsp/__tests__/server.test.js +0 -259
  126. package/clients/lsp/__tests__/server.test.ts +0 -332
  127. package/clients/lsp/__tests__/service.test.js +0 -438
  128. package/clients/lsp/__tests__/service.test.ts +0 -530
  129. package/clients/lsp/client.js +0 -350
  130. package/clients/lsp/config.js +0 -112
  131. package/clients/lsp/index.js +0 -318
  132. package/clients/lsp/installer/index.js +0 -391
  133. package/clients/lsp/interactive-install.js +0 -221
  134. package/clients/lsp/language.js +0 -170
  135. package/clients/lsp/launch.js +0 -329
  136. package/clients/lsp/lsp/launch.js +0 -116
  137. package/clients/lsp/lsp/server.js +0 -532
  138. package/clients/lsp/lsp-index.js +0 -10
  139. package/clients/lsp/path-utils.js +0 -5
  140. package/clients/lsp/server.js +0 -725
  141. package/clients/lsp/test-py-spawn/requirements.txt +0 -1
  142. package/clients/lsp/test-py-spawn/test.py +0 -3
  143. package/clients/lsp/test-py-svc/requirements.txt +0 -1
  144. package/clients/lsp/test-py-svc/test.py +0 -3
  145. package/clients/lsp/test-python-project/requirements.txt +0 -1
  146. package/clients/lsp/test-python-project/test.py +0 -5
  147. package/clients/metrics-client.js +0 -107
  148. package/clients/metrics-client.test.js +0 -128
  149. package/clients/metrics-client.test.ts +0 -163
  150. package/clients/metrics-history.js +0 -367
  151. package/clients/path-utils.js +0 -142
  152. package/clients/pipeline.js +0 -272
  153. package/clients/production-readiness.js +0 -522
  154. package/clients/project-index.js +0 -255
  155. package/clients/project-metadata.js +0 -531
  156. package/clients/ruff-client.js +0 -325
  157. package/clients/ruff-client.test.js +0 -132
  158. package/clients/ruff-client.test.ts +0 -153
  159. package/clients/rules-scanner.js +0 -97
  160. package/clients/runner-tracker.js +0 -152
  161. package/clients/rust-client.js +0 -205
  162. package/clients/rust-client.test.js +0 -108
  163. package/clients/rust-client.test.ts +0 -130
  164. package/clients/safe-spawn-async.js +0 -163
  165. package/clients/safe-spawn.js +0 -241
  166. package/clients/sanitize.js +0 -291
  167. package/clients/sanitize.test.js +0 -177
  168. package/clients/sanitize.test.ts +0 -223
  169. package/clients/scan-architectural-debt.js +0 -167
  170. package/clients/scan-utils.js +0 -83
  171. package/clients/secrets-scanner.js +0 -119
  172. package/clients/secrets-scanner.test.js +0 -100
  173. package/clients/secrets-scanner.test.ts +0 -113
  174. package/clients/sg-runner.js +0 -292
  175. package/clients/state-matrix.js +0 -160
  176. package/clients/subprocess-client.js +0 -65
  177. package/clients/symbol-types.js +0 -5
  178. package/clients/test-runner-client.js +0 -523
  179. package/clients/test-runner-client.test.js +0 -192
  180. package/clients/test-runner-client.test.ts +0 -253
  181. package/clients/test-utils.js +0 -27
  182. package/clients/test-utils.ts +0 -36
  183. package/clients/todo-scanner.js +0 -200
  184. package/clients/todo-scanner.test.js +0 -301
  185. package/clients/todo-scanner.test.ts +0 -352
  186. package/clients/tool-availability.js +0 -207
  187. package/clients/tree-sitter-client.js +0 -601
  188. package/clients/tree-sitter-query-loader.js +0 -355
  189. package/clients/tree-sitter-symbol-extractor.js +0 -289
  190. package/clients/ts-service.js +0 -129
  191. package/clients/type-coverage-client.js +0 -127
  192. package/clients/type-coverage-client.test.js +0 -105
  193. package/clients/type-coverage-client.test.ts +0 -125
  194. package/clients/type-safety-client.js +0 -138
  195. package/clients/types.js +0 -11
  196. package/clients/typescript-client.codefix.test.js +0 -157
  197. package/clients/typescript-client.codefix.test.ts +0 -186
  198. package/clients/typescript-client.js +0 -509
  199. package/clients/typescript-client.test.js +0 -105
  200. package/clients/typescript-client.test.ts +0 -126
  201. package/commands/booboo.js +0 -1007
  202. package/commands/fix-from-booboo.js +0 -398
  203. package/commands/fix-simplified.js +0 -618
  204. package/commands/rate.js +0 -281
  205. package/commands/rate.test.js +0 -119
  206. package/commands/rate.test.ts +0 -131
  207. 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
- });