wcag-scanner 1.2.60 → 1.2.61
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/README.md +19 -6
- package/dist/ai/suggestions.d.ts +11 -0
- package/dist/ai/suggestions.js +103 -0
- package/dist/cli/index.js +0 -67
- package/dist/index.js +0 -31
- package/dist/react/WcagDevOverlay.d.ts +13 -0
- package/dist/react/WcagDevOverlay.js +498 -0
- package/dist/react/browserScanner.d.ts +18 -0
- package/dist/react/browserScanner.js +149 -0
- package/dist/react/index.d.ts +5 -0
- package/dist/react/index.js +9 -0
- package/dist/react/init.d.ts +16 -0
- package/dist/react/init.js +90 -0
- package/dist/rules/aria.js +6 -16
- package/dist/rules/contrast.js +2 -1
- package/dist/rules/forms.js +1 -1
- package/dist/scanner.js +4 -5
- package/dist/types/index.d.ts +12 -5
- package/dist/wasm/index.js +3 -35
- package/package.json +25 -12
- package/dist/pkg/package.json +0 -11
- package/dist/pkg/scrapper.d.ts +0 -4
- package/dist/pkg/scrapper.js +0 -614
- package/dist/pkg/scrapper_bg.wasm +0 -0
- package/dist/pkg/scrapper_bg.wasm.d.ts +0 -16
package/README.md
CHANGED
|
@@ -32,17 +32,30 @@ WCAG Scanner is a powerful accessibility testing tool that helps developers iden
|
|
|
32
32
|
|
|
33
33
|
## 📦 Installation
|
|
34
34
|
|
|
35
|
-
### Using npm
|
|
36
|
-
|
|
37
35
|
```bash
|
|
38
|
-
npm
|
|
39
|
-
|
|
36
|
+
# npm
|
|
37
|
+
npm install wcag-scanner
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
```bash
|
|
39
|
+
# yarn
|
|
43
40
|
yarn add wcag-scanner
|
|
41
|
+
|
|
42
|
+
# pnpm
|
|
43
|
+
pnpm add wcag-scanner
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
> **React users:** React and React DOM are peer dependencies and are already installed in your project — no extra steps needed.
|
|
47
|
+
|
|
48
|
+
### For the React Dev Overlay
|
|
49
|
+
|
|
50
|
+
Add one line to your app entry point (`main.ts`, `index.js`, `App.tsx`, etc.):
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { initWcagOverlay } from 'wcag-scanner/react';
|
|
54
|
+
initWcagOverlay(); // shows a live WCAG inspector in the browser, dev only
|
|
44
55
|
```
|
|
45
56
|
|
|
57
|
+
The overlay is automatically disabled in production (`NODE_ENV=production`) and never ships to your users.
|
|
58
|
+
|
|
46
59
|
## How to use
|
|
47
60
|
### CLI Usage Example:
|
|
48
61
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Violation, FixSuggestion } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Generate fix suggestions for accessibility violations
|
|
4
|
+
* @param violation WCAG violation to fix
|
|
5
|
+
* @returns Promise<FixSuggestion> Suggested fix
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateFixSuggestion(violation: Violation): Promise<FixSuggestion>;
|
|
8
|
+
declare const _default: {
|
|
9
|
+
generateFixSuggestion: typeof generateFixSuggestion;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateFixSuggestion = generateFixSuggestion;
|
|
7
|
+
const generative_ai_1 = require("@google/generative-ai");
|
|
8
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
+
/**
|
|
10
|
+
* @TODO working on AI-powered suggestions, it's still working in progress.
|
|
11
|
+
*/
|
|
12
|
+
// Load environment variables from .env file
|
|
13
|
+
dotenv_1.default.config();
|
|
14
|
+
// Get API key from environment
|
|
15
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
16
|
+
// Initialize Gemini if API key is available
|
|
17
|
+
let genAI = null;
|
|
18
|
+
let model = null;
|
|
19
|
+
if (apiKey) {
|
|
20
|
+
try {
|
|
21
|
+
genAI = new generative_ai_1.GoogleGenerativeAI(apiKey);
|
|
22
|
+
model = genAI.getGenerativeModel({ model: 'gemini-1.0-pro' });
|
|
23
|
+
console.log('Gemini AI initialized');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error('Error initializing Gemini AI:', error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generate fix suggestions for accessibility violations
|
|
31
|
+
* @param violation WCAG violation to fix
|
|
32
|
+
* @returns Promise<FixSuggestion> Suggested fix
|
|
33
|
+
*/
|
|
34
|
+
async function generateFixSuggestion(violation) {
|
|
35
|
+
// Ig Gemini is not available, use rule-based suggestion
|
|
36
|
+
if (!model) {
|
|
37
|
+
console.log('Gemini AI not available, using rule-based suggestion');
|
|
38
|
+
return generateRuleBasedSuggestion(violation);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
// Create prompt for Gemini
|
|
42
|
+
const prompt = `
|
|
43
|
+
As a web accessibility expert, I need a fix for this WCAG issue:
|
|
44
|
+
Rule: ${violation.rule || ''}
|
|
45
|
+
Description: ${violation.description || ''}
|
|
46
|
+
|
|
47
|
+
HTML code with issue:
|
|
48
|
+
${violation.snippet || ''}
|
|
49
|
+
|
|
50
|
+
Please provide a corrected version of the code and a brief explanation.
|
|
51
|
+
`;
|
|
52
|
+
// Generate content with Gemini
|
|
53
|
+
const result = await model.generateContent(prompt);
|
|
54
|
+
const response = result.response.text();
|
|
55
|
+
// Extract code and explanation from response
|
|
56
|
+
const codeMatch = response.match(/```(?:html)?\s*([\s\S]*?)\s*```/);
|
|
57
|
+
const code = codeMatch ? codeMatch[1].trim() : '';
|
|
58
|
+
// Remove code block for clean explanation
|
|
59
|
+
const explanation = response
|
|
60
|
+
.replace(/```(?:html)?\s*[\s\S]*?\s*```/g, '')
|
|
61
|
+
.trim();
|
|
62
|
+
return {
|
|
63
|
+
code: code || 'Unable to generate specific code fix',
|
|
64
|
+
description: 'AI-suggested fix',
|
|
65
|
+
explanation: explanation || 'Fix the accessibility issue as suggested by the AI'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('Error generating fix suggestion with Gemini AI:', error);
|
|
70
|
+
return generateRuleBasedSuggestion(violation);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Generate basic rule-based suggestion as fallback
|
|
75
|
+
* @param violation WCAG violation
|
|
76
|
+
* @returns FixSuggestion
|
|
77
|
+
*/
|
|
78
|
+
function generateRuleBasedSuggestion(violation) {
|
|
79
|
+
var _a;
|
|
80
|
+
const rule = violation.rule || '';
|
|
81
|
+
if (rule.includes('img-alt')) {
|
|
82
|
+
return {
|
|
83
|
+
code: ((_a = violation.snippet) === null || _a === void 0 ? void 0 : _a.replace(/<img/i, '<img alt="Descriptive text"')) || '',
|
|
84
|
+
description: 'Add alt text to image',
|
|
85
|
+
explanation: 'Images need alternative text for screen readers'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (rule.includes('contrast')) {
|
|
89
|
+
return {
|
|
90
|
+
code: '/* Increase color contrast to at least 4.5:1 ratio */',
|
|
91
|
+
description: 'Increase color contrast',
|
|
92
|
+
explanation: 'Text needs sufficient contrast with its background'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
code: '',
|
|
97
|
+
description: 'Fix needed',
|
|
98
|
+
explanation: violation.help || 'Fix this issue to improve accessibility'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
exports.default = {
|
|
102
|
+
generateFixSuggestion
|
|
103
|
+
};
|
package/dist/cli/index.js
CHANGED
|
@@ -8,9 +8,6 @@ const commander_1 = require("commander");
|
|
|
8
8
|
const __1 = require("..");
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const http_1 = __importDefault(require("http"));
|
|
12
|
-
const https_1 = __importDefault(require("https"));
|
|
13
|
-
const url_1 = require("url");
|
|
14
11
|
const __2 = require("..");
|
|
15
12
|
// Try to load package.json for version information
|
|
16
13
|
let version = '0.1.0';
|
|
@@ -155,73 +152,9 @@ commander_1.program
|
|
|
155
152
|
process.exit(1);
|
|
156
153
|
}
|
|
157
154
|
});
|
|
158
|
-
// Helper function to fetch URL content
|
|
159
|
-
async function fetchUrl(urlString) {
|
|
160
|
-
return new Promise((resolve, reject) => {
|
|
161
|
-
const url = new url_1.URL(urlString);
|
|
162
|
-
const client = url.protocol === 'https:' ? https_1.default : http_1.default;
|
|
163
|
-
const req = client.get(urlString, (res) => {
|
|
164
|
-
if (res.statusCode !== 200) {
|
|
165
|
-
reject(new Error(`Request failed with status code ${res.statusCode}`));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
let data = '';
|
|
169
|
-
res.on('data', (chunk) => {
|
|
170
|
-
data += chunk;
|
|
171
|
-
});
|
|
172
|
-
res.on('end', () => {
|
|
173
|
-
resolve(data);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
req.on('error', (err) => {
|
|
177
|
-
reject(err);
|
|
178
|
-
});
|
|
179
|
-
req.end();
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
155
|
// If no arguments provided, show help
|
|
183
156
|
if (process.argv.length <= 2) {
|
|
184
157
|
commander_1.program.help();
|
|
185
158
|
}
|
|
186
159
|
// Parse command line arguments
|
|
187
160
|
commander_1.program.parse(process.argv);
|
|
188
|
-
/**
|
|
189
|
-
* Fallback function to fetch a URL when the primary method fails
|
|
190
|
-
* @param urlString The URL to fetch
|
|
191
|
-
* @returns Promise<string> HTML content
|
|
192
|
-
*/
|
|
193
|
-
async function fetchUrlWithFallback(urlString) {
|
|
194
|
-
return new Promise((resolve, reject) => {
|
|
195
|
-
try {
|
|
196
|
-
// Use a simpler approach that ignores some errors
|
|
197
|
-
const url = new url_1.URL(urlString);
|
|
198
|
-
const options = {
|
|
199
|
-
method: 'GET',
|
|
200
|
-
headers: {
|
|
201
|
-
'User-Agent': 'Mozilla/5.0 (WCAG Scanner Bot) Chrome/91.0.4472.124'
|
|
202
|
-
},
|
|
203
|
-
timeout: 10000
|
|
204
|
-
};
|
|
205
|
-
const client = url.protocol === 'https:' ? https_1.default : http_1.default;
|
|
206
|
-
const req = client.request(url, options, (res) => {
|
|
207
|
-
let data = '';
|
|
208
|
-
res.on('data', (chunk) => {
|
|
209
|
-
data += chunk;
|
|
210
|
-
});
|
|
211
|
-
res.on('end', () => {
|
|
212
|
-
// Return whatever we got, even if status code isn't 200
|
|
213
|
-
resolve(data || '<html><body><p>Failed to fetch content</p></body></html>');
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
req.on('error', () => {
|
|
217
|
-
// Provide minimal HTML if we can't fetch anything
|
|
218
|
-
resolve('<html><body><p>Failed to fetch content</p></body></html>');
|
|
219
|
-
});
|
|
220
|
-
req.end();
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
// Return minimal HTML on any error
|
|
224
|
-
resolve('<html><body><p>Failed to fetch content</p></body></html>');
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
}
|
package/dist/index.js
CHANGED
|
@@ -52,9 +52,6 @@ const middleware_1 = __importDefault(require("./middleware"));
|
|
|
52
52
|
exports.middleware = middleware_1.default;
|
|
53
53
|
const fs_1 = __importDefault(require("fs"));
|
|
54
54
|
const path_1 = __importDefault(require("path"));
|
|
55
|
-
const url_1 = require("url");
|
|
56
|
-
const http_1 = __importDefault(require("http"));
|
|
57
|
-
const https_1 = __importDefault(require("https"));
|
|
58
55
|
const crypto_1 = __importDefault(require("crypto"));
|
|
59
56
|
const os_1 = __importDefault(require("os"));
|
|
60
57
|
/**
|
|
@@ -174,34 +171,6 @@ function formatReport(results, format = 'json', options = {}) {
|
|
|
174
171
|
function saveReport(report, filePath) {
|
|
175
172
|
fs_1.default.writeFileSync(filePath, report);
|
|
176
173
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Helper function to fetch URL content
|
|
179
|
-
* @param urlString URL to fetch
|
|
180
|
-
* @returns Promise<string> HTML content
|
|
181
|
-
*/
|
|
182
|
-
async function fetchUrl(urlString) {
|
|
183
|
-
return new Promise((resolve, reject) => {
|
|
184
|
-
const url = new url_1.URL(urlString);
|
|
185
|
-
const client = url.protocol === 'https:' ? https_1.default : http_1.default;
|
|
186
|
-
const req = client.get(urlString, (res) => {
|
|
187
|
-
if (res.statusCode !== 200) {
|
|
188
|
-
reject(new Error(`Request failed with status code ${res.statusCode}`));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
let data = '';
|
|
192
|
-
res.on('data', (chunk) => {
|
|
193
|
-
data += chunk;
|
|
194
|
-
});
|
|
195
|
-
res.on('end', () => {
|
|
196
|
-
resolve(data);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
req.on('error', (err) => {
|
|
200
|
-
reject(err);
|
|
201
|
-
});
|
|
202
|
-
req.end();
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
174
|
__exportStar(require("./types"), exports);
|
|
206
175
|
// Default export with all main functions
|
|
207
176
|
exports.default = {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface WcagDevOverlayProps {
|
|
3
|
+
/** WCAG conformance level (default: 'AA') */
|
|
4
|
+
level?: 'A' | 'AA' | 'AAA';
|
|
5
|
+
/** Subset of rules to run. Omit to run all. */
|
|
6
|
+
rules?: string[];
|
|
7
|
+
/** Corner to anchor the overlay (default: 'bottom-right') */
|
|
8
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
9
|
+
/** Delay in ms between DOM mutation and re-scan (default: 750) */
|
|
10
|
+
debounce?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare const WcagDevOverlay: React.FC<WcagDevOverlayProps>;
|
|
13
|
+
export default WcagDevOverlay;
|