wcag-scanner 1.2.64 → 1.2.66
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 +61 -84
- package/dist/index.d.ts +4 -24
- package/dist/index.js +5 -125
- package/dist/react/WcagDevOverlay.d.ts +0 -4
- package/dist/react/WcagDevOverlay.js +229 -412
- package/dist/react/browserScanner.d.ts +9 -1
- package/dist/react/browserScanner.js +44 -6
- package/dist/react/gemini.d.ts +12 -0
- package/dist/react/gemini.js +60 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +5 -1
- package/package.json +2 -10
- package/dist/ai/suggestions.d.ts +0 -11
- package/dist/ai/suggestions.js +0 -103
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -160
- package/dist/wasm/index.d.ts +0 -19
- package/dist/wasm/index.js +0 -53
package/README.md
CHANGED
|
@@ -22,13 +22,13 @@ WCAG Scanner is a powerful accessibility testing tool that helps developers iden
|
|
|
22
22
|
|
|
23
23
|
## ✨ Features
|
|
24
24
|
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
25
|
+
- **WCAG 2.1 Compliance Scanning**: Checks against A, AA, and AAA conformance levels
|
|
26
|
+
- **6 Built-in Rules**: Images, contrast, forms, ARIA, structure, and keyboard accessibility
|
|
27
|
+
- **React Dev Overlay**: Live in-browser inspector with element highlighting, pinning, and impact filtering
|
|
28
|
+
- **AI Fix Suggestions**: Paste your Gemini API key in the overlay settings to get instant fix suggestions per violation
|
|
29
|
+
- **Programmatic API**: Scan HTML strings or local files from Node.js
|
|
30
|
+
- **Express Middleware**: Auto-scan responses in your Express app
|
|
31
|
+
- **Multiple Report Formats**: JSON, HTML, and console output
|
|
32
32
|
|
|
33
33
|
## 📦 Installation
|
|
34
34
|
|
|
@@ -43,105 +43,82 @@ yarn add wcag-scanner
|
|
|
43
43
|
pnpm add wcag-scanner
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
> **React users:** React and React DOM are peer dependencies
|
|
46
|
+
> **React users:** React and React DOM are peer dependencies already in your project — no extra install needed.
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
## 🖥️ React Dev Overlay
|
|
49
49
|
|
|
50
|
-
Add one line to your
|
|
50
|
+
The easiest way to use wcag-scanner in a React app. Add **one line** to your entry file and a live accessibility inspector appears in the corner of your browser during development.
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
|
+
// main.ts / main.jsx / index.tsx — works with any file type
|
|
53
54
|
import { initWcagOverlay } from 'wcag-scanner/react';
|
|
54
|
-
|
|
55
|
+
|
|
56
|
+
initWcagOverlay(); // auto-disabled in production
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Options:**
|
|
60
|
+
```ts
|
|
61
|
+
initWcagOverlay({
|
|
62
|
+
level: 'AA', // 'A' | 'AA' | 'AAA' — default: 'AA'
|
|
63
|
+
position: 'bottom-right', // 'bottom-right' | 'bottom-left'
|
|
64
|
+
debounce: 750, // ms to wait after DOM change before rescanning
|
|
65
|
+
rules: ['images', 'contrast'], // run a subset of rules only
|
|
66
|
+
});
|
|
55
67
|
```
|
|
56
68
|
|
|
57
|
-
|
|
69
|
+
**Features:**
|
|
70
|
+
- Hover over a violation to highlight the element on the page
|
|
71
|
+
- Click to pin the highlight; click again to unpin
|
|
72
|
+
- Expand any violation card for the HTML snippet, element path, WCAG criteria, and fix hint
|
|
73
|
+
- Filter by impact level (critical / serious / moderate / minor)
|
|
74
|
+
- Drag the panel anywhere on screen
|
|
75
|
+
- Keyboard shortcut `Alt+Shift+W` to toggle open/close
|
|
76
|
+
- **⚙ Settings** — paste a free Google Gemini API key to get AI-powered fix suggestions per violation
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
### CLI Usage Example:
|
|
78
|
+
> The overlay never runs in production (`NODE_ENV=production`) and is never included in your production bundle.
|
|
61
79
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
## 🔧 Programmatic API
|
|
81
|
+
|
|
82
|
+
Scan HTML strings or local files from Node.js scripts, CI pipelines, or build tools.
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
import { scanHtml, scanFile, formatReport, saveReport } from 'wcag-scanner';
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
// Scan an HTML string
|
|
88
|
+
const results = await scanHtml('<img src="logo.png">', { level: 'AA' });
|
|
89
|
+
console.log(`${results.violations.length} violations found`);
|
|
90
|
+
|
|
91
|
+
// Scan a local HTML file
|
|
92
|
+
const results = await scanFile('./public/index.html', { level: 'AA' });
|
|
93
|
+
|
|
94
|
+
// Generate and save a report
|
|
95
|
+
const html = formatReport(results, 'html'); // 'html' | 'json' | 'console'
|
|
96
|
+
saveReport(html, 'accessibility-report.html');
|
|
68
97
|
```
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
## 🌐 Express Middleware
|
|
71
100
|
|
|
72
|
-
|
|
101
|
+
Automatically scan every HTML response in your Express app and inject a violation badge.
|
|
102
|
+
|
|
103
|
+
```js
|
|
73
104
|
import express from 'express';
|
|
74
105
|
import { middleware } from 'wcag-scanner';
|
|
75
106
|
|
|
76
107
|
const app = express();
|
|
77
108
|
|
|
78
|
-
// Add the WCAG scanner middleware
|
|
79
109
|
app.use(middleware.express.createMiddleware({
|
|
80
|
-
enabled:
|
|
81
|
-
level:
|
|
82
|
-
headerName:
|
|
83
|
-
inlineReport: true,
|
|
84
|
-
onViolation: (results, req
|
|
85
|
-
console.log(
|
|
86
|
-
}
|
|
110
|
+
enabled: true,
|
|
111
|
+
level: 'AA',
|
|
112
|
+
headerName: 'X-WCAG-Violations', // violation count added to response headers
|
|
113
|
+
inlineReport: true, // inject a small widget into the HTML response
|
|
114
|
+
onViolation: (results, req) => {
|
|
115
|
+
console.log(`${results.violations.length} issues on ${req.path}`);
|
|
116
|
+
},
|
|
87
117
|
}));
|
|
88
118
|
|
|
89
|
-
// Your routes
|
|
90
119
|
app.get('/', (req, res) => {
|
|
91
|
-
res.send(
|
|
92
|
-
<!DOCTYPE html>
|
|
93
|
-
<html>
|
|
94
|
-
<head>
|
|
95
|
-
<title>Test Page</title>
|
|
96
|
-
</head>
|
|
97
|
-
<body>
|
|
98
|
-
<h1>Hello World</h1>
|
|
99
|
-
<img src="logo.png"> <!-- Missing alt text will trigger violation -->
|
|
100
|
-
</body>
|
|
101
|
-
</html>
|
|
102
|
-
`);
|
|
120
|
+
res.send(`<!DOCTYPE html><html><body><h1>Hello</h1></body></html>`);
|
|
103
121
|
});
|
|
104
122
|
|
|
105
|
-
app.listen(3000
|
|
106
|
-
console.log('Server running on http://localhost:3000');
|
|
107
|
-
});
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### Programmatic API Usage Example:
|
|
111
|
-
```JavaScript
|
|
112
|
-
import { scanHtml, scanUrl, formatReport } from 'wcag-scanner';
|
|
113
|
-
|
|
114
|
-
async function checkMyWebsite() {
|
|
115
|
-
try {
|
|
116
|
-
// Scan a URL
|
|
117
|
-
const results = await scanUrl('https://example.com', { level: 'AA' });
|
|
118
|
-
|
|
119
|
-
console.log(`Found ${results.violations.length} accessibility issues`);
|
|
120
|
-
|
|
121
|
-
// Generate a report
|
|
122
|
-
const htmlReport = formatReport(results, 'html');
|
|
123
|
-
|
|
124
|
-
// Save the report
|
|
125
|
-
fs.writeFileSync('accessibility-report.html', htmlReport);
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error('Error scanning website:', error);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function checkHtmlString() {
|
|
132
|
-
const html = `
|
|
133
|
-
<!DOCTYPE html>
|
|
134
|
-
<html>
|
|
135
|
-
<head>
|
|
136
|
-
<title>Test</title>
|
|
137
|
-
</head>
|
|
138
|
-
<body>
|
|
139
|
-
<img src="logo.png"> <!-- Missing alt text -->
|
|
140
|
-
</body>
|
|
141
|
-
</html>
|
|
142
|
-
`;
|
|
143
|
-
|
|
144
|
-
const results = await scanHtml(html);
|
|
145
|
-
console.log(formatReport(results, 'console'));
|
|
146
|
-
}
|
|
123
|
+
app.listen(3000);
|
|
147
124
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -3,38 +3,19 @@ import { ScannerOptions, ScanResults } from './types';
|
|
|
3
3
|
import { ReporterFormat } from './reporters';
|
|
4
4
|
import middleware from './middleware';
|
|
5
5
|
/**
|
|
6
|
-
* Scan HTML string for WCAG violations
|
|
7
|
-
* @param html HTML content to scan
|
|
8
|
-
* @param options Scanner options
|
|
9
|
-
* @returns Promise<ScanResults> Scan results
|
|
6
|
+
* Scan an HTML string for WCAG violations.
|
|
10
7
|
*/
|
|
11
8
|
export declare function scanHtml(html: string, options?: ScannerOptions): Promise<ScanResults>;
|
|
12
9
|
/**
|
|
13
|
-
* Scan HTML file for WCAG violations
|
|
14
|
-
* @param filePath Path to HTML file
|
|
15
|
-
* @param options Scanner options
|
|
16
|
-
* @returns Promise<ScanResults> Scan results
|
|
10
|
+
* Scan a local HTML file for WCAG violations.
|
|
17
11
|
*/
|
|
18
12
|
export declare function scanFile(filePath: string, options?: ScannerOptions): Promise<ScanResults>;
|
|
19
13
|
/**
|
|
20
|
-
*
|
|
21
|
-
* @param url URL to scan
|
|
22
|
-
* @param options Scanner options
|
|
23
|
-
* @returns Promise<ScanResults> Scan results
|
|
24
|
-
*/
|
|
25
|
-
export declare function scanUrl(url: string, options?: ScannerOptions): Promise<ScanResults>;
|
|
26
|
-
/**
|
|
27
|
-
* Generate a report from scan results
|
|
28
|
-
* @param results Scan results
|
|
29
|
-
* @param format Report format
|
|
30
|
-
* @param options Scanner options
|
|
31
|
-
* @returns Report string
|
|
14
|
+
* Generate a report from scan results.
|
|
32
15
|
*/
|
|
33
16
|
export declare function formatReport(results: ScanResults, format?: ReporterFormat, options?: ScannerOptions): string;
|
|
34
17
|
/**
|
|
35
|
-
* Save report to a file
|
|
36
|
-
* @param report Report string
|
|
37
|
-
* @param filePath Output file path
|
|
18
|
+
* Save a report string to a file.
|
|
38
19
|
*/
|
|
39
20
|
export declare function saveReport(report: string, filePath: string): void;
|
|
40
21
|
export { WCAGScanner };
|
|
@@ -44,7 +25,6 @@ export { middleware };
|
|
|
44
25
|
declare const _default: {
|
|
45
26
|
scanHtml: typeof scanHtml;
|
|
46
27
|
scanFile: typeof scanFile;
|
|
47
|
-
scanUrl: typeof scanUrl;
|
|
48
28
|
formatReport: typeof formatReport;
|
|
49
29
|
saveReport: typeof saveReport;
|
|
50
30
|
middleware: {
|
package/dist/index.js
CHANGED
|
@@ -10,28 +10,6 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
10
10
|
if (k2 === undefined) k2 = k;
|
|
11
11
|
o[k2] = m[k];
|
|
12
12
|
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
13
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
36
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
15
|
};
|
|
@@ -42,7 +20,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
42
20
|
exports.middleware = exports.WCAGScanner = void 0;
|
|
43
21
|
exports.scanHtml = scanHtml;
|
|
44
22
|
exports.scanFile = scanFile;
|
|
45
|
-
exports.scanUrl = scanUrl;
|
|
46
23
|
exports.formatReport = formatReport;
|
|
47
24
|
exports.saveReport = saveReport;
|
|
48
25
|
const scanner_1 = require("./scanner");
|
|
@@ -52,13 +29,8 @@ const middleware_1 = __importDefault(require("./middleware"));
|
|
|
52
29
|
exports.middleware = middleware_1.default;
|
|
53
30
|
const fs_1 = __importDefault(require("fs"));
|
|
54
31
|
const path_1 = __importDefault(require("path"));
|
|
55
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
56
|
-
const os_1 = __importDefault(require("os"));
|
|
57
32
|
/**
|
|
58
|
-
* Scan HTML string for WCAG violations
|
|
59
|
-
* @param html HTML content to scan
|
|
60
|
-
* @param options Scanner options
|
|
61
|
-
* @returns Promise<ScanResults> Scan results
|
|
33
|
+
* Scan an HTML string for WCAG violations.
|
|
62
34
|
*/
|
|
63
35
|
async function scanHtml(html, options = {}) {
|
|
64
36
|
const scanner = new scanner_1.WCAGScanner(options);
|
|
@@ -66,24 +38,7 @@ async function scanHtml(html, options = {}) {
|
|
|
66
38
|
return scanner.scan();
|
|
67
39
|
}
|
|
68
40
|
/**
|
|
69
|
-
*
|
|
70
|
-
*/
|
|
71
|
-
async function getWasmScraper() {
|
|
72
|
-
try {
|
|
73
|
-
const { default: wasmModule } = await Promise.resolve().then(() => __importStar(require('./wasm')));
|
|
74
|
-
await wasmModule.initialize();
|
|
75
|
-
return wasmModule;
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
console.error('Failed to load WASM scraper, falling back to HTTP requests:', error);
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Scan HTML file for WCAG violations
|
|
84
|
-
* @param filePath Path to HTML file
|
|
85
|
-
* @param options Scanner options
|
|
86
|
-
* @returns Promise<ScanResults> Scan results
|
|
41
|
+
* Scan a local HTML file for WCAG violations.
|
|
87
42
|
*/
|
|
88
43
|
async function scanFile(filePath, options = {}) {
|
|
89
44
|
const html = fs_1.default.readFileSync(path_1.default.resolve(filePath), 'utf8');
|
|
@@ -93,91 +48,16 @@ async function scanFile(filePath, options = {}) {
|
|
|
93
48
|
return scanner.scan();
|
|
94
49
|
}
|
|
95
50
|
/**
|
|
96
|
-
*
|
|
97
|
-
* @param url URL to scan
|
|
98
|
-
* @param options Scanner options
|
|
99
|
-
* @returns Promise<ScanResults> Scan results
|
|
100
|
-
*/
|
|
101
|
-
async function scanUrl(url, options = {}) {
|
|
102
|
-
console.log(`Scanning URL: ${url}`);
|
|
103
|
-
// Create temp directory for saving content
|
|
104
|
-
const tempDir = path_1.default.join(os_1.default.tmpdir(), 'wcag-scanner-temp');
|
|
105
|
-
if (!fs_1.default.existsSync(tempDir)) {
|
|
106
|
-
fs_1.default.mkdirSync(tempDir, { recursive: true });
|
|
107
|
-
}
|
|
108
|
-
// Generate unique file name
|
|
109
|
-
const fileId = crypto_1.default.createHash('md5').update(url + Date.now().toString()).digest('hex').substring(0, 10);
|
|
110
|
-
const tempFile = path_1.default.join(tempDir, `${fileId}.html`);
|
|
111
|
-
try {
|
|
112
|
-
// Get the WASM scraper
|
|
113
|
-
const wasmScraper = await getWasmScraper();
|
|
114
|
-
let html;
|
|
115
|
-
if (wasmScraper) {
|
|
116
|
-
// Use WASM scraper
|
|
117
|
-
html = await wasmScraper.scrapeUrl(url);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
// Fallback to simple HTTP request
|
|
121
|
-
const response = await fetch(url, {
|
|
122
|
-
headers: {
|
|
123
|
-
'User-Agent': 'WCAG-Scanner/1.0-js'
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
if (!response.ok) {
|
|
127
|
-
throw new Error(`HTTP error: ${response.status}`);
|
|
128
|
-
}
|
|
129
|
-
html = await response.text();
|
|
130
|
-
}
|
|
131
|
-
if (!html || html.trim().length === 0) {
|
|
132
|
-
throw new Error('Scraper returned empty HTML content');
|
|
133
|
-
}
|
|
134
|
-
console.log(`Successfully scraped ${html.length} bytes of HTML content`);
|
|
135
|
-
// Save content to temp file
|
|
136
|
-
fs_1.default.writeFileSync(tempFile, html);
|
|
137
|
-
console.log(`Saved scraped content to ${tempFile}`);
|
|
138
|
-
// If verbose logging is enabled, show a sample
|
|
139
|
-
if (options.verbose) {
|
|
140
|
-
console.log('First 200 characters of HTML:');
|
|
141
|
-
console.log(html.substring(0, 200) + '...');
|
|
142
|
-
}
|
|
143
|
-
// Run the scanner on the HTML content
|
|
144
|
-
const scanner = new scanner_1.WCAGScanner({
|
|
145
|
-
...options,
|
|
146
|
-
baseUrl: url
|
|
147
|
-
});
|
|
148
|
-
await scanner.loadHTML(html, url);
|
|
149
|
-
return scanner.scan();
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
console.error('Error scanning URL:', error);
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Generate a report from scan results
|
|
158
|
-
* @param results Scan results
|
|
159
|
-
* @param format Report format
|
|
160
|
-
* @param options Scanner options
|
|
161
|
-
* @returns Report string
|
|
51
|
+
* Generate a report from scan results.
|
|
162
52
|
*/
|
|
163
53
|
function formatReport(results, format = 'json', options = {}) {
|
|
164
54
|
return (0, reporters_1.generateReport)(results, format, options);
|
|
165
55
|
}
|
|
166
56
|
/**
|
|
167
|
-
* Save report to a file
|
|
168
|
-
* @param report Report string
|
|
169
|
-
* @param filePath Output file path
|
|
57
|
+
* Save a report string to a file.
|
|
170
58
|
*/
|
|
171
59
|
function saveReport(report, filePath) {
|
|
172
60
|
fs_1.default.writeFileSync(filePath, report);
|
|
173
61
|
}
|
|
174
62
|
__exportStar(require("./types"), exports);
|
|
175
|
-
|
|
176
|
-
exports.default = {
|
|
177
|
-
scanHtml,
|
|
178
|
-
scanFile,
|
|
179
|
-
scanUrl,
|
|
180
|
-
formatReport,
|
|
181
|
-
saveReport,
|
|
182
|
-
middleware: middleware_1.default
|
|
183
|
-
};
|
|
63
|
+
exports.default = { scanHtml, scanFile, formatReport, saveReport, middleware: middleware_1.default };
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export interface WcagDevOverlayProps {
|
|
3
|
-
/** WCAG conformance level (default: 'AA') */
|
|
4
3
|
level?: 'A' | 'AA' | 'AAA';
|
|
5
|
-
/** Subset of rules to run. Omit to run all. */
|
|
6
4
|
rules?: string[];
|
|
7
|
-
/** Corner to anchor the overlay (default: 'bottom-right') */
|
|
8
5
|
position?: 'bottom-right' | 'bottom-left';
|
|
9
|
-
/** Delay in ms between DOM mutation and re-scan (default: 750) */
|
|
10
6
|
debounce?: number;
|
|
11
7
|
}
|
|
12
8
|
export declare const WcagDevOverlay: React.FC<WcagDevOverlayProps>;
|