ally-a11y 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ACCESSIBILITY.md +205 -0
- package/LICENSE +21 -0
- package/README.md +940 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +528 -0
- package/dist/commands/audit-palette.d.ts +18 -0
- package/dist/commands/audit-palette.js +613 -0
- package/dist/commands/auto-pr.d.ts +19 -0
- package/dist/commands/auto-pr.js +434 -0
- package/dist/commands/badge.d.ts +11 -0
- package/dist/commands/badge.js +143 -0
- package/dist/commands/completion.d.ts +4 -0
- package/dist/commands/completion.js +185 -0
- package/dist/commands/crawl.d.ts +12 -0
- package/dist/commands/crawl.js +249 -0
- package/dist/commands/doctor.d.ts +5 -0
- package/dist/commands/doctor.js +233 -0
- package/dist/commands/explain.d.ts +12 -0
- package/dist/commands/explain.js +233 -0
- package/dist/commands/fix.d.ts +13 -0
- package/dist/commands/fix.js +668 -0
- package/dist/commands/health.d.ts +11 -0
- package/dist/commands/health.js +367 -0
- package/dist/commands/history.d.ts +10 -0
- package/dist/commands/history.js +191 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +164 -0
- package/dist/commands/learn.d.ts +8 -0
- package/dist/commands/learn.js +592 -0
- package/dist/commands/pr-check.d.ts +12 -0
- package/dist/commands/pr-check.js +270 -0
- package/dist/commands/report.d.ts +11 -0
- package/dist/commands/report.js +375 -0
- package/dist/commands/scan-storybook.d.ts +18 -0
- package/dist/commands/scan-storybook.js +402 -0
- package/dist/commands/scan.d.ts +25 -0
- package/dist/commands/scan.js +673 -0
- package/dist/commands/stats.d.ts +5 -0
- package/dist/commands/stats.js +137 -0
- package/dist/commands/tree.d.ts +12 -0
- package/dist/commands/tree.js +635 -0
- package/dist/commands/triage.d.ts +13 -0
- package/dist/commands/triage.js +327 -0
- package/dist/commands/watch.d.ts +17 -0
- package/dist/commands/watch.js +302 -0
- package/dist/types/index.d.ts +60 -0
- package/dist/types/index.js +4 -0
- package/dist/utils/baseline.d.ts +62 -0
- package/dist/utils/baseline.js +169 -0
- package/dist/utils/browser.d.ts +78 -0
- package/dist/utils/browser.js +239 -0
- package/dist/utils/cache.d.ts +76 -0
- package/dist/utils/cache.js +178 -0
- package/dist/utils/config.d.ts +102 -0
- package/dist/utils/config.js +237 -0
- package/dist/utils/converters.d.ts +77 -0
- package/dist/utils/converters.js +200 -0
- package/dist/utils/copilot.d.ts +36 -0
- package/dist/utils/copilot.js +139 -0
- package/dist/utils/detect.d.ts +22 -0
- package/dist/utils/detect.js +197 -0
- package/dist/utils/enhanced-errors.d.ts +46 -0
- package/dist/utils/enhanced-errors.js +295 -0
- package/dist/utils/errors.d.ts +31 -0
- package/dist/utils/errors.js +149 -0
- package/dist/utils/fix-patterns.d.ts +56 -0
- package/dist/utils/fix-patterns.js +529 -0
- package/dist/utils/history-tracking.d.ts +94 -0
- package/dist/utils/history-tracking.js +230 -0
- package/dist/utils/history.d.ts +42 -0
- package/dist/utils/history.js +255 -0
- package/dist/utils/impact-scores.d.ts +44 -0
- package/dist/utils/impact-scores.js +257 -0
- package/dist/utils/retry.d.ts +24 -0
- package/dist/utils/retry.js +76 -0
- package/dist/utils/scanner.d.ts +74 -0
- package/dist/utils/scanner.js +606 -0
- package/dist/utils/scanner.test.d.ts +4 -0
- package/dist/utils/scanner.test.js +162 -0
- package/dist/utils/ui.d.ts +44 -0
- package/dist/utils/ui.js +276 -0
- package/mcp-server/dist/index.d.ts +8 -0
- package/mcp-server/dist/index.js +1923 -0
- package/package.json +88 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Error Messages System
|
|
3
|
+
*
|
|
4
|
+
* Provides actionable, helpful error messages with:
|
|
5
|
+
* - Clear explanations of what went wrong
|
|
6
|
+
* - Step-by-step remediation guidance
|
|
7
|
+
* - Code examples
|
|
8
|
+
* - Links to documentation
|
|
9
|
+
* - Context-specific suggestions
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Error messages for common issues
|
|
13
|
+
*/
|
|
14
|
+
export const ERROR_MESSAGES = {
|
|
15
|
+
// Installation & Setup Errors
|
|
16
|
+
NO_FILES_FOUND: {
|
|
17
|
+
title: 'No HTML files found',
|
|
18
|
+
message: 'ally couldn\'t find any HTML files to scan in the specified directory.',
|
|
19
|
+
remediation: [
|
|
20
|
+
'1. Check that the path is correct: `ally scan ./src`',
|
|
21
|
+
'2. Verify HTML files exist in the directory: `ls ./src/**/*.html`',
|
|
22
|
+
'3. For React/Vue/Svelte apps, scan build output: `ally scan ./dist`',
|
|
23
|
+
'4. Or scan your dev server: `ally scan --url http://localhost:3000`',
|
|
24
|
+
],
|
|
25
|
+
example: '# For Next.js apps\nnpm run build\nally scan .next/\n\n# Or scan live dev server\nally scan --url http://localhost:3000',
|
|
26
|
+
docs: 'https://github.com/forbiddenlink/ally#scanning-frameworks',
|
|
27
|
+
tip: 'Most modern frameworks generate HTML at build time or runtime - scan the output, not source!',
|
|
28
|
+
},
|
|
29
|
+
BROWSER_LAUNCH_FAILED: {
|
|
30
|
+
title: 'Browser failed to launch',
|
|
31
|
+
message: 'Puppeteer couldn\'t start the browser for scanning.',
|
|
32
|
+
remediation: [
|
|
33
|
+
'1. Install Chromium: `npx puppeteer browsers install chrome`',
|
|
34
|
+
'2. Check system dependencies (Linux): `apt-get install -y libnss3 libatk1.0-0`',
|
|
35
|
+
'3. Try a different browser: `ally scan --browser firefox`',
|
|
36
|
+
'4. For Docker/CI: Add `--no-sandbox` flag (not recommended for local)',
|
|
37
|
+
],
|
|
38
|
+
example: '# Install browser\nnpx puppeteer browsers install chrome\n\n# Scan with specific browser\nally scan --browser firefox',
|
|
39
|
+
docs: 'https://pptr.dev/troubleshooting',
|
|
40
|
+
tip: 'Running in CI? Make sure browser dependencies are installed in your Docker image.',
|
|
41
|
+
},
|
|
42
|
+
PAGE_LOAD_TIMEOUT: {
|
|
43
|
+
title: 'Page load timeout',
|
|
44
|
+
message: 'The page took too long to load and the scan timed out.',
|
|
45
|
+
remediation: [
|
|
46
|
+
'1. Increase timeout: `ally scan --timeout 60000` (60 seconds)',
|
|
47
|
+
'2. Check the URL is accessible: `curl -I https://example.com`',
|
|
48
|
+
'3. Verify network connectivity and firewall rules',
|
|
49
|
+
'4. For local dev servers, ensure they\'re running before scanning',
|
|
50
|
+
],
|
|
51
|
+
example: '# Increase timeout for slow pages\nally scan --url https://example.com --timeout 60000\n\n# Start dev server first\nnpm run dev &\nsleep 5\nally scan --url http://localhost:3000',
|
|
52
|
+
docs: 'https://github.com/forbiddenlink/ally#troubleshooting',
|
|
53
|
+
tip: 'Large pages or slow connections? Try scanning individual routes instead of the homepage.',
|
|
54
|
+
},
|
|
55
|
+
INVALID_URL: {
|
|
56
|
+
title: 'Invalid URL',
|
|
57
|
+
message: 'The URL format is invalid or not supported.',
|
|
58
|
+
remediation: [
|
|
59
|
+
'1. URLs must start with http:// or https://',
|
|
60
|
+
'2. Check for typos: `https://` not `http:/`',
|
|
61
|
+
'3. For local files, use file paths: `ally scan ./dist/index.html`',
|
|
62
|
+
'4. For localhost, include port: `http://localhost:3000`',
|
|
63
|
+
],
|
|
64
|
+
example: '# Valid URLs\nally scan --url https://example.com\nally scan --url http://localhost:3000\n\n# Invalid - missing protocol\nally scan --url example.com ✗',
|
|
65
|
+
tip: 'Scanning a local file? Drop the --url flag: `ally scan ./index.html`',
|
|
66
|
+
},
|
|
67
|
+
// Configuration Errors
|
|
68
|
+
INVALID_CONFIG: {
|
|
69
|
+
title: 'Invalid configuration',
|
|
70
|
+
message: 'The .allyrc.json file contains invalid configuration.',
|
|
71
|
+
remediation: [
|
|
72
|
+
'1. Validate JSON syntax: `cat .allyrc.json | jq`',
|
|
73
|
+
'2. Check for typos in property names',
|
|
74
|
+
'3. See valid options: `ally init --help`',
|
|
75
|
+
'4. Delete config to reset: `rm .allyrc.json && ally init`',
|
|
76
|
+
],
|
|
77
|
+
example: '// Valid .allyrc.json\n{\n "scan": {\n "standard": "wcag22aa",\n "threshold": 5\n },\n "fix": {\n "autoApprove": ["image-alt"]\n }\n}',
|
|
78
|
+
docs: 'https://github.com/forbiddenlink/ally#configuration',
|
|
79
|
+
tip: 'Use `ally init` to create a valid config file with sensible defaults.',
|
|
80
|
+
},
|
|
81
|
+
MISSING_COPILOT_CLI: {
|
|
82
|
+
title: 'GitHub Copilot CLI not found',
|
|
83
|
+
message: 'The `copilot` command is not available. Some features require GitHub Copilot CLI.',
|
|
84
|
+
remediation: [
|
|
85
|
+
'1. Install Copilot CLI: `npm install -g @github/copilot-cli`',
|
|
86
|
+
'2. Login: `copilot auth login`',
|
|
87
|
+
'3. Verify: `copilot --version`',
|
|
88
|
+
'4. Or use ally without Copilot: `ally scan` works without it!',
|
|
89
|
+
],
|
|
90
|
+
example: '# Install and setup\nnpm install -g @github/copilot-cli\ncopilot auth login\n\n# Now use AI features\nally explain\nally fix',
|
|
91
|
+
docs: 'https://githubnext.com/projects/copilot-cli',
|
|
92
|
+
tip: 'Basic scanning works without Copilot CLI - only `explain` and `fix` need it for AI features.',
|
|
93
|
+
},
|
|
94
|
+
// Runtime Errors
|
|
95
|
+
PERMISSION_DENIED: {
|
|
96
|
+
title: 'Permission denied',
|
|
97
|
+
message: 'ally doesn\'t have permission to access the file or directory.',
|
|
98
|
+
remediation: [
|
|
99
|
+
'1. Check file permissions: `ls -la /path/to/file`',
|
|
100
|
+
'2. Run with correct user: `sudo ally scan`',
|
|
101
|
+
'3. Fix permissions: `chmod -R 755 ./src`',
|
|
102
|
+
'4. Verify you own the directory: `chown -R $USER ./src`',
|
|
103
|
+
],
|
|
104
|
+
example: '# Fix permissions\nchmod -R 755 ./src\nally scan ./src\n\n# Or run as correct user\nsudo ally scan ./protected',
|
|
105
|
+
tip: 'Never run ally as root unless absolutely necessary - fix permissions instead!',
|
|
106
|
+
},
|
|
107
|
+
OUT_OF_MEMORY: {
|
|
108
|
+
title: 'Out of memory',
|
|
109
|
+
message: 'ally ran out of memory while processing files.',
|
|
110
|
+
remediation: [
|
|
111
|
+
'1. Scan fewer files: `ally scan ./src/components` instead of `./src`',
|
|
112
|
+
'2. Increase Node memory: `NODE_OPTIONS="--max-old-space-size=4096" ally scan`',
|
|
113
|
+
'3. Use ignore patterns: Add large dirs to .allyignore',
|
|
114
|
+
'4. Scan in batches: `ally scan ./src/a* && ally scan ./src/b*`',
|
|
115
|
+
],
|
|
116
|
+
example: '# Increase Node memory limit\nNODE_OPTIONS="--max-old-space-size=4096" ally scan ./src\n\n# Create .allyignore\necho "node_modules/" > .allyignore\necho "dist/" >> .allyignore\necho "coverage/" >> .allyignore',
|
|
117
|
+
docs: 'https://github.com/forbiddenlink/ally#ignore-patterns',
|
|
118
|
+
tip: 'Scanning node_modules? Add it to .allyignore to skip dependency files!',
|
|
119
|
+
},
|
|
120
|
+
// Git/CI Errors
|
|
121
|
+
NOT_A_GIT_REPO: {
|
|
122
|
+
title: 'Not a git repository',
|
|
123
|
+
message: 'Some features require a git repository but none was found.',
|
|
124
|
+
remediation: [
|
|
125
|
+
'1. Initialize git: `git init`',
|
|
126
|
+
'2. Or skip git features: `ally scan` works without git',
|
|
127
|
+
'3. In CI: Clone full repo, not shallow: `git clone --depth 1` causes issues',
|
|
128
|
+
],
|
|
129
|
+
example: '# Initialize git\ngit init\ngit add .\ngit commit -m "Initial commit"\n\n# Now git features work\nally pr-check',
|
|
130
|
+
tip: 'History tracking works better with git - it shows branch and commit for each scan.',
|
|
131
|
+
},
|
|
132
|
+
PR_NOT_FOUND: {
|
|
133
|
+
title: 'Pull request not found',
|
|
134
|
+
message: 'ally couldn\'t detect or access the pull request.',
|
|
135
|
+
remediation: [
|
|
136
|
+
'1. In GitHub Actions: Ensure `gh` CLI is authenticated',
|
|
137
|
+
'2. Set GITHUB_TOKEN: `GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}`',
|
|
138
|
+
'3. Specify PR manually: `ally pr-check --pr 123`',
|
|
139
|
+
'4. Verify repo access: `gh pr list`',
|
|
140
|
+
],
|
|
141
|
+
example: '# GitHub Actions workflow\n- name: Check PR\n env:\n GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n run: ally pr-check',
|
|
142
|
+
docs: 'https://github.com/forbiddenlink/ally#github-actions',
|
|
143
|
+
tip: 'Local testing? Use `ally pr-check --pr 123` to specify the PR number.',
|
|
144
|
+
},
|
|
145
|
+
// Fix Errors
|
|
146
|
+
FIX_FAILED: {
|
|
147
|
+
title: 'Fix failed',
|
|
148
|
+
message: 'ally couldn\'t apply the fix to the file.',
|
|
149
|
+
remediation: [
|
|
150
|
+
'1. Check file is writable: `ls -la /path/to/file`',
|
|
151
|
+
'2. Verify file wasn\'t deleted: `test -f /path/to/file`',
|
|
152
|
+
'3. Try dry-run first: `ally fix --dry-run`',
|
|
153
|
+
'4. Apply fixes manually using the diff shown',
|
|
154
|
+
],
|
|
155
|
+
example: '# Dry-run to preview changes\nally fix --dry-run\n\n# Fix permissions if needed\nchmod 644 ./src/Button.tsx\nally fix',
|
|
156
|
+
tip: 'Files under version control? Check `git status` to see what changed.',
|
|
157
|
+
},
|
|
158
|
+
LOW_CONFIDENCE_FIX: {
|
|
159
|
+
title: 'Low confidence fix',
|
|
160
|
+
message: 'The fix has low confidence and needs manual review.',
|
|
161
|
+
remediation: [
|
|
162
|
+
'1. Review the suggested fix carefully',
|
|
163
|
+
'2. Apply manually if it looks correct',
|
|
164
|
+
'3. Use `--auto` to auto-apply high-confidence fixes only: `ally fix --auto`',
|
|
165
|
+
'4. Or skip low confidence: Press "skip" when prompted',
|
|
166
|
+
],
|
|
167
|
+
example: '# Auto-apply only high-confidence fixes\nally fix --auto\n\n# Or approve each fix interactively\nally fix # Press y/n/skip for each',
|
|
168
|
+
tip: 'watch --fix-on-save only applies fixes ≥90% confidence - safe by default!',
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Get enhanced error message
|
|
173
|
+
*/
|
|
174
|
+
export function getEnhancedError(errorCode) {
|
|
175
|
+
return ERROR_MESSAGES[errorCode] || null;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Format enhanced error for console output
|
|
179
|
+
*/
|
|
180
|
+
export function formatEnhancedError(error) {
|
|
181
|
+
let output = '';
|
|
182
|
+
// Title with emoji
|
|
183
|
+
output += `\n❌ ${error.title}\n\n`;
|
|
184
|
+
// Message
|
|
185
|
+
output += `${error.message}\n\n`;
|
|
186
|
+
// Remediation steps
|
|
187
|
+
output += `🔧 How to fix:\n`;
|
|
188
|
+
for (const step of error.remediation) {
|
|
189
|
+
output += ` ${step}\n`;
|
|
190
|
+
}
|
|
191
|
+
// Example
|
|
192
|
+
if (error.example) {
|
|
193
|
+
output += `\n💡 Example:\n`;
|
|
194
|
+
const lines = error.example.split('\n');
|
|
195
|
+
for (const line of lines) {
|
|
196
|
+
output += ` ${line}\n`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Tip
|
|
200
|
+
if (error.tip) {
|
|
201
|
+
output += `\n💭 Tip: ${error.tip}\n`;
|
|
202
|
+
}
|
|
203
|
+
// Docs
|
|
204
|
+
if (error.docs) {
|
|
205
|
+
output += `\n📖 Documentation: ${error.docs}\n`;
|
|
206
|
+
}
|
|
207
|
+
return output;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Common error patterns and their codes
|
|
211
|
+
*/
|
|
212
|
+
export function detectErrorCode(error) {
|
|
213
|
+
const message = error.message.toLowerCase();
|
|
214
|
+
// Installation & Setup
|
|
215
|
+
if (message.includes('no html files') || message.includes('no files found')) {
|
|
216
|
+
return 'NO_FILES_FOUND';
|
|
217
|
+
}
|
|
218
|
+
if (message.includes('browser') && message.includes('launch')) {
|
|
219
|
+
return 'BROWSER_LAUNCH_FAILED';
|
|
220
|
+
}
|
|
221
|
+
if (message.includes('timeout') || message.includes('timed out')) {
|
|
222
|
+
return 'PAGE_LOAD_TIMEOUT';
|
|
223
|
+
}
|
|
224
|
+
if (message.includes('invalid url') || message.includes('malformed url')) {
|
|
225
|
+
return 'INVALID_URL';
|
|
226
|
+
}
|
|
227
|
+
// Configuration
|
|
228
|
+
if (message.includes('invalid config') || message.includes('allyrc')) {
|
|
229
|
+
return 'INVALID_CONFIG';
|
|
230
|
+
}
|
|
231
|
+
if (message.includes('copilot') && message.includes('not found')) {
|
|
232
|
+
return 'MISSING_COPILOT_CLI';
|
|
233
|
+
}
|
|
234
|
+
// Runtime
|
|
235
|
+
if (message.includes('permission denied') || message.includes('eacces')) {
|
|
236
|
+
return 'PERMISSION_DENIED';
|
|
237
|
+
}
|
|
238
|
+
if (message.includes('out of memory') || message.includes('enomem')) {
|
|
239
|
+
return 'OUT_OF_MEMORY';
|
|
240
|
+
}
|
|
241
|
+
// Git/CI
|
|
242
|
+
if (message.includes('not a git repository')) {
|
|
243
|
+
return 'NOT_A_GIT_REPO';
|
|
244
|
+
}
|
|
245
|
+
if (message.includes('pull request') && message.includes('not found')) {
|
|
246
|
+
return 'PR_NOT_FOUND';
|
|
247
|
+
}
|
|
248
|
+
// Fix
|
|
249
|
+
if (message.includes('fix failed') || message.includes('failed to apply')) {
|
|
250
|
+
return 'FIX_FAILED';
|
|
251
|
+
}
|
|
252
|
+
if (message.includes('low confidence')) {
|
|
253
|
+
return 'LOW_CONFIDENCE_FIX';
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Show enhanced error and exit
|
|
259
|
+
*/
|
|
260
|
+
export function showEnhancedErrorAndExit(errorCode, exitCode = 1) {
|
|
261
|
+
const enhanced = getEnhancedError(errorCode);
|
|
262
|
+
if (enhanced) {
|
|
263
|
+
const formatted = formatEnhancedError(enhanced);
|
|
264
|
+
console.error(formatted);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
console.error(`\n❌ Error code: ${errorCode}\n`);
|
|
268
|
+
}
|
|
269
|
+
process.exit(exitCode);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Throw enhanced error (for backwards compatibility)
|
|
273
|
+
*/
|
|
274
|
+
export function throwEnhancedError(errorCode, originalError) {
|
|
275
|
+
showEnhancedErrorAndExit(errorCode, 1);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Handle error with enhanced message
|
|
279
|
+
*/
|
|
280
|
+
export function handleErrorWithEnhancement(error) {
|
|
281
|
+
const errorCode = detectErrorCode(error);
|
|
282
|
+
if (errorCode) {
|
|
283
|
+
const enhanced = getEnhancedError(errorCode);
|
|
284
|
+
if (enhanced) {
|
|
285
|
+
console.error(formatEnhancedError(enhanced));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Fallback to original error
|
|
290
|
+
console.error(`\n❌ Error: ${error.message}\n`);
|
|
291
|
+
if (error.stack) {
|
|
292
|
+
console.error(`\n📋 Stack trace:\n${error.stack}\n`);
|
|
293
|
+
}
|
|
294
|
+
console.error(`\n💭 Tip: Run with --verbose for more details\n`);
|
|
295
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities with contextual hints for better user experience
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Suggest running 'ally scan' when scan results are missing
|
|
6
|
+
*/
|
|
7
|
+
export declare function suggestInit(reportPath?: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Suggest using --url flag when no HTML files are found
|
|
10
|
+
*/
|
|
11
|
+
export declare function suggestUrl(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Diagnose Puppeteer errors and return a helpful message
|
|
14
|
+
*/
|
|
15
|
+
export declare function diagnosePuppeteer(error: Error): string;
|
|
16
|
+
/**
|
|
17
|
+
* Check if an error is a Puppeteer-related error
|
|
18
|
+
*/
|
|
19
|
+
export declare function isPuppeteerError(error: Error): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Format an error with context and suggestions in a nice box
|
|
22
|
+
*/
|
|
23
|
+
export declare function formatError(error: Error, context?: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* Format a scan failure with helpful context
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatScanError(error: Error, target: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Provide helpful message when scan results file is corrupted
|
|
30
|
+
*/
|
|
31
|
+
export declare function suggestRescan(reportPath: string): void;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities with contextual hints for better user experience
|
|
3
|
+
*/
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import boxen from 'boxen';
|
|
6
|
+
import { printError, printInfo, printWarning } from './ui.js';
|
|
7
|
+
/**
|
|
8
|
+
* Suggest running 'ally scan' when scan results are missing
|
|
9
|
+
*/
|
|
10
|
+
export function suggestInit(reportPath) {
|
|
11
|
+
printError(`No scan results found`);
|
|
12
|
+
printInfo(`Run 'ally scan' first to generate results`);
|
|
13
|
+
if (reportPath) {
|
|
14
|
+
console.log(chalk.dim(` Expected file: ${reportPath}`));
|
|
15
|
+
}
|
|
16
|
+
console.log();
|
|
17
|
+
console.log(chalk.dim('Quick start:'));
|
|
18
|
+
console.log(chalk.cyan(' ally scan ') + chalk.dim('# Scan HTML files in current directory'));
|
|
19
|
+
console.log(chalk.cyan(' ally scan --url <url> ') + chalk.dim('# Scan a running web app'));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Suggest using --url flag when no HTML files are found
|
|
23
|
+
*/
|
|
24
|
+
export function suggestUrl() {
|
|
25
|
+
printWarning('No HTML files found to scan');
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(chalk.dim('For static HTML projects:'));
|
|
28
|
+
console.log(chalk.cyan(' ally scan ./public ') + chalk.dim('# Scan a specific directory'));
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(chalk.dim('For React, Vue, Next.js, or other SPA projects:'));
|
|
31
|
+
console.log(chalk.cyan(' ally scan --url http://localhost:3000'));
|
|
32
|
+
console.log();
|
|
33
|
+
printInfo('Start your dev server first, then scan the running app');
|
|
34
|
+
}
|
|
35
|
+
const PUPPETEER_DIAGNOSTICS = [
|
|
36
|
+
{
|
|
37
|
+
pattern: /Could not find (Chrome|Chromium|browser)/i,
|
|
38
|
+
message: 'Chrome browser not found',
|
|
39
|
+
suggestion: "Run 'npx puppeteer browsers install chrome' to download Chrome",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
pattern: /No usable sandbox/i,
|
|
43
|
+
message: 'Chrome sandbox error (common on Linux/CI)',
|
|
44
|
+
suggestion: "Try running with '--no-sandbox' flag or configure user namespaces",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /ECONNREFUSED|ERR_CONNECTION_REFUSED/i,
|
|
48
|
+
message: 'Could not connect to the URL',
|
|
49
|
+
suggestion: 'Check that the server is running and the URL is correct',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
pattern: /Navigation timeout|TimeoutError|Timeout exceeded/i,
|
|
53
|
+
message: 'Page took too long to load',
|
|
54
|
+
suggestion: 'Check the URL is accessible, or try scanning a simpler page first',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
pattern: /net::ERR_NAME_NOT_RESOLVED/i,
|
|
58
|
+
message: 'Could not resolve hostname',
|
|
59
|
+
suggestion: 'Check the URL is spelled correctly and the domain exists',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
pattern: /net::ERR_CERT/i,
|
|
63
|
+
message: 'SSL certificate error',
|
|
64
|
+
suggestion: 'The site has an invalid SSL certificate. Try using http:// instead of https://',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
pattern: /Protocol error|Target closed/i,
|
|
68
|
+
message: 'Browser crashed or was closed unexpectedly',
|
|
69
|
+
suggestion: 'Try running the scan again. If the issue persists, check system memory',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
pattern: /EPERM|EACCES|Permission denied/i,
|
|
73
|
+
message: 'Permission denied',
|
|
74
|
+
suggestion: 'Check file permissions or try running with elevated privileges',
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Diagnose Puppeteer errors and return a helpful message
|
|
79
|
+
*/
|
|
80
|
+
export function diagnosePuppeteer(error) {
|
|
81
|
+
const errorMessage = error.message || String(error);
|
|
82
|
+
for (const diagnostic of PUPPETEER_DIAGNOSTICS) {
|
|
83
|
+
if (diagnostic.pattern.test(errorMessage)) {
|
|
84
|
+
return `${diagnostic.message}\n\nSuggestion: ${diagnostic.suggestion}`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Generic fallback for unknown Puppeteer errors
|
|
88
|
+
return `Browser error: ${errorMessage}\n\nIf this persists, try:\n - Updating Puppeteer: npm update puppeteer\n - Reinstalling Chrome: npx puppeteer browsers install chrome`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if an error is a Puppeteer-related error
|
|
92
|
+
*/
|
|
93
|
+
export function isPuppeteerError(error) {
|
|
94
|
+
const errorMessage = error.message || String(error);
|
|
95
|
+
return PUPPETEER_DIAGNOSTICS.some((d) => d.pattern.test(errorMessage));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Format an error with context and suggestions in a nice box
|
|
99
|
+
*/
|
|
100
|
+
export function formatError(error, context) {
|
|
101
|
+
const errorMessage = error.message || String(error);
|
|
102
|
+
// Check for Puppeteer errors first
|
|
103
|
+
if (isPuppeteerError(error)) {
|
|
104
|
+
const diagnosis = diagnosePuppeteer(error);
|
|
105
|
+
console.log(boxen(diagnosis, {
|
|
106
|
+
padding: 1,
|
|
107
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
108
|
+
borderStyle: 'round',
|
|
109
|
+
borderColor: 'red',
|
|
110
|
+
title: 'Browser Error',
|
|
111
|
+
titleAlignment: 'left',
|
|
112
|
+
}));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Build error display
|
|
116
|
+
let content = chalk.red(errorMessage);
|
|
117
|
+
if (context) {
|
|
118
|
+
content = `${chalk.dim(context)}\n\n${content}`;
|
|
119
|
+
}
|
|
120
|
+
console.log(boxen(content, {
|
|
121
|
+
padding: 1,
|
|
122
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
123
|
+
borderStyle: 'round',
|
|
124
|
+
borderColor: 'red',
|
|
125
|
+
title: 'Error',
|
|
126
|
+
titleAlignment: 'left',
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Format a scan failure with helpful context
|
|
131
|
+
*/
|
|
132
|
+
export function formatScanError(error, target) {
|
|
133
|
+
if (isPuppeteerError(error)) {
|
|
134
|
+
formatError(error, `Failed to scan: ${target}`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
printError(`Failed to scan ${target}`);
|
|
138
|
+
console.log(chalk.dim(` ${error.message}`));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Provide helpful message when scan results file is corrupted
|
|
143
|
+
*/
|
|
144
|
+
export function suggestRescan(reportPath) {
|
|
145
|
+
printError('Scan results file appears to be corrupted');
|
|
146
|
+
printInfo(`Delete ${reportPath} and run 'ally scan' again`);
|
|
147
|
+
console.log();
|
|
148
|
+
console.log(chalk.cyan(` rm ${reportPath} && ally scan`));
|
|
149
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-fix patterns for common accessibility violations
|
|
3
|
+
*
|
|
4
|
+
* Each pattern takes the violation node HTML and returns a fixed version,
|
|
5
|
+
* or null if it can't be auto-fixed.
|
|
6
|
+
*/
|
|
7
|
+
import type { Violation } from '../types/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Helper to extract attribute value from HTML string
|
|
10
|
+
*/
|
|
11
|
+
export declare function getAttr(html: string, attr: string): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Helper to check if element has an attribute
|
|
14
|
+
*/
|
|
15
|
+
export declare function hasAttr(html: string, attr: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Helper to add attribute to opening tag
|
|
18
|
+
*/
|
|
19
|
+
export declare function addAttr(html: string, tag: string, attr: string, value: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Helper to extract tag name from HTML
|
|
22
|
+
*/
|
|
23
|
+
export declare function getTagName(html: string): string | null;
|
|
24
|
+
/**
|
|
25
|
+
* Helper to infer a description from existing attributes or content
|
|
26
|
+
*/
|
|
27
|
+
export declare function inferDescription(html: string, fallback: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Fix pattern function type
|
|
30
|
+
*/
|
|
31
|
+
export type FixPatternFn = (html: string, violation: Violation) => string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Confidence scores for each fix pattern (0.0 to 1.0)
|
|
34
|
+
*
|
|
35
|
+
* High confidence (0.9+): Simple attribute additions (alt, aria-label, lang)
|
|
36
|
+
* Medium confidence (0.7-0.9): Structural changes, role assignments
|
|
37
|
+
* Lower confidence (0.5-0.7): Complex fixes requiring context
|
|
38
|
+
*/
|
|
39
|
+
export declare const FIX_CONFIDENCE: Record<string, number>;
|
|
40
|
+
/**
|
|
41
|
+
* Get confidence score for a fix pattern
|
|
42
|
+
* @returns Confidence score between 0 and 1, or null if pattern not found
|
|
43
|
+
*/
|
|
44
|
+
export declare function getFixConfidence(patternId: string): number | null;
|
|
45
|
+
/**
|
|
46
|
+
* Get confidence level category
|
|
47
|
+
*/
|
|
48
|
+
export declare function getConfidenceLevel(confidence: number): 'high' | 'medium' | 'low';
|
|
49
|
+
/**
|
|
50
|
+
* Auto-fix patterns for common accessibility violations
|
|
51
|
+
*/
|
|
52
|
+
export declare const FIX_PATTERNS: Record<string, FixPatternFn>;
|
|
53
|
+
/**
|
|
54
|
+
* Generate a suggested fix for a violation
|
|
55
|
+
*/
|
|
56
|
+
export declare function generateSuggestedFix(violation: Violation, html: string): string | null;
|