@octaviaflow/accessibility-checker 1.0.0 ā 1.1.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/README.md +378 -52
- package/bin/achecker.js +9 -0
- package/cjs/checker/index.js +4 -43
- package/cjs/index.js +117 -0
- package/cjs/storage/reportWriter.js +190 -0
- package/cjs/utils/wcag-rules.js +65 -0
- package/mjs/checker/index.js +2 -9
- package/mjs/index.js +110 -0
- package/mjs/storage/reportWriter.js +186 -0
- package/mjs/utils/wcag-rules.js +61 -0
- package/package.json +35 -15
- package/types/checker/index.d.ts +11 -2
- package/types/index.d.ts +16 -0
- package/types/storage/reportWriter.d.ts +3 -0
- package/types/utils/wcag-rules.d.ts +10 -0
package/README.md
CHANGED
|
@@ -1,94 +1,420 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @octaviaflow/accessibility-checker
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Enterprise-grade accessibility checker for OctaviaFlow Design System**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A comprehensive TypeScript-based accessibility testing tool that combines heuristic analysis with axe-core integration to provide detailed accessibility reports in multiple formats.
|
|
6
6
|
|
|
7
|
-
## Features
|
|
7
|
+
## š Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
9
|
+
- **š Dual Analysis Engine**: Combines custom heuristics with axe-core for comprehensive accessibility testing
|
|
10
|
+
- **š Multiple Output Formats**: JSON, HTML, CSV, and Excel reports
|
|
11
|
+
- **šØ Beautiful HTML Reports**: Responsive, styled reports with issue categorization
|
|
12
|
+
- **ā” High Performance**: Built with TypeScript and optimized for speed
|
|
13
|
+
- **š ļø CLI & Programmatic API**: Use as command-line tool or integrate into your workflow
|
|
14
|
+
- **š WCAG Compliance**: Maps issues to WCAG guidelines with help URLs
|
|
15
|
+
- **š§ Extensible Architecture**: Easy to add custom rules and checks
|
|
12
16
|
|
|
13
|
-
## Installation
|
|
17
|
+
## š¦ Installation
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
### NPM/Yarn
|
|
20
|
+
```bash
|
|
21
|
+
npm install @octaviaflow/accessibility-checker
|
|
22
|
+
# or
|
|
23
|
+
yarn add @octaviaflow/accessibility-checker
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Bun
|
|
27
|
+
```bash
|
|
28
|
+
bun add @octaviaflow/accessibility-checker
|
|
29
|
+
```
|
|
16
30
|
|
|
31
|
+
### Global Installation
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g @octaviaflow/accessibility-checker
|
|
17
34
|
```
|
|
18
|
-
|
|
35
|
+
|
|
36
|
+
## šÆ Quick Start
|
|
37
|
+
|
|
38
|
+
### CLI Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Analyze an HTML file
|
|
42
|
+
octaviaflow-achecker --input index.html
|
|
43
|
+
|
|
44
|
+
# Generate HTML report
|
|
45
|
+
octaviaflow-achecker --input page.html --format html --verbose
|
|
46
|
+
|
|
47
|
+
# Test with sample HTML
|
|
48
|
+
octaviaflow-achecker --sample --format excel
|
|
49
|
+
|
|
50
|
+
# Custom output location
|
|
51
|
+
octaviaflow-achecker --input app.html --output reports/accessibility.json
|
|
19
52
|
```
|
|
20
53
|
|
|
21
|
-
|
|
54
|
+
### Programmatic Usage
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { analyze } from '@octaviaflow/accessibility-checker';
|
|
58
|
+
import { saveReport } from '@octaviaflow/accessibility-checker/storage';
|
|
59
|
+
|
|
60
|
+
// Analyze HTML content
|
|
61
|
+
const html = '<html><body><img src="logo.png"><h1>Welcome</h1></body></html>';
|
|
62
|
+
const result = await analyze(html);
|
|
63
|
+
|
|
64
|
+
// Save results in different formats
|
|
65
|
+
await saveReport(result, './report.json', 'json');
|
|
66
|
+
await saveReport(result, './report.html', 'html');
|
|
67
|
+
await saveReport(result, './report.xlsx', 'excel');
|
|
22
68
|
|
|
23
|
-
|
|
69
|
+
console.log(`Found ${result.summary.totalIssues} accessibility issues`);
|
|
70
|
+
```
|
|
24
71
|
|
|
72
|
+
## š CLI Options
|
|
73
|
+
|
|
74
|
+
| Option | Alias | Description | Default |
|
|
75
|
+
|--------|-------|-------------|---------|
|
|
76
|
+
| `--input <file>` | `-i` | HTML file to analyze | - |
|
|
77
|
+
| `--output <file>` | `-o` | Output file path | `./data/results.json` |
|
|
78
|
+
| `--format <type>` | - | Output format: `json`, `html`, `csv`, `excel` | `json` |
|
|
79
|
+
| `--sample` | - | Run with sample HTML for testing | `false` |
|
|
80
|
+
| `--verbose` | `-v` | Verbose output with detailed logging | `false` |
|
|
81
|
+
| `--help` | `-h` | Show help message | - |
|
|
82
|
+
|
|
83
|
+
## š Output Formats
|
|
84
|
+
|
|
85
|
+
### JSON Format
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"summary": {
|
|
89
|
+
"totalIssues": 3,
|
|
90
|
+
"byType": {
|
|
91
|
+
"image-missing-alt": 1,
|
|
92
|
+
"link-empty": 1,
|
|
93
|
+
"axe:color-contrast": 1
|
|
94
|
+
},
|
|
95
|
+
"axeViolations": 1
|
|
96
|
+
},
|
|
97
|
+
"issues": [
|
|
98
|
+
{
|
|
99
|
+
"type": "image-missing-alt",
|
|
100
|
+
"message": "Image tag missing alt attribute",
|
|
101
|
+
"snippet": "<img src=\"logo.png\">",
|
|
102
|
+
"source": "heuristic"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"axe": { /* Full axe-core results */ },
|
|
106
|
+
"meta": {
|
|
107
|
+
"timestamp": "2025-11-13T10:52:00.000Z",
|
|
108
|
+
"source": "index.html",
|
|
109
|
+
"version": "1.1.1"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
25
112
|
```
|
|
26
|
-
|
|
113
|
+
|
|
114
|
+
### HTML Report
|
|
115
|
+
- **Responsive design** with modern styling
|
|
116
|
+
- **Color-coded issues** by severity and type
|
|
117
|
+
- **Interactive elements** with expandable details
|
|
118
|
+
- **Summary dashboard** with metrics
|
|
119
|
+
- **WCAG compliance mapping**
|
|
120
|
+
|
|
121
|
+
### Excel Report
|
|
122
|
+
- **Summary sheet** with overview metrics
|
|
123
|
+
- **Issues sheet** with detailed findings
|
|
124
|
+
- **Formatted tables** with proper styling
|
|
125
|
+
- **Multiple worksheets** for organization
|
|
126
|
+
|
|
127
|
+
## š§ Development
|
|
128
|
+
|
|
129
|
+
### Prerequisites
|
|
130
|
+
- **Node.js** ā„ 22.0.0
|
|
131
|
+
- **Bun** ā„ 1.3.2 (recommended)
|
|
132
|
+
- **TypeScript** ā„ 5.8.3
|
|
133
|
+
|
|
134
|
+
### Setup
|
|
135
|
+
```bash
|
|
136
|
+
# Clone the repository
|
|
137
|
+
git clone https://github.com/OctaviaFlow/OctaviaFlow-Design-System.git
|
|
138
|
+
cd OctaviaFlow-Design-System/packages/accessibility-checker
|
|
139
|
+
|
|
140
|
+
# Install dependencies
|
|
141
|
+
bun install
|
|
142
|
+
|
|
143
|
+
# Build the project
|
|
144
|
+
bun run build
|
|
145
|
+
|
|
146
|
+
# Run tests
|
|
147
|
+
bun run test
|
|
148
|
+
|
|
149
|
+
# Run linting
|
|
150
|
+
bun run lint
|
|
151
|
+
|
|
152
|
+
# Development mode
|
|
153
|
+
bun run dev
|
|
27
154
|
```
|
|
28
155
|
|
|
29
|
-
|
|
156
|
+
### Scripts
|
|
157
|
+
|
|
158
|
+
| Script | Description |
|
|
159
|
+
|--------|-------------|
|
|
160
|
+
| `bun run build` | Build CommonJS, ESM, and TypeScript declarations |
|
|
161
|
+
| `bun run test` | Run tests once and exit |
|
|
162
|
+
| `bun run test:watch` | Run tests in watch mode |
|
|
163
|
+
| `bun run lint` | Run ESLint |
|
|
164
|
+
| `bun run lint:fix` | Fix ESLint issues |
|
|
165
|
+
| `bun run format` | Format code with Prettier |
|
|
166
|
+
| `bun run ci-check` | Full CI pipeline (build + test + lint) |
|
|
167
|
+
| `bun run dev` | Build and run sample |
|
|
30
168
|
|
|
31
|
-
|
|
169
|
+
## šļø Architecture
|
|
170
|
+
|
|
171
|
+
### Core Components
|
|
32
172
|
|
|
33
173
|
```
|
|
34
|
-
|
|
174
|
+
src/
|
|
175
|
+
āāā checker/
|
|
176
|
+
ā āāā index.ts # Main analysis engine
|
|
177
|
+
āāā storage/
|
|
178
|
+
ā āāā reportWriter.ts # Multi-format report generation
|
|
179
|
+
āāā utils/
|
|
180
|
+
ā āāā wcag-rules.ts # WCAG compliance mapping
|
|
181
|
+
āāā index.ts # CLI interface
|
|
35
182
|
```
|
|
36
183
|
|
|
37
|
-
|
|
184
|
+
### Analysis Engine
|
|
185
|
+
|
|
186
|
+
The accessibility checker uses a **dual-engine approach**:
|
|
187
|
+
|
|
188
|
+
1. **Heuristic Analysis**: Fast, lightweight checks for common issues
|
|
189
|
+
- Missing alt attributes on images
|
|
190
|
+
- Empty links and headings
|
|
191
|
+
- Basic structural problems
|
|
192
|
+
|
|
193
|
+
2. **Axe-Core Integration**: Comprehensive WCAG compliance testing
|
|
194
|
+
- Color contrast analysis
|
|
195
|
+
- Keyboard navigation
|
|
196
|
+
- ARIA compliance
|
|
197
|
+
- Semantic structure validation
|
|
198
|
+
|
|
199
|
+
### Type Definitions
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
interface Issue {
|
|
203
|
+
type: string;
|
|
204
|
+
message: string;
|
|
205
|
+
snippet?: string;
|
|
206
|
+
helpUrl?: string;
|
|
207
|
+
source?: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface Result {
|
|
211
|
+
summary: {
|
|
212
|
+
totalIssues: number;
|
|
213
|
+
byType: Record<string, number>;
|
|
214
|
+
axeViolations?: number;
|
|
215
|
+
};
|
|
216
|
+
issues: Issue[];
|
|
217
|
+
axe?: AxeResults | { error: string };
|
|
218
|
+
meta?: ResultMeta;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
38
221
|
|
|
39
|
-
|
|
222
|
+
## šØ Examples
|
|
223
|
+
|
|
224
|
+
### Basic HTML Analysis
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { analyze } from '@octaviaflow/accessibility-checker';
|
|
228
|
+
|
|
229
|
+
const html = `
|
|
230
|
+
<!DOCTYPE html>
|
|
231
|
+
<html lang="en">
|
|
232
|
+
<head>
|
|
233
|
+
<title>Test Page</title>
|
|
234
|
+
</head>
|
|
235
|
+
<body>
|
|
236
|
+
<h1>Welcome</h1>
|
|
237
|
+
<img src="hero.jpg">
|
|
238
|
+
<a href="/about"></a>
|
|
239
|
+
<button style="color: #ccc; background: #ddd;">Click me</button>
|
|
240
|
+
</body>
|
|
241
|
+
</html>
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
const result = await analyze(html);
|
|
245
|
+
console.log(`Found ${result.summary.totalIssues} issues:`);
|
|
246
|
+
result.issues.forEach(issue => {
|
|
247
|
+
console.log(`- ${issue.type}: ${issue.message}`);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
40
250
|
|
|
41
|
-
|
|
251
|
+
### CI/CD Integration
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
# GitHub Actions example
|
|
255
|
+
- name: Accessibility Check
|
|
256
|
+
run: |
|
|
257
|
+
octaviaflow-achecker --input dist/index.html --format json --output accessibility-report.json
|
|
258
|
+
|
|
259
|
+
- name: Upload Report
|
|
260
|
+
uses: actions/upload-artifact@v3
|
|
261
|
+
with:
|
|
262
|
+
name: accessibility-report
|
|
263
|
+
path: accessibility-report.json
|
|
264
|
+
```
|
|
42
265
|
|
|
43
|
-
|
|
266
|
+
### Custom Report Generation
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { analyze, saveReport } from '@octaviaflow/accessibility-checker';
|
|
270
|
+
|
|
271
|
+
async function generateAccessibilityReport(htmlFiles: string[]) {
|
|
272
|
+
const results = [];
|
|
273
|
+
|
|
274
|
+
for (const file of htmlFiles) {
|
|
275
|
+
const html = await fs.readFile(file, 'utf8');
|
|
276
|
+
const result = await analyze(html);
|
|
277
|
+
result.meta = { ...result.meta, source: file };
|
|
278
|
+
results.push(result);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Generate combined report
|
|
282
|
+
const combinedResult = {
|
|
283
|
+
summary: {
|
|
284
|
+
totalIssues: results.reduce((sum, r) => sum + r.summary.totalIssues, 0),
|
|
285
|
+
byType: results.reduce((acc, r) => {
|
|
286
|
+
Object.entries(r.summary.byType).forEach(([type, count]) => {
|
|
287
|
+
acc[type] = (acc[type] || 0) + count;
|
|
288
|
+
});
|
|
289
|
+
return acc;
|
|
290
|
+
}, {})
|
|
291
|
+
},
|
|
292
|
+
issues: results.flatMap(r => r.issues),
|
|
293
|
+
meta: {
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
source: 'batch-analysis',
|
|
296
|
+
fileCount: htmlFiles.length
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
await saveReport(combinedResult, './batch-report.html', 'html');
|
|
301
|
+
}
|
|
302
|
+
```
|
|
44
303
|
|
|
45
|
-
###
|
|
304
|
+
### Integration with Testing Frameworks
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
// Jest/Vitest example
|
|
308
|
+
import { analyze } from '@octaviaflow/accessibility-checker';
|
|
309
|
+
|
|
310
|
+
describe('Accessibility Tests', () => {
|
|
311
|
+
test('homepage should be accessible', async () => {
|
|
312
|
+
const html = await getRenderedHTML('/');
|
|
313
|
+
const result = await analyze(html);
|
|
314
|
+
|
|
315
|
+
expect(result.summary.totalIssues).toBe(0);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test('should have no critical axe violations', async () => {
|
|
319
|
+
const html = await getRenderedHTML('/dashboard');
|
|
320
|
+
const result = await analyze(html);
|
|
321
|
+
|
|
322
|
+
if (result.axe && 'violations' in result.axe) {
|
|
323
|
+
const criticalViolations = result.axe.violations.filter(
|
|
324
|
+
v => v.impact === 'critical'
|
|
325
|
+
);
|
|
326
|
+
expect(criticalViolations).toHaveLength(0);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
```
|
|
46
331
|
|
|
47
|
-
|
|
332
|
+
## š§ Configuration
|
|
333
|
+
|
|
334
|
+
### ESLint Integration
|
|
335
|
+
|
|
336
|
+
The package uses **ESLint v9** with flat config:
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// eslint.config.js
|
|
340
|
+
import typescriptEslint from '@typescript-eslint/eslint-plugin';
|
|
341
|
+
import tsParser from '@typescript-eslint/parser';
|
|
342
|
+
import js from '@eslint/js';
|
|
343
|
+
|
|
344
|
+
export default [
|
|
345
|
+
js.configs.recommended,
|
|
346
|
+
{
|
|
347
|
+
files: ['**/*.ts'],
|
|
348
|
+
languageOptions: {
|
|
349
|
+
parser: tsParser,
|
|
350
|
+
parserOptions: {
|
|
351
|
+
ecmaVersion: 2022,
|
|
352
|
+
sourceType: 'module'
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
plugins: {
|
|
356
|
+
'@typescript-eslint': typescriptEslint
|
|
357
|
+
},
|
|
358
|
+
rules: {
|
|
359
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
360
|
+
'no-console': 'off'
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
];
|
|
364
|
+
```
|
|
48
365
|
|
|
49
|
-
|
|
50
|
-
- node src/index.js --sample
|
|
51
|
-
- Optional: --output path/to/results.json (defaults to ./data/results.json)
|
|
366
|
+
### TypeScript Configuration
|
|
52
367
|
|
|
53
|
-
|
|
368
|
+
Uses **unified OctaviaFlow TypeScript configs**:
|
|
54
369
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
370
|
+
```json
|
|
371
|
+
{
|
|
372
|
+
"extends": "typescript-config-octaviaflow/base",
|
|
373
|
+
"compilerOptions": {
|
|
374
|
+
"rootDir": "src",
|
|
375
|
+
"outDir": "dist"
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
58
379
|
|
|
59
|
-
|
|
380
|
+
## š Performance
|
|
60
381
|
|
|
61
|
-
|
|
382
|
+
- **Fast Analysis**: Optimized for large HTML files
|
|
383
|
+
- **Memory Efficient**: Streaming analysis for large documents
|
|
384
|
+
- **Concurrent Processing**: Parallel heuristic and axe-core analysis
|
|
385
|
+
- **Minimal Dependencies**: Lightweight package with essential dependencies only
|
|
62
386
|
|
|
63
|
-
|
|
387
|
+
## š¤ Contributing
|
|
64
388
|
|
|
65
|
-
|
|
66
|
-
|
|
389
|
+
1. **Fork** the repository
|
|
390
|
+
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
|
|
391
|
+
3. **Commit** your changes: `git commit -m 'Add amazing feature'`
|
|
392
|
+
4. **Push** to the branch: `git push origin feature/amazing-feature`
|
|
393
|
+
5. **Open** a Pull Request
|
|
67
394
|
|
|
68
|
-
|
|
395
|
+
### Development Guidelines
|
|
69
396
|
|
|
70
|
-
|
|
397
|
+
- **TypeScript**: All code must be properly typed
|
|
398
|
+
- **Testing**: Maintain 100% test coverage for new features
|
|
399
|
+
- **Linting**: Follow ESLint configuration
|
|
400
|
+
- **Documentation**: Update README for new features
|
|
71
401
|
|
|
72
|
-
|
|
402
|
+
## š License
|
|
73
403
|
|
|
74
|
-
|
|
75
|
-
or
|
|
76
|
-
octaviaflow --input path/to/file.html --output ./data/results.json
|
|
404
|
+
Licensed under the **Apache-2.0** License. See [LICENSE](LICENSE) file for details.
|
|
77
405
|
|
|
78
|
-
|
|
406
|
+
## š Related Packages
|
|
79
407
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
- No IBM telemetry or Carbon packages included.
|
|
408
|
+
- **[@octaviaflow/react](../react)** - React components with built-in accessibility
|
|
409
|
+
- **[@octaviaflow/styles](../styles)** - Accessible CSS framework
|
|
410
|
+
- **[@octaviaflow/themes](../themes)** - Accessible color themes
|
|
84
411
|
|
|
85
|
-
|
|
412
|
+
## š Support
|
|
86
413
|
|
|
87
|
-
-
|
|
414
|
+
- **Issues**: [GitHub Issues](https://github.com/OctaviaFlow/OctaviaFlow-Design-System/issues)
|
|
415
|
+
- **Discussions**: [GitHub Discussions](https://github.com/OctaviaFlow/OctaviaFlow-Design-System/discussions)
|
|
416
|
+
- **Documentation**: [OctaviaFlow Docs](https://octaviaflow.dev)
|
|
88
417
|
|
|
89
|
-
|
|
418
|
+
---
|
|
90
419
|
|
|
91
|
-
|
|
92
|
-
- mjs/ ES module build (mjs/index.js)
|
|
93
|
-
- types/ TypeScript declaration files
|
|
94
|
-
- bin/ CLI shim (bin/octaviaflow.js)
|
|
420
|
+
**Made with ā¤ļø by the OctaviaFlow Team**
|
package/bin/achecker.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright OctaviaFlow
|
|
3
|
+
* Author: Vishal Kumar
|
|
4
|
+
* Created: 11/November/2025
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the Apache-2.0 license found in the
|
|
7
|
+
* LICENSE file in the root directory of this source tree.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
#!/usr/bin/env node
|
|
2
11
|
// simple shim that delegates to the CommonJS build
|
|
3
12
|
try {
|
package/cjs/checker/index.js
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.analyze = analyze;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
37
5
|
const jsdom_1 = require("jsdom");
|
|
38
|
-
const axe = __importStar(require("axe-core"));
|
|
6
|
+
const axe = tslib_1.__importStar(require("axe-core"));
|
|
39
7
|
function heuristicAnalyze(html) {
|
|
40
8
|
const issues = [];
|
|
41
|
-
// images without alt
|
|
42
9
|
const imgRegex = /<img\b[^>]*>/gi;
|
|
43
10
|
let m;
|
|
44
11
|
while ((m = imgRegex.exec(html))) {
|
|
@@ -51,7 +18,6 @@ function heuristicAnalyze(html) {
|
|
|
51
18
|
});
|
|
52
19
|
}
|
|
53
20
|
}
|
|
54
|
-
// links without descriptive text (naive)
|
|
55
21
|
const aRegex = /<a\b[^>]*>([\s\S]*?)<\/a>/gi;
|
|
56
22
|
while ((m = aRegex.exec(html))) {
|
|
57
23
|
const inner = (m[1] || '').trim();
|
|
@@ -63,7 +29,6 @@ function heuristicAnalyze(html) {
|
|
|
63
29
|
});
|
|
64
30
|
}
|
|
65
31
|
}
|
|
66
|
-
// empty headings
|
|
67
32
|
const hRegex = /<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi;
|
|
68
33
|
while ((m = hRegex.exec(html))) {
|
|
69
34
|
const content = (m[1] || '').replace(/<[^>]*>/g, '').trim();
|
|
@@ -83,10 +48,7 @@ async function runAxe(html) {
|
|
|
83
48
|
resources: 'usable',
|
|
84
49
|
});
|
|
85
50
|
const { window } = dom;
|
|
86
|
-
|
|
87
|
-
const result = await axe.run(window.document, {
|
|
88
|
-
// default rules; you can customize here
|
|
89
|
-
});
|
|
51
|
+
const result = await axe.run(window.document, {});
|
|
90
52
|
return result;
|
|
91
53
|
}
|
|
92
54
|
async function analyze(html) {
|
|
@@ -96,11 +58,10 @@ async function analyze(html) {
|
|
|
96
58
|
axeRes = await runAxe(html);
|
|
97
59
|
}
|
|
98
60
|
catch (e) {
|
|
99
|
-
// If axe fails for some HTML, continue with heuristics
|
|
100
61
|
axeRes = { error: String(e) };
|
|
101
62
|
}
|
|
102
63
|
const axeIssues = [];
|
|
103
|
-
if (axeRes && Array.isArray(axeRes.violations)) {
|
|
64
|
+
if (axeRes && 'violations' in axeRes && Array.isArray(axeRes.violations)) {
|
|
104
65
|
for (const v of axeRes.violations) {
|
|
105
66
|
for (const node of v.nodes || []) {
|
|
106
67
|
axeIssues.push({
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.saveReport = exports.analyze = void 0;
|
|
4
|
+
exports.main = main;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const minimist_1 = tslib_1.__importDefault(require("minimist"));
|
|
9
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
10
|
+
const ora_1 = tslib_1.__importDefault(require("ora"));
|
|
11
|
+
const index_js_1 = require("./checker/index.js");
|
|
12
|
+
const reportWriter_js_1 = require("./storage/reportWriter.js");
|
|
13
|
+
var index_js_2 = require("./checker/index.js");
|
|
14
|
+
Object.defineProperty(exports, "analyze", { enumerable: true, get: function () { return index_js_2.analyze; } });
|
|
15
|
+
var reportWriter_js_2 = require("./storage/reportWriter.js");
|
|
16
|
+
Object.defineProperty(exports, "saveReport", { enumerable: true, get: function () { return reportWriter_js_2.saveReport; } });
|
|
17
|
+
function showHelp() {
|
|
18
|
+
console.log(chalk_1.default.blue.bold('OctaviaFlow Accessibility Checker'));
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log(chalk_1.default.yellow('Usage:'));
|
|
21
|
+
console.log(' octaviaflow-achecker [options]');
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(chalk_1.default.yellow('Options:'));
|
|
24
|
+
console.log(' -i, --input <file> Input HTML file to analyze');
|
|
25
|
+
console.log(' -o, --output <file> Output file for results (default: ./data/results.json)');
|
|
26
|
+
console.log(' --sample Run with sample HTML for testing');
|
|
27
|
+
console.log(' --format <type> Output format: json, html, csv, excel (default: json)');
|
|
28
|
+
console.log(' -v, --verbose Verbose output');
|
|
29
|
+
console.log(' -h, --help Show this help message');
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(chalk_1.default.yellow('Examples:'));
|
|
32
|
+
console.log(' octaviaflow-achecker --input index.html');
|
|
33
|
+
console.log(' octaviaflow-achecker --sample --format html');
|
|
34
|
+
console.log(' octaviaflow-achecker -i page.html -o report.json --verbose');
|
|
35
|
+
}
|
|
36
|
+
async function main(args) {
|
|
37
|
+
const argv = (0, minimist_1.default)(args || process.argv.slice(2));
|
|
38
|
+
if (argv.help || argv.h) {
|
|
39
|
+
showHelp();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const inputPath = argv.input || argv.i;
|
|
43
|
+
const outputPath = argv.output || argv.o || (0, path_1.join)(process.cwd(), 'data', 'results.json');
|
|
44
|
+
const format = argv.format || 'json';
|
|
45
|
+
const verbose = argv.verbose || argv.v || false;
|
|
46
|
+
let html = '';
|
|
47
|
+
let spinner = null;
|
|
48
|
+
try {
|
|
49
|
+
if (argv.sample) {
|
|
50
|
+
html = `<html>
|
|
51
|
+
<head><title>Sample Accessibility Test</title></head>
|
|
52
|
+
<body>
|
|
53
|
+
<img src="image.png">
|
|
54
|
+
<a href="/"></a>
|
|
55
|
+
<h1></h1>
|
|
56
|
+
<button></button>
|
|
57
|
+
<input type="text">
|
|
58
|
+
<div role="button"></div>
|
|
59
|
+
</body>
|
|
60
|
+
</html>`;
|
|
61
|
+
if (verbose) {
|
|
62
|
+
console.log(chalk_1.default.gray('Using sample HTML for testing'));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (inputPath) {
|
|
66
|
+
if (verbose) {
|
|
67
|
+
console.log(chalk_1.default.gray(`Reading HTML from: ${inputPath}`));
|
|
68
|
+
}
|
|
69
|
+
html = (0, fs_1.readFileSync)((0, path_1.resolve)(inputPath), 'utf8');
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.error(chalk_1.default.red('Error: No input specified'));
|
|
73
|
+
showHelp();
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
spinner = (0, ora_1.default)('Analyzing accessibility...').start();
|
|
77
|
+
const result = await (0, index_js_1.analyze)(html);
|
|
78
|
+
result.meta = {
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
source: inputPath || 'sample',
|
|
81
|
+
format,
|
|
82
|
+
version: '1.0.0'
|
|
83
|
+
};
|
|
84
|
+
spinner.succeed('Analysis complete');
|
|
85
|
+
if (verbose) {
|
|
86
|
+
console.log(chalk_1.default.green(`Found ${result.summary.totalIssues} accessibility issues`));
|
|
87
|
+
console.log(chalk_1.default.gray(`Axe violations: ${result.axe && 'violations' in result.axe ? result.axe.violations?.length || 0 : 0}`));
|
|
88
|
+
}
|
|
89
|
+
await (0, reportWriter_js_1.saveReport)(result, outputPath, format);
|
|
90
|
+
console.log(chalk_1.default.green(`ā Results saved to ${outputPath}`));
|
|
91
|
+
if (result.summary.totalIssues > 0) {
|
|
92
|
+
console.log(chalk_1.default.yellow('\nIssue Summary:'));
|
|
93
|
+
Object.entries(result.summary.byType).forEach(([type, count]) => {
|
|
94
|
+
console.log(chalk_1.default.gray(` ${type}: ${count}`));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(chalk_1.default.green('\nā No accessibility issues found!'));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (spinner) {
|
|
103
|
+
spinner.fail('Analysis failed');
|
|
104
|
+
}
|
|
105
|
+
console.error(chalk_1.default.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
106
|
+
if (verbose && error instanceof Error) {
|
|
107
|
+
console.error(chalk_1.default.gray(error.stack));
|
|
108
|
+
}
|
|
109
|
+
process.exit(2);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (require.main === module) {
|
|
113
|
+
main().catch(err => {
|
|
114
|
+
console.error(chalk_1.default.red('Fatal error:'), err);
|
|
115
|
+
process.exit(2);
|
|
116
|
+
});
|
|
117
|
+
}
|