@vibecheckai/cli 3.5.0 → 3.5.2
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/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mock Data Detection Engine
|
|
3
3
|
* Uses AST analysis to detect mock/test data patterns in production code
|
|
4
|
-
* Enhanced with context-aware detection to reduce false positives
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
6
|
const { getAST, parseCode } = require("./ast-cache");
|
|
8
7
|
const traverse = require("@babel/traverse").default;
|
|
9
8
|
const t = require("@babel/types");
|
|
10
9
|
const { shouldExcludeFile } = require("./file-filter");
|
|
11
|
-
const {
|
|
12
|
-
detectFalsePositive,
|
|
13
|
-
quickFalsePositiveCheck,
|
|
14
|
-
isTailwindPlaceholderModifier,
|
|
15
|
-
isLegitimateUILabel,
|
|
16
|
-
} = require("./context-detection");
|
|
17
10
|
|
|
18
11
|
/**
|
|
19
12
|
* Check if a string literal looks like mock/test data
|
|
@@ -113,25 +106,6 @@ function isSuspiciousSetTimeout(node) {
|
|
|
113
106
|
return false;
|
|
114
107
|
}
|
|
115
108
|
|
|
116
|
-
/**
|
|
117
|
-
* Check if a finding should be skipped due to context
|
|
118
|
-
*/
|
|
119
|
-
function shouldSkipFinding(line, lineIndex, lines, patternType) {
|
|
120
|
-
// Quick pre-filter for common false positives
|
|
121
|
-
if (quickFalsePositiveCheck(line)) {
|
|
122
|
-
return true;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Detailed context analysis
|
|
126
|
-
const result = detectFalsePositive(line, line, {
|
|
127
|
-
lineIndex,
|
|
128
|
-
allLines: lines,
|
|
129
|
-
patternType,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return result.isFalsePositive;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
109
|
/**
|
|
136
110
|
* Analyze a file for mock data patterns
|
|
137
111
|
*/
|
|
@@ -151,16 +125,10 @@ function analyzeMockData(code, filePath) {
|
|
|
151
125
|
VariableDeclarator(path) {
|
|
152
126
|
const id = path.node.id;
|
|
153
127
|
const init = path.node.init;
|
|
154
|
-
const line = getLineNumber(path.node, code);
|
|
155
|
-
const lineContent = lines[line - 1] || "";
|
|
156
|
-
|
|
157
|
-
// Skip if this line is a false positive (CSS class, UI label, etc.)
|
|
158
|
-
if (shouldSkipFinding(lineContent, line - 1, lines, "mock")) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
128
|
|
|
162
129
|
// Check variable name
|
|
163
130
|
if (t.isIdentifier(id) && isMockVariableName(id.name)) {
|
|
131
|
+
const line = getLineNumber(path.node, code);
|
|
164
132
|
findings.push({
|
|
165
133
|
type: "mock_variable",
|
|
166
134
|
severity: "WARN",
|
|
@@ -170,13 +138,14 @@ function analyzeMockData(code, filePath) {
|
|
|
170
138
|
column: path.node.loc.start.column,
|
|
171
139
|
title: `Mock data variable: ${id.name}`,
|
|
172
140
|
message: `Variable name suggests mock/test data: ${id.name}`,
|
|
173
|
-
codeSnippet:
|
|
141
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
174
142
|
confidence: "high",
|
|
175
143
|
});
|
|
176
144
|
}
|
|
177
145
|
|
|
178
146
|
// Check initializer value
|
|
179
147
|
if (init && t.isStringLiteral(init) && isMockString(init.value)) {
|
|
148
|
+
const line = getLineNumber(path.node, code);
|
|
180
149
|
findings.push({
|
|
181
150
|
type: "mock_string_literal",
|
|
182
151
|
severity: "WARN",
|
|
@@ -186,7 +155,7 @@ function analyzeMockData(code, filePath) {
|
|
|
186
155
|
column: path.node.loc.start.column,
|
|
187
156
|
title: "Mock/test string literal detected",
|
|
188
157
|
message: `String value appears to be mock data: "${init.value.substring(0, 50)}"`,
|
|
189
|
-
codeSnippet:
|
|
158
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
190
159
|
confidence: "med",
|
|
191
160
|
});
|
|
192
161
|
}
|
|
@@ -196,15 +165,9 @@ function analyzeMockData(code, filePath) {
|
|
|
196
165
|
ObjectProperty(path) {
|
|
197
166
|
const key = path.node.key;
|
|
198
167
|
const value = path.node.value;
|
|
199
|
-
const line = getLineNumber(path.node, code);
|
|
200
|
-
const lineContent = lines[line - 1] || "";
|
|
201
|
-
|
|
202
|
-
// Skip if this line is a false positive (CSS class, UI label, etc.)
|
|
203
|
-
if (shouldSkipFinding(lineContent, line - 1, lines, "mock")) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
168
|
|
|
207
169
|
if (t.isIdentifier(key) && isMockVariableName(key.name)) {
|
|
170
|
+
const line = getLineNumber(path.node, code);
|
|
208
171
|
findings.push({
|
|
209
172
|
type: "mock_object_property",
|
|
210
173
|
severity: "WARN",
|
|
@@ -214,12 +177,13 @@ function analyzeMockData(code, filePath) {
|
|
|
214
177
|
column: path.node.loc.start.column,
|
|
215
178
|
title: `Mock data property: ${key.name}`,
|
|
216
179
|
message: `Object property name suggests mock data: ${key.name}`,
|
|
217
|
-
codeSnippet:
|
|
180
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
218
181
|
confidence: "med",
|
|
219
182
|
});
|
|
220
183
|
}
|
|
221
184
|
|
|
222
185
|
if (t.isStringLiteral(value) && isMockString(value.value)) {
|
|
186
|
+
const line = getLineNumber(path.node, code);
|
|
223
187
|
findings.push({
|
|
224
188
|
type: "mock_property_value",
|
|
225
189
|
severity: "WARN",
|
|
@@ -229,7 +193,7 @@ function analyzeMockData(code, filePath) {
|
|
|
229
193
|
column: path.node.loc.start.column,
|
|
230
194
|
title: "Mock string in object property",
|
|
231
195
|
message: `Property value appears to be mock data: "${value.value.substring(0, 50)}"`,
|
|
232
|
-
codeSnippet:
|
|
196
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
233
197
|
confidence: "med",
|
|
234
198
|
});
|
|
235
199
|
}
|
|
@@ -272,17 +236,10 @@ function analyzeMockData(code, filePath) {
|
|
|
272
236
|
|
|
273
237
|
// Check template literals
|
|
274
238
|
TemplateLiteral(path) {
|
|
275
|
-
const line = getLineNumber(path.node, code);
|
|
276
|
-
const lineContent = lines[line - 1] || "";
|
|
277
|
-
|
|
278
|
-
// Skip if this line is a false positive (CSS class, UI label, etc.)
|
|
279
|
-
if (shouldSkipFinding(lineContent, line - 1, lines, "mock")) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
239
|
const quasis = path.node.quasis;
|
|
284
240
|
for (const quasi of quasis) {
|
|
285
241
|
if (quasi.value && isMockString(quasi.value.raw)) {
|
|
242
|
+
const line = getLineNumber(path.node, code);
|
|
286
243
|
findings.push({
|
|
287
244
|
type: "mock_template_literal",
|
|
288
245
|
severity: "WARN",
|
|
@@ -292,7 +249,7 @@ function analyzeMockData(code, filePath) {
|
|
|
292
249
|
column: path.node.loc.start.column,
|
|
293
250
|
title: "Mock data in template literal",
|
|
294
251
|
message: `Template literal contains mock/test data pattern`,
|
|
295
|
-
codeSnippet:
|
|
252
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
296
253
|
confidence: "med",
|
|
297
254
|
});
|
|
298
255
|
break;
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Performance Issues Detection Engine
|
|
3
3
|
* Detects memory leaks, inefficient loops, large bundle sizes, and performance anti-patterns
|
|
4
|
-
* Enhanced with framework-aware detection:
|
|
5
|
-
* - React Server Components (different rules apply)
|
|
6
|
-
* - Next.js App Router patterns
|
|
7
|
-
* - Client-only hooks in server components
|
|
8
4
|
*/
|
|
9
5
|
|
|
10
6
|
const { getAST } = require("./ast-cache");
|
|
@@ -12,32 +8,6 @@ const traverse = require("@babel/traverse").default;
|
|
|
12
8
|
const t = require("@babel/types");
|
|
13
9
|
const { shouldExcludeFile } = require("./file-filter");
|
|
14
10
|
|
|
15
|
-
/**
|
|
16
|
-
* Check if file is a React Server Component
|
|
17
|
-
*/
|
|
18
|
-
function isServerComponent(code, filePath) {
|
|
19
|
-
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
20
|
-
|
|
21
|
-
// Check if it's in app directory (Next.js App Router)
|
|
22
|
-
if (!normalizedPath.includes("/app/")) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Check for "use client" directive - if present, it's NOT a server component
|
|
27
|
-
if (/['"]use client['"]/.test(code)) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Check if file is a client component
|
|
36
|
-
*/
|
|
37
|
-
function isClientComponent(code) {
|
|
38
|
-
return /['"]use client['"]/.test(code);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
11
|
/**
|
|
42
12
|
* Analyze a file for performance issues
|
|
43
13
|
*/
|
|
@@ -51,77 +21,48 @@ function analyzePerformanceIssues(code, filePath) {
|
|
|
51
21
|
if (!ast) return findings;
|
|
52
22
|
|
|
53
23
|
const lines = code.split("\n");
|
|
54
|
-
|
|
55
|
-
// Determine component type
|
|
56
|
-
const isRSC = isServerComponent(code, filePath);
|
|
57
|
-
const isCC = isClientComponent(code);
|
|
58
24
|
|
|
59
25
|
// Memory leaks: Event listeners not removed
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
26
|
+
traverse(ast, {
|
|
27
|
+
CallExpression(path) {
|
|
28
|
+
const node = path.node;
|
|
29
|
+
|
|
30
|
+
// Check for addEventListener without corresponding removeEventListener
|
|
31
|
+
if (t.isMemberExpression(node.callee)) {
|
|
32
|
+
const prop = node.callee.property;
|
|
65
33
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
34
|
+
if (t.isIdentifier(prop) && prop.name === "addEventListener") {
|
|
35
|
+
// Check if there's a corresponding removeEventListener in the same scope
|
|
36
|
+
const scope = path.scope;
|
|
37
|
+
const hasRemoveListener = scope.getAllBindings().some((binding, name) => {
|
|
38
|
+
return name.includes("removeEventListener") || name.includes("removeListener");
|
|
39
|
+
});
|
|
69
40
|
|
|
70
|
-
if
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
t.isIdentifier(p.node.callee, { name: "useEffect" })
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
// Check if there's a cleanup function returning removeEventListener
|
|
94
|
-
if (parent) {
|
|
95
|
-
const useEffectArg = parent.node.arguments[0];
|
|
96
|
-
if (t.isArrowFunctionExpression(useEffectArg) || t.isFunctionExpression(useEffectArg)) {
|
|
97
|
-
const funcCode = code.substring(useEffectArg.start, useEffectArg.end);
|
|
98
|
-
if (/return.*removeEventListener|removeEventListener.*return/s.test(funcCode)) {
|
|
99
|
-
hasRemoveListener = true;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (!hasRemoveListener && !parent) {
|
|
105
|
-
const line = node.loc.start.line;
|
|
106
|
-
findings.push({
|
|
107
|
-
type: "memory_leak",
|
|
108
|
-
severity: "WARN",
|
|
109
|
-
category: "Performance",
|
|
110
|
-
file: filePath,
|
|
111
|
-
line,
|
|
112
|
-
column: node.loc.start.column,
|
|
113
|
-
title: "Potential memory leak: Event listener not removed",
|
|
114
|
-
message: "addEventListener called without corresponding removeEventListener. Use useEffect cleanup.",
|
|
115
|
-
codeSnippet: lines[line - 1]?.trim(),
|
|
116
|
-
confidence: "med",
|
|
117
|
-
fixHint: "Add cleanup in useEffect: useEffect(() => { el.addEventListener(...); return () => el.removeEventListener(...); }, [])",
|
|
118
|
-
});
|
|
119
|
-
}
|
|
41
|
+
// Also check if it's in a useEffect cleanup (React)
|
|
42
|
+
const parent = path.findParent(p =>
|
|
43
|
+
t.isCallExpression(p.node) &&
|
|
44
|
+
t.isIdentifier(p.node.callee, { name: "useEffect" })
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (!hasRemoveListener && !parent) {
|
|
48
|
+
const line = node.loc.start.line;
|
|
49
|
+
findings.push({
|
|
50
|
+
type: "memory_leak",
|
|
51
|
+
severity: "WARN",
|
|
52
|
+
category: "Performance",
|
|
53
|
+
file: filePath,
|
|
54
|
+
line,
|
|
55
|
+
column: node.loc.start.column,
|
|
56
|
+
title: "Potential memory leak: Event listener not removed",
|
|
57
|
+
message: "addEventListener called without corresponding removeEventListener",
|
|
58
|
+
codeSnippet: lines[line - 1]?.trim(),
|
|
59
|
+
confidence: "med",
|
|
60
|
+
});
|
|
120
61
|
}
|
|
121
62
|
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
125
66
|
|
|
126
67
|
// Inefficient loops: nested loops with O(n²) complexity
|
|
127
68
|
// Only flag if depth >= 4 (3 levels is often acceptable)
|
|
@@ -310,93 +251,12 @@ function analyzePerformanceIssues(code, filePath) {
|
|
|
310
251
|
message: `Importing entire ${source} library - use tree-shaking or import specific functions`,
|
|
311
252
|
codeSnippet: lines[line - 1]?.trim(),
|
|
312
253
|
confidence: "med",
|
|
313
|
-
fixHint: `Import specific functions: import { specificFn } from '${source}/specificFn'`,
|
|
314
254
|
});
|
|
315
255
|
}
|
|
316
256
|
}
|
|
317
257
|
},
|
|
318
258
|
});
|
|
319
259
|
|
|
320
|
-
// React Server Components: Detect client-only hooks used in server components
|
|
321
|
-
if (isRSC) {
|
|
322
|
-
const clientOnlyHooks = [
|
|
323
|
-
"useState",
|
|
324
|
-
"useEffect",
|
|
325
|
-
"useLayoutEffect",
|
|
326
|
-
"useRef",
|
|
327
|
-
"useCallback",
|
|
328
|
-
"useMemo",
|
|
329
|
-
"useReducer",
|
|
330
|
-
"useContext",
|
|
331
|
-
"useImperativeHandle",
|
|
332
|
-
"useDebugValue",
|
|
333
|
-
];
|
|
334
|
-
|
|
335
|
-
traverse(ast, {
|
|
336
|
-
CallExpression(path) {
|
|
337
|
-
const node = path.node;
|
|
338
|
-
|
|
339
|
-
if (t.isIdentifier(node.callee)) {
|
|
340
|
-
const hookName = node.callee.name;
|
|
341
|
-
|
|
342
|
-
if (clientOnlyHooks.includes(hookName)) {
|
|
343
|
-
const line = node.loc.start.line;
|
|
344
|
-
findings.push({
|
|
345
|
-
type: "client_hook_in_server_component",
|
|
346
|
-
severity: "BLOCK",
|
|
347
|
-
category: "Performance",
|
|
348
|
-
file: filePath,
|
|
349
|
-
line,
|
|
350
|
-
column: node.loc.start.column,
|
|
351
|
-
title: `Client-only hook '${hookName}' used in Server Component`,
|
|
352
|
-
message: `${hookName} cannot be used in Server Components. Add 'use client' directive or move to a client component.`,
|
|
353
|
-
codeSnippet: lines[line - 1]?.trim(),
|
|
354
|
-
confidence: "high",
|
|
355
|
-
fixHint: "Add 'use client' at the top of the file, or extract this logic to a client component",
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Detect async/await in client components that should be server components
|
|
364
|
-
if (isCC) {
|
|
365
|
-
// Check for database queries in client components
|
|
366
|
-
const serverOnlyPatterns = [
|
|
367
|
-
/prisma\./,
|
|
368
|
-
/db\./,
|
|
369
|
-
/\.findUnique\(/,
|
|
370
|
-
/\.findMany\(/,
|
|
371
|
-
/\.create\(/,
|
|
372
|
-
/\.update\(/,
|
|
373
|
-
/\.delete\(/,
|
|
374
|
-
/fs\./,
|
|
375
|
-
/readFile/,
|
|
376
|
-
/writeFile/,
|
|
377
|
-
];
|
|
378
|
-
|
|
379
|
-
if (serverOnlyPatterns.some(p => p.test(code))) {
|
|
380
|
-
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
381
|
-
// Only warn if in app directory where RSC is available
|
|
382
|
-
if (normalizedPath.includes("/app/")) {
|
|
383
|
-
findings.push({
|
|
384
|
-
type: "server_code_in_client_component",
|
|
385
|
-
severity: "WARN",
|
|
386
|
-
category: "Performance",
|
|
387
|
-
file: filePath,
|
|
388
|
-
line: 1,
|
|
389
|
-
column: 0,
|
|
390
|
-
title: "Server-only code in Client Component",
|
|
391
|
-
message: "Database/filesystem operations should be in Server Components for better performance",
|
|
392
|
-
codeSnippet: "'use client'",
|
|
393
|
-
confidence: "med",
|
|
394
|
-
fixHint: "Remove 'use client' and move client interactivity to child components",
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
260
|
return findings;
|
|
401
261
|
}
|
|
402
262
|
|