@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,333 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Accessibility Fixes - Auto-fix Polish Pass
|
|
3
|
-
*
|
|
4
|
-
* Fixes common accessibility issues:
|
|
5
|
-
* - Missing alt attributes on images
|
|
6
|
-
* - Missing aria-label on icon buttons
|
|
7
|
-
* - Missing form labels
|
|
8
|
-
* - Missing role attributes where needed
|
|
9
|
-
*
|
|
10
|
-
* Uses Babel AST for JSX/TSX analysis.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
"use strict";
|
|
14
|
-
|
|
15
|
-
const { parse } = require("@babel/parser");
|
|
16
|
-
const traverse = require("@babel/traverse").default || require("@babel/traverse");
|
|
17
|
-
const generate = require("@babel/generator").default || require("@babel/generator");
|
|
18
|
-
const t = require("@babel/types");
|
|
19
|
-
const fs = require("fs");
|
|
20
|
-
const path = require("path");
|
|
21
|
-
const { findAllFilesSync, readFileSafeSync } = require("../utils");
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Check if JSX element has an attribute
|
|
25
|
-
*/
|
|
26
|
-
function hasAttribute(node, attrName) {
|
|
27
|
-
if (!node.openingElement?.attributes) return false;
|
|
28
|
-
|
|
29
|
-
return node.openingElement.attributes.some((attr) => {
|
|
30
|
-
if (attr.type === "JSXAttribute" && attr.name.name === attrName) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
// Check spread attributes (can't be sure, assume covered)
|
|
34
|
-
if (attr.type === "JSXSpreadAttribute") {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get attribute value
|
|
43
|
-
*/
|
|
44
|
-
function getAttributeValue(node, attrName) {
|
|
45
|
-
if (!node.openingElement?.attributes) return null;
|
|
46
|
-
|
|
47
|
-
for (const attr of node.openingElement.attributes) {
|
|
48
|
-
if (attr.type === "JSXAttribute" && attr.name.name === attrName) {
|
|
49
|
-
if (attr.value?.type === "StringLiteral") {
|
|
50
|
-
return attr.value.value;
|
|
51
|
-
}
|
|
52
|
-
if (attr.value?.type === "JSXExpressionContainer") {
|
|
53
|
-
return attr.value.expression;
|
|
54
|
-
}
|
|
55
|
-
// Boolean attribute (no value = true)
|
|
56
|
-
if (attr.value === null) {
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Add JSX attribute to element
|
|
66
|
-
*/
|
|
67
|
-
function addAttribute(node, name, value) {
|
|
68
|
-
const attr = t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value));
|
|
69
|
-
node.openingElement.attributes.push(attr);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Parse code into AST
|
|
74
|
-
*/
|
|
75
|
-
function parseCode(code, filePath) {
|
|
76
|
-
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
77
|
-
const plugins = ["decorators-legacy", "classProperties", "jsx"];
|
|
78
|
-
|
|
79
|
-
if (isTypeScript) plugins.push("typescript");
|
|
80
|
-
|
|
81
|
-
return parse(code, {
|
|
82
|
-
sourceType: "module",
|
|
83
|
-
plugins,
|
|
84
|
-
errorRecovery: true,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Check if element is likely an icon (based on name or import)
|
|
90
|
-
*/
|
|
91
|
-
function isIconElement(node) {
|
|
92
|
-
const elementName = node.openingElement?.name;
|
|
93
|
-
if (!elementName) return false;
|
|
94
|
-
|
|
95
|
-
let name = "";
|
|
96
|
-
if (elementName.type === "JSXIdentifier") {
|
|
97
|
-
name = elementName.name;
|
|
98
|
-
} else if (elementName.type === "JSXMemberExpression") {
|
|
99
|
-
name = elementName.property?.name || "";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Common icon naming patterns
|
|
103
|
-
const iconPatterns = [
|
|
104
|
-
/Icon$/i,
|
|
105
|
-
/^Icon/i,
|
|
106
|
-
/^Fa[A-Z]/, // FontAwesome
|
|
107
|
-
/^Md[A-Z]/, // Material Design
|
|
108
|
-
/^Io[A-Z]/, // Ionicons
|
|
109
|
-
/^Fi[A-Z]/, // Feather
|
|
110
|
-
/^Bs[A-Z]/, // Bootstrap
|
|
111
|
-
/^Ai[A-Z]/, // Ant Design
|
|
112
|
-
/^Bi[A-Z]/, // BoxIcons
|
|
113
|
-
/^Ri[A-Z]/, // Remix Icons
|
|
114
|
-
/^Hi[A-Z]/, // Hero Icons
|
|
115
|
-
/^Lu[A-Z]/, // Lucide
|
|
116
|
-
];
|
|
117
|
-
|
|
118
|
-
return iconPatterns.some((p) => p.test(name));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Process a single file
|
|
123
|
-
*/
|
|
124
|
-
function processFile(filePath, options = {}) {
|
|
125
|
-
const { dryRun = true } = options;
|
|
126
|
-
const changes = [];
|
|
127
|
-
|
|
128
|
-
// Only process JSX/TSX files
|
|
129
|
-
if (!filePath.endsWith(".jsx") && !filePath.endsWith(".tsx")) {
|
|
130
|
-
return { changes: [], modified: false };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const code = readFileSafeSync(filePath);
|
|
134
|
-
if (!code) return { changes: [], modified: false };
|
|
135
|
-
|
|
136
|
-
let ast;
|
|
137
|
-
try {
|
|
138
|
-
ast = parseCode(code, filePath);
|
|
139
|
-
} catch (err) {
|
|
140
|
-
return { changes: [], modified: false, error: `Parse error: ${err.message}` };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
let modified = false;
|
|
144
|
-
const relativePath = path.relative(process.cwd(), filePath);
|
|
145
|
-
|
|
146
|
-
traverse(ast, {
|
|
147
|
-
JSXElement(nodePath) {
|
|
148
|
-
const { node } = nodePath;
|
|
149
|
-
const elementName = node.openingElement?.name;
|
|
150
|
-
if (!elementName) return;
|
|
151
|
-
|
|
152
|
-
let tagName = "";
|
|
153
|
-
if (elementName.type === "JSXIdentifier") {
|
|
154
|
-
tagName = elementName.name;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const line = node.loc?.start?.line || "?";
|
|
158
|
-
|
|
159
|
-
// 1. Check img elements for alt attribute
|
|
160
|
-
if (tagName === "img" || tagName === "Image") {
|
|
161
|
-
if (!hasAttribute(node, "alt")) {
|
|
162
|
-
changes.push({
|
|
163
|
-
file: relativePath,
|
|
164
|
-
line,
|
|
165
|
-
type: "missing-alt",
|
|
166
|
-
detail: `<${tagName}> missing alt attribute`,
|
|
167
|
-
suggestion: "Add descriptive alt text for screen readers",
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
if (!dryRun) {
|
|
171
|
-
addAttribute(node, "alt", ""); // Add empty alt, user should fill
|
|
172
|
-
modified = true;
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
// Check if alt is empty string (decorative image should have alt="")
|
|
176
|
-
const altValue = getAttributeValue(node, "alt");
|
|
177
|
-
if (altValue === "" || altValue === null) {
|
|
178
|
-
// Empty alt is valid for decorative images
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 2. Check buttons with only icons for aria-label
|
|
184
|
-
if (tagName === "button" || tagName === "Button") {
|
|
185
|
-
const hasText = node.children?.some(
|
|
186
|
-
(child) =>
|
|
187
|
-
child.type === "JSXText" && child.value.trim() !== "" ||
|
|
188
|
-
child.type === "JSXExpressionContainer"
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const hasAriaLabel =
|
|
192
|
-
hasAttribute(node, "aria-label") ||
|
|
193
|
-
hasAttribute(node, "aria-labelledby") ||
|
|
194
|
-
hasAttribute(node, "title");
|
|
195
|
-
|
|
196
|
-
// Check if button contains only icons
|
|
197
|
-
const childElements = node.children?.filter((c) => c.type === "JSXElement");
|
|
198
|
-
const hasOnlyIcons =
|
|
199
|
-
childElements?.length > 0 &&
|
|
200
|
-
childElements.every((c) => isIconElement(c));
|
|
201
|
-
|
|
202
|
-
if (!hasText && hasOnlyIcons && !hasAriaLabel) {
|
|
203
|
-
changes.push({
|
|
204
|
-
file: relativePath,
|
|
205
|
-
line,
|
|
206
|
-
type: "missing-aria-label",
|
|
207
|
-
detail: "Button with icon-only content missing aria-label",
|
|
208
|
-
suggestion: "Add aria-label to describe button action",
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
if (!dryRun) {
|
|
212
|
-
addAttribute(node, "aria-label", ""); // Add placeholder
|
|
213
|
-
modified = true;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// 3. Check interactive elements for keyboard accessibility
|
|
219
|
-
if (tagName === "div" || tagName === "span") {
|
|
220
|
-
const hasOnClick = hasAttribute(node, "onClick");
|
|
221
|
-
const hasRole = hasAttribute(node, "role");
|
|
222
|
-
const hasTabIndex = hasAttribute(node, "tabIndex");
|
|
223
|
-
|
|
224
|
-
if (hasOnClick && !hasRole) {
|
|
225
|
-
changes.push({
|
|
226
|
-
file: relativePath,
|
|
227
|
-
line,
|
|
228
|
-
type: "missing-role",
|
|
229
|
-
detail: `<${tagName}> with onClick missing role attribute`,
|
|
230
|
-
suggestion: 'Add role="button" for interactive elements',
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
if (!dryRun) {
|
|
234
|
-
addAttribute(node, "role", "button");
|
|
235
|
-
if (!hasTabIndex) {
|
|
236
|
-
addAttribute(node, "tabIndex", "0");
|
|
237
|
-
}
|
|
238
|
-
modified = true;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 4. Check form inputs for labels
|
|
244
|
-
if (tagName === "input" || tagName === "Input" || tagName === "select" || tagName === "textarea") {
|
|
245
|
-
const hasLabel =
|
|
246
|
-
hasAttribute(node, "aria-label") ||
|
|
247
|
-
hasAttribute(node, "aria-labelledby") ||
|
|
248
|
-
hasAttribute(node, "id"); // id can be associated with <label htmlFor>
|
|
249
|
-
|
|
250
|
-
const inputType = getAttributeValue(node, "type");
|
|
251
|
-
|
|
252
|
-
// Skip hidden, submit, button types
|
|
253
|
-
if (inputType === "hidden" || inputType === "submit" || inputType === "button") {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!hasLabel) {
|
|
258
|
-
changes.push({
|
|
259
|
-
file: relativePath,
|
|
260
|
-
line,
|
|
261
|
-
type: "missing-label",
|
|
262
|
-
detail: `<${tagName}> missing associated label`,
|
|
263
|
-
suggestion: "Add aria-label or associate with <label> via id",
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
// Write changes
|
|
271
|
-
if (modified && !dryRun) {
|
|
272
|
-
try {
|
|
273
|
-
const output = generate(ast, { retainLines: true }, code);
|
|
274
|
-
fs.writeFileSync(filePath, output.code, "utf8");
|
|
275
|
-
} catch (err) {
|
|
276
|
-
return { changes, modified: false, error: `Generate error: ${err.message}` };
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return { changes, modified };
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Fix accessibility issues in all files
|
|
285
|
-
*/
|
|
286
|
-
async function fixAccessibility(targetPath, options = {}) {
|
|
287
|
-
const { dryRun = true } = options;
|
|
288
|
-
|
|
289
|
-
// Only JSX/TSX files
|
|
290
|
-
const pattern = /\.(jsx|tsx)$/;
|
|
291
|
-
const files = findAllFilesSync(targetPath, pattern);
|
|
292
|
-
|
|
293
|
-
const allChanges = [];
|
|
294
|
-
const modifiedFiles = [];
|
|
295
|
-
const errors = [];
|
|
296
|
-
|
|
297
|
-
for (const file of files) {
|
|
298
|
-
const result = processFile(file, { dryRun });
|
|
299
|
-
|
|
300
|
-
if (result.error) {
|
|
301
|
-
errors.push({ file, error: result.error });
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (result.changes.length > 0) {
|
|
305
|
-
allChanges.push(...result.changes);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (result.modified) {
|
|
309
|
-
modifiedFiles.push(path.relative(process.cwd(), file));
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return {
|
|
314
|
-
changes: allChanges,
|
|
315
|
-
files: modifiedFiles,
|
|
316
|
-
errors,
|
|
317
|
-
summary: {
|
|
318
|
-
filesScanned: files.length,
|
|
319
|
-
filesModified: modifiedFiles.length,
|
|
320
|
-
missingAlt: allChanges.filter((c) => c.type === "missing-alt").length,
|
|
321
|
-
missingAriaLabel: allChanges.filter((c) => c.type === "missing-aria-label").length,
|
|
322
|
-
missingRole: allChanges.filter((c) => c.type === "missing-role").length,
|
|
323
|
-
missingLabel: allChanges.filter((c) => c.type === "missing-label").length,
|
|
324
|
-
},
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
module.exports = {
|
|
329
|
-
fixAccessibility,
|
|
330
|
-
processFile,
|
|
331
|
-
hasAttribute,
|
|
332
|
-
isIconElement,
|
|
333
|
-
};
|
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Async Handlers - Auto-fix Polish Pass
|
|
3
|
-
*
|
|
4
|
-
* Wraps unhandled async/await calls in try-catch blocks.
|
|
5
|
-
* Detects:
|
|
6
|
-
* - Top-level await without try-catch
|
|
7
|
-
* - Async function bodies without error handling
|
|
8
|
-
* - Promise chains without .catch()
|
|
9
|
-
*
|
|
10
|
-
* Note: This is a conservative pass - it suggests wrapping but
|
|
11
|
-
* only auto-applies to clear cases to avoid breaking code.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
"use strict";
|
|
15
|
-
|
|
16
|
-
const { parse } = require("@babel/parser");
|
|
17
|
-
const traverse = require("@babel/traverse").default || require("@babel/traverse");
|
|
18
|
-
const generate = require("@babel/generator").default || require("@babel/generator");
|
|
19
|
-
const t = require("@babel/types");
|
|
20
|
-
const fs = require("fs");
|
|
21
|
-
const path = require("path");
|
|
22
|
-
const { findAllFilesSync, readFileSafeSync } = require("../utils");
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Check if a node is wrapped in try-catch
|
|
26
|
-
*/
|
|
27
|
-
function isWrappedInTryCatch(nodePath) {
|
|
28
|
-
let current = nodePath.parentPath;
|
|
29
|
-
while (current) {
|
|
30
|
-
if (current.type === "TryStatement") {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
if (current.type === "FunctionDeclaration" || current.type === "ArrowFunctionExpression" || current.type === "FunctionExpression") {
|
|
34
|
-
break;
|
|
35
|
-
}
|
|
36
|
-
current = current.parentPath;
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Check if a Promise chain has .catch()
|
|
43
|
-
*/
|
|
44
|
-
function hasChainedCatch(node) {
|
|
45
|
-
if (node.type !== "CallExpression") return false;
|
|
46
|
-
if (node.callee.type !== "MemberExpression") return false;
|
|
47
|
-
|
|
48
|
-
const method = node.callee.property?.name;
|
|
49
|
-
if (method === "catch") return true;
|
|
50
|
-
|
|
51
|
-
// Check parent for chained .catch()
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Check if an async function has try-catch
|
|
57
|
-
*/
|
|
58
|
-
function asyncFunctionHasTryCatch(node) {
|
|
59
|
-
if (!node.body || node.body.type !== "BlockStatement") {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const body = node.body.body;
|
|
64
|
-
if (body.length === 0) return true; // Empty is fine
|
|
65
|
-
|
|
66
|
-
// Check if there's a try statement wrapping the content
|
|
67
|
-
const hasTry = body.some((stmt) => stmt.type === "TryStatement");
|
|
68
|
-
if (hasTry) return true;
|
|
69
|
-
|
|
70
|
-
// Check if all await expressions are in try blocks
|
|
71
|
-
let allAwaitsProtected = true;
|
|
72
|
-
const awaits = [];
|
|
73
|
-
|
|
74
|
-
traverse(
|
|
75
|
-
node,
|
|
76
|
-
{
|
|
77
|
-
AwaitExpression(path) {
|
|
78
|
-
awaits.push(path);
|
|
79
|
-
if (!isWrappedInTryCatch(path)) {
|
|
80
|
-
allAwaitsProtected = false;
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
null,
|
|
85
|
-
{ node }
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
return awaits.length === 0 || allAwaitsProtected;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Create a try-catch wrapper
|
|
93
|
-
*/
|
|
94
|
-
function createTryCatchWrapper(statements) {
|
|
95
|
-
return t.tryStatement(
|
|
96
|
-
t.blockStatement(statements),
|
|
97
|
-
t.catchClause(
|
|
98
|
-
t.identifier("error"),
|
|
99
|
-
t.blockStatement([
|
|
100
|
-
t.expressionStatement(
|
|
101
|
-
t.callExpression(
|
|
102
|
-
t.memberExpression(t.identifier("console"), t.identifier("error")),
|
|
103
|
-
[t.stringLiteral("Error:"), t.identifier("error")]
|
|
104
|
-
)
|
|
105
|
-
),
|
|
106
|
-
t.throwStatement(t.identifier("error")),
|
|
107
|
-
])
|
|
108
|
-
)
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Parse code into AST
|
|
114
|
-
*/
|
|
115
|
-
function parseCode(code, filePath) {
|
|
116
|
-
const isTypeScript = filePath.endsWith(".ts") || filePath.endsWith(".tsx");
|
|
117
|
-
const plugins = ["decorators-legacy", "classProperties"];
|
|
118
|
-
|
|
119
|
-
if (isTypeScript) plugins.push("typescript");
|
|
120
|
-
plugins.push("jsx");
|
|
121
|
-
|
|
122
|
-
return parse(code, {
|
|
123
|
-
sourceType: "module",
|
|
124
|
-
plugins,
|
|
125
|
-
errorRecovery: true,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Process a single file
|
|
131
|
-
*/
|
|
132
|
-
function processFile(filePath, options = {}) {
|
|
133
|
-
const { dryRun = true, aggressive = false } = options;
|
|
134
|
-
const changes = [];
|
|
135
|
-
|
|
136
|
-
const code = readFileSafeSync(filePath);
|
|
137
|
-
if (!code) return { changes: [], modified: false };
|
|
138
|
-
|
|
139
|
-
// Quick check
|
|
140
|
-
if (!code.includes("async") && !code.includes("await") && !code.includes(".then(")) {
|
|
141
|
-
return { changes: [], modified: false };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let ast;
|
|
145
|
-
try {
|
|
146
|
-
ast = parseCode(code, filePath);
|
|
147
|
-
} catch (err) {
|
|
148
|
-
return { changes: [], modified: false, error: `Parse error: ${err.message}` };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
let modified = false;
|
|
152
|
-
const relativePath = path.relative(process.cwd(), filePath);
|
|
153
|
-
|
|
154
|
-
traverse(ast, {
|
|
155
|
-
// Check async functions
|
|
156
|
-
"FunctionDeclaration|ArrowFunctionExpression|FunctionExpression"(nodePath) {
|
|
157
|
-
const { node } = nodePath;
|
|
158
|
-
if (!node.async) return;
|
|
159
|
-
|
|
160
|
-
if (!asyncFunctionHasTryCatch(node)) {
|
|
161
|
-
const line = node.loc?.start?.line || "?";
|
|
162
|
-
const name = node.id?.name || "(anonymous)";
|
|
163
|
-
|
|
164
|
-
changes.push({
|
|
165
|
-
file: relativePath,
|
|
166
|
-
line,
|
|
167
|
-
type: "unhandled-async",
|
|
168
|
-
detail: `Async function '${name}' has unhandled await expressions`,
|
|
169
|
-
suggestion: "Add try-catch to handle potential errors",
|
|
170
|
-
autoFixed: false, // Suggest only, don't auto-fix by default
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Auto-fix in aggressive mode
|
|
174
|
-
if (aggressive && node.body?.type === "BlockStatement" && node.body.body.length > 0) {
|
|
175
|
-
const wrapped = createTryCatchWrapper([...node.body.body]);
|
|
176
|
-
node.body.body = [wrapped];
|
|
177
|
-
modified = true;
|
|
178
|
-
changes[changes.length - 1].autoFixed = true;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
// Check Promise.then() without .catch()
|
|
184
|
-
CallExpression(nodePath) {
|
|
185
|
-
const { node } = nodePath;
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
node.callee.type === "MemberExpression" &&
|
|
189
|
-
node.callee.property?.name === "then"
|
|
190
|
-
) {
|
|
191
|
-
// Check if there's a .catch() chained
|
|
192
|
-
const parent = nodePath.parent;
|
|
193
|
-
const hasCatch =
|
|
194
|
-
parent?.type === "CallExpression" &&
|
|
195
|
-
parent.callee?.property?.name === "catch";
|
|
196
|
-
|
|
197
|
-
// Also check if the .then result is further chained
|
|
198
|
-
if (!hasCatch && !hasChainedCatch(parent)) {
|
|
199
|
-
const line = node.loc?.start?.line || "?";
|
|
200
|
-
|
|
201
|
-
changes.push({
|
|
202
|
-
file: relativePath,
|
|
203
|
-
line,
|
|
204
|
-
type: "unhandled-promise",
|
|
205
|
-
detail: "Promise .then() without .catch() handler",
|
|
206
|
-
suggestion: "Add .catch() to handle rejections",
|
|
207
|
-
autoFixed: false,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
// Write changes if modified
|
|
215
|
-
if (modified && !dryRun) {
|
|
216
|
-
try {
|
|
217
|
-
const output = generate(ast, { retainLines: true }, code);
|
|
218
|
-
fs.writeFileSync(filePath, output.code, "utf8");
|
|
219
|
-
} catch (err) {
|
|
220
|
-
return { changes, modified: false, error: `Generate error: ${err.message}` };
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return { changes, modified };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Wrap async handlers in all files
|
|
229
|
-
*/
|
|
230
|
-
async function wrapAsyncHandlers(targetPath, options = {}) {
|
|
231
|
-
const { dryRun = true, aggressive = false } = options;
|
|
232
|
-
|
|
233
|
-
const pattern = /\.(js|jsx|ts|tsx|mjs)$/;
|
|
234
|
-
const files = findAllFilesSync(targetPath, pattern);
|
|
235
|
-
|
|
236
|
-
const allChanges = [];
|
|
237
|
-
const modifiedFiles = [];
|
|
238
|
-
const errors = [];
|
|
239
|
-
|
|
240
|
-
for (const file of files) {
|
|
241
|
-
const result = processFile(file, { dryRun, aggressive });
|
|
242
|
-
|
|
243
|
-
if (result.error) {
|
|
244
|
-
errors.push({ file, error: result.error });
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (result.changes.length > 0) {
|
|
248
|
-
allChanges.push(...result.changes);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (result.modified) {
|
|
252
|
-
modifiedFiles.push(path.relative(process.cwd(), file));
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return {
|
|
257
|
-
changes: allChanges,
|
|
258
|
-
files: modifiedFiles,
|
|
259
|
-
errors,
|
|
260
|
-
summary: {
|
|
261
|
-
filesScanned: files.length,
|
|
262
|
-
filesModified: modifiedFiles.length,
|
|
263
|
-
issuesFound: allChanges.length,
|
|
264
|
-
autoFixed: allChanges.filter((c) => c.autoFixed).length,
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
module.exports = {
|
|
270
|
-
wrapAsyncHandlers,
|
|
271
|
-
processFile,
|
|
272
|
-
isWrappedInTryCatch,
|
|
273
|
-
};
|