dtp-caal 1.0.0 β†’ 1.0.1

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 ADDED
@@ -0,0 +1,296 @@
1
+ # dtp-caal (Context-Aware Accessibility Linter)
2
+
3
+ [![npm version](https://img.shields.io/npm/v/dtp-caal.svg)](https://www.npmjs.com/package/dtp-caal)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![Playwright](https://img.shields.io/badge/tested%20with-Playwright-2EAD33.svg)](https://playwright.dev/)
6
+ [![WCAG Compliant](https://img.shields.io/badge/standards-WCAG%202.1%20AA-orange.svg)](https://www.w3.org/WAI/standards-guidelines/wcag/)
7
+
8
+ > **Next-generation, context-aware web accessibility auditing and automated remediation powered by Headless Browser automation and LLMs.**
9
+
10
+ ---
11
+
12
+ ## πŸ“Œ Overview
13
+
14
+ Traditional accessibility tools (like Lighthouse, axe-core, and WAVE) rely on static DOM syntax checks and regular expressions. While effective for basic rules, they suffer from fundamental limitations:
15
+
16
+ 1. **They are "Rule-Based", Not "Semantic":** An image with `<img src="hero.png" alt="photo.jpg">` passes static tests because an `alt` attribute is technically presentβ€”even though "photo.jpg" is meaningless to a screen-reader user.
17
+ 2. **Inability to Understand Context:** Generic elements like multiple `<button>Read More</button>` links pass syntax checks, but screen readers cannot determine which article or topic each button relates to.
18
+ 3. **Flagging vs. Fixing:** Existing tools dump warnings with links to dense WCAG documentation, forcing developers to manually decipher and write complex ARIA markup.
19
+
20
+ **`dtp-caal`** shifts accessibility testing from static syntax checking to **context-aware semantic analysis**:
21
+ - It launches a headless browser via **Playwright** to let single-page applications (React, Vue, Next.js, Angular, etc.) fully render.
22
+ - It extracts interactive elements along with their **surrounding parent DOM context**.
23
+ - It uses high-performance LLMs (via the **Groq API**) to assess accessibility intent and generate exact, framework-compliant ARIA code.
24
+ - With the `--auto-fix` option, it automatically finds the corresponding component in your source directory and patches the fix directly!
25
+
26
+ ---
27
+
28
+ ## ✨ Features
29
+
30
+ - 🌐 **Headless Browser Scanning:** Accurately audits dynamic, client-side rendered Single Page Applications (SPAs) using Playwright.
31
+ - 🧠 **Context-Aware Semantic Analysis:** Analyzes target elements in conjunction with their surrounding DOM tree to understand intent.
32
+ - πŸ› οΈ **Automated Source Remediation (`--auto-fix`):** Locates the responsible source file (`.tsx`, `.jsx`, `.html`, `.vue`, etc.) and patches the fix directly.
33
+ - πŸ“Š **Multi-Format Reporting:** Generates clean, human-readable **Markdown (`.md`)** reports or structured **JSON (`.json`)** summaries.
34
+ - πŸš€ **CI/CD & Pull Request Integration:** Exits with code `1` upon detecting accessibility violations to guard against PR regressions in CI/CD pipelines.
35
+
36
+ ---
37
+
38
+ ## πŸ“‹ Prerequisites
39
+
40
+ 1. **Node.js**: Version 18 or higher (Node 20+ recommended).
41
+ 2. **Groq API Key**: `dtp-caal` uses Groq for fast inference. You can get a free key from the [Groq Console](https://console.groq.com/).
42
+ 3. **Playwright Chromium**: Playwright headless browser binaries must be installed.
43
+
44
+ ---
45
+
46
+ ## πŸš€ Installation & Quick Start
47
+
48
+ ### 1. Instant Run with `npx` (Recommended)
49
+
50
+ You can run `dtp-caal` on any project immediately without installing it globally:
51
+
52
+ ```bash
53
+ # Set your API Key
54
+ # Linux / macOS:
55
+ export GROQ_API_KEY="your_groq_api_key_here"
56
+
57
+ # Windows (PowerShell):
58
+ $env:GROQ_API_KEY="your_groq_api_key_here"
59
+
60
+ # Install Chromium browser binaries (first-time only)
61
+ npx playwright install chromium
62
+
63
+ # Run the audit against your local dev server
64
+ npx dtp-caal --url http://localhost:3000
65
+ ```
66
+
67
+ ---
68
+
69
+ ### 2. Global Installation
70
+
71
+ Install globally across your machine:
72
+
73
+ ```bash
74
+ npm install -g dtp-caal
75
+ npx playwright install chromium
76
+
77
+ # Run anywhere
78
+ dtp-caal --url http://localhost:3000
79
+ ```
80
+
81
+ ---
82
+
83
+ ### 3. Local Project Dependency
84
+
85
+ Install within your web project:
86
+
87
+ ```bash
88
+ npm install --save-dev dtp-caal
89
+ npx playwright install chromium
90
+ ```
91
+
92
+ Add an audit script to your `package.json`:
93
+
94
+ ```json
95
+ {
96
+ "scripts": {
97
+ "a11y:audit": "dtp-caal --url http://localhost:3000",
98
+ "a11y:fix": "dtp-caal --url http://localhost:3000 --auto-fix --src-dir ./src"
99
+ }
100
+ }
101
+ ```
102
+
103
+ Then run:
104
+
105
+ ```bash
106
+ npm run a11y:audit
107
+ ```
108
+
109
+ ---
110
+
111
+ ## πŸ’» CLI Usage & Commands
112
+
113
+ ### Basic Audit
114
+ Audit a local or remote URL and output a Markdown report:
115
+ ```bash
116
+ dtp-caal --url http://localhost:3000
117
+ ```
118
+
119
+ ### Automated Remediation (Auto-Fix)
120
+ Audit the rendered application and apply source code patches directly to your project files:
121
+ ```bash
122
+ dtp-caal --url http://localhost:3000 --auto-fix --src-dir ./src
123
+ ```
124
+ > **Note:** Changes made by `--auto-fix` are left uncommitted in your working tree so you can review diffs (`git diff`) before committing.
125
+
126
+ ### Custom Output Report Path and Format
127
+ Generate a JSON report for programmatic consumption or custom CI dashboards:
128
+ ```bash
129
+ dtp-caal --url http://localhost:3000 --format json --output ./reports/a11y-results.json
130
+ ```
131
+
132
+ ---
133
+
134
+ ## βš™οΈ CLI Options Reference
135
+
136
+ | Flag | Shorthand | Default | Description |
137
+ | :--- | :--- | :--- | :--- |
138
+ | `--url <url>` | `-u` | `http://localhost:3000` | The URL of the web page to scan. |
139
+ | `--output <path>` | `-o` | `./caal-report.md` | Destination file path for the audit report. |
140
+ | `--format <format>`| `-f` | `md` | Output format: `md` (Markdown) or `json` (JSON). |
141
+ | `--auto-fix` | | `false` | Automatically attempt to locate and patch source files. |
142
+ | `--src-dir <path>` | | `./` | Directory containing source files when `--auto-fix` is enabled. |
143
+ | `--help` | `-h` | | Display help and argument descriptions. |
144
+ | `--version` | `-v` | | Display CLI version. |
145
+
146
+ ---
147
+
148
+ ## πŸ” How It Works
149
+
150
+ ```
151
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
152
+ β”‚ Running Web App β”‚ (e.g., http://localhost:3000)
153
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
154
+ β”‚ 1. Navigate & Render (Playwright)
155
+ β–Ό
156
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
157
+ β”‚ Target Elements & β”‚ (Buttons, images, inputs, links +
158
+ β”‚ Parent DOM Context β”‚ surrounding contextual HTML)
159
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
160
+ β”‚ 2. Semantic Evaluation
161
+ β–Ό
162
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
163
+ β”‚ Groq LLM Engine β”‚ (WCAG validation, explanation,
164
+ β”‚ (openai/gpt-oss-120b) β”‚ and precise code replacement)
165
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
166
+ β”‚ 3. Output
167
+ β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
168
+ β–Ό β–Ό
169
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
170
+ β”‚ Report Generated β”‚ β”‚ Auto-Fix Applied β”‚
171
+ β”‚ (.md / .json) β”‚ β”‚ (Direct source file patch)β”‚
172
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
173
+ ```
174
+
175
+ 1. **Extraction:** Playwright launches headless Chromium, navigates to the specified URL, waits for network idle, and queries target elements (`button`, `img`, `input`, `a`, `[role="button"]`, etc.). For each element, it extracts both the element's markup and sanitized parent container HTML.
176
+ 2. **Contextual Analysis:** Each element is analyzed using Groq's LLM endpoint. The prompt instructs the model to act as an accessibility engineer, identifying WCAG failures and synthesizing valid replacement markup.
177
+ 3. **Reporting:** Results are structured into either a Markdown document or JSON file.
178
+ 4. **Remediation:** If `--auto-fix` is passed, the tool searches the specified `--src-dir` for files containing matching tokens, prompts the LLM to integrate the accessibility fix while preserving framework syntax (JSX, Vue, standard HTML), and updates the source files.
179
+ 5. **Exit Code:** If any element fails WCAG checks, the CLI terminates with exit code `1`, making it ideal for CI/CD gates.
180
+
181
+ ---
182
+
183
+ ## πŸ€– CI/CD Integration (GitHub Actions)
184
+
185
+ You can easily integrate `dtp-caal` into your pull request pipeline to block regressions and post automated fixes:
186
+
187
+ ```yaml
188
+ name: Accessibility Linter (CAAL)
189
+
190
+ on:
191
+ pull_request:
192
+ branches: [ main, master ]
193
+
194
+ jobs:
195
+ a11y-audit:
196
+ runs-on: ubuntu-latest
197
+ steps:
198
+ - name: Checkout Code
199
+ uses: actions/checkout@v3
200
+
201
+ - name: Setup Node.js
202
+ uses: actions/setup-node@v3
203
+ with:
204
+ node-version: '20'
205
+
206
+ - name: Install App Dependencies & Build
207
+ run: |
208
+ npm ci
209
+ npm run build --if-present
210
+
211
+ - name: Start App Server
212
+ run: |
213
+ npm run start &
214
+ npx wait-on http://localhost:3000
215
+
216
+ - name: Install Playwright Browsers
217
+ run: npx playwright install --with-deps chromium
218
+
219
+ - name: Run Accessibility Audit
220
+ id: a11y_audit
221
+ continue-on-error: true
222
+ env:
223
+ GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
224
+ run: |
225
+ npx dtp-caal --url http://localhost:3000 --output ./caal-report.md --auto-fix --src-dir ./src
226
+
227
+ - name: Generate Fix Patch
228
+ id: git_diff
229
+ run: |
230
+ git diff > caal-fix.patch
231
+ if [ -s caal-fix.patch ]; then
232
+ echo "has_fixes=true" >> $GITHUB_OUTPUT
233
+ fi
234
+
235
+ - name: Comment on PR
236
+ if: steps.a11y_audit.outcome == 'failure'
237
+ uses: actions/github-script@v7
238
+ with:
239
+ github-token: ${{ secrets.GITHUB_TOKEN }}
240
+ script: |
241
+ const fs = require('fs');
242
+ const report = fs.readFileSync('caal-report.md', 'utf8');
243
+ let body = `## 🚨 Accessibility Audit Failed\n\n${report}`;
244
+
245
+ if (fs.existsSync('caal-fix.patch')) {
246
+ const patch = fs.readFileSync('caal-fix.patch', 'utf8');
247
+ if (patch.trim()) {
248
+ body += `\n\n### πŸ› οΈ Suggested Code Fix\n\`\`\`diff\n${patch}\n\`\`\``;
249
+ }
250
+ }
251
+
252
+ await github.rest.issues.createComment({
253
+ issue_number: context.issue.number,
254
+ owner: context.repo.owner,
255
+ repo: context.repo.repo,
256
+ body: body
257
+ });
258
+ core.setFailed('Accessibility violations detected.');
259
+ ```
260
+
261
+ ---
262
+
263
+ ## πŸ› οΈ Local Development & Building
264
+
265
+ If you are contributing to or modifying the CLI module:
266
+
267
+ ```bash
268
+ # Clone the repository
269
+ git clone https://github.com/ankith5980/Mini_Project.git
270
+ cd Mini_Project/DTP_CAAL/cli
271
+
272
+ # Install dependencies
273
+ npm install
274
+
275
+ # Build TypeScript to dist/
276
+ npm run build
277
+
278
+ # Run in development mode
279
+ npm start -- --url http://localhost:3000
280
+ ```
281
+
282
+ ---
283
+
284
+ ## ❓ Troubleshooting
285
+
286
+ | Issue | Cause | Solution |
287
+ | :--- | :--- | :--- |
288
+ | `Error: GROQ_API_KEY environment variable is not set.` | Missing API key in environment. | Set `GROQ_API_KEY` via `export`, `set`, PowerShell `$env:`, or a `.env` file. |
289
+ | `browserType.launch: Executable doesn't exist` | Playwright browser binaries not installed. | Run `npx playwright install chromium` or `npx playwright install --with-deps chromium`. |
290
+ | `Page load timeout / net::ERR_CONNECTION_REFUSED` | The target server is not running on the specified URL. | Ensure your development server is active before running the audit (e.g. `npm run dev`). |
291
+
292
+ ---
293
+
294
+ ## πŸ“„ License
295
+
296
+ This project is licensed under the [ISC License](LICENSE).
package/dist/analyzer.js CHANGED
@@ -60,7 +60,7 @@ Return a JSON object with this exact structure:
60
60
  content: prompt
61
61
  }
62
62
  ],
63
- model: 'llama-3.3-70b-versatile',
63
+ model: 'openai/gpt-oss-120b',
64
64
  response_format: { type: 'json_object' }
65
65
  });
66
66
  const text = completion.choices[0]?.message?.content;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ const program = new commander_1.Command();
14
14
  program
15
15
  .name('a11y-audit')
16
16
  .description('Context-Aware Accessibility Linter CLI')
17
- .version('1.0.0')
17
+ .version('1.0.1')
18
18
  .requiredOption('-u, --url <url>', 'URL to scan (e.g., http://localhost:3000)', 'http://localhost:3000')
19
19
  .option('-o, --output <path>', 'Output file path', './caal-report.md')
20
20
  .option('-f, --format <format>', 'Output format (json or md)', 'md')
@@ -115,7 +115,7 @@ DO NOT wrap the response in markdown code blocks. OUTPUT ONLY THE RAW CODE.
115
115
  content: prompt
116
116
  }
117
117
  ],
118
- model: 'llama-3.3-70b-versatile',
118
+ model: 'openai/gpt-oss-120b',
119
119
  });
120
120
  let newContent = completion.choices[0]?.message?.content || '';
121
121
  // Just in case it wraps in markdown despite instructions
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dtp-caal",
3
- "version": "1.0.0",
4
- "description": "",
3
+ "version": "1.0.1",
4
+ "description": "Context-Aware Accessibility Linter CLI powered by Playwright and LLMs",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "dtp-caal": "./dist/index.js"
@@ -1,119 +0,0 @@
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.analyzeElement = analyzeElement;
7
- exports.analyzeElements = analyzeElements;
8
- const groq_sdk_1 = __importDefault(require("groq-sdk"));
9
- const dotenv_1 = __importDefault(require("dotenv"));
10
- dotenv_1.default.config();
11
- const apiKey = process.env.GROQ_API_KEY || '';
12
- const groq = new groq_sdk_1.default({ apiKey });
13
- const delay = (ms) => new Promise(res => setTimeout(res, ms));
14
- async function analyzeElement(element) {
15
- const prompt = `
16
- You are an expert accessibility engineer. Your task is to analyze an HTML/JSX element within its parent context to determine if it meets WCAG accessibility standards.
17
- You must output ONLY valid JSON without any markdown code blocks or conversational text.
18
-
19
- Context:
20
- Parent Code:
21
- \`\`\`
22
- ${element.parentHtml}
23
- \`\`\`
24
-
25
- Target Element Code:
26
- \`\`\`
27
- ${element.elementHtml}
28
- \`\`\`
29
-
30
- Analyze the target element. Does it have sufficient context for screen readers? Does it use semantic HTML properly? Is it missing ARIA attributes where necessary?
31
-
32
- Be extremely accurate in your code fix. Ensure the replacement code contains all necessary structural semantics, aria-labels, alt text, and valid roles based on the parent context.
33
- CRITICAL INSTRUCTIONS FOR FIX:
34
- 1. Your suggested fix MUST COMPLETELY resolve the accessibility issue. If the fixed element were analyzed again, it MUST pass all WCAG checks.
35
- 2. Provide the COMPLETE Target Element in your fix, including its opening tag, all original children, and its closing tag. Do NOT provide partial snippets.
36
- 3. The replacement code MUST match the exact framework syntax of the input. If the input uses React JSX syntax (like \`className\`, camelCase attributes, or \`style={{}}\`), the output MUST be valid JSX. If standard HTML, output standard HTML.
37
- 4. Maintain all existing non-accessibility attributes (e.g., \`id\`, \`class\`, \`onClick\`, \`href\`, etc.) exactly as they appear in the original Target Element.
38
-
39
- Return a JSON object with this exact structure:
40
- {
41
- "isAccessible": boolean,
42
- "issueTitle": string (or null if isAccessible is true. A concise title of the WCAG violation),
43
- "explanation": string (or null. Explain the issue concisely to a developer),
44
- "suggestedFixCode": string (or null. Provide the highly accurate, complete replacement code for the Target Element that fixes the issue, matching the input's syntax),
45
- "fixReasoning": string (or null. Briefly explain exactly what the suggested fix code does and how it solves the accessibility issue)
46
- }
47
- `;
48
- let retries = 3;
49
- let delayMs = 2000;
50
- while (retries > 0) {
51
- try {
52
- const completion = await groq.chat.completions.create({
53
- messages: [
54
- {
55
- role: 'system',
56
- content: 'You are an AI that only outputs valid JSON. Do not output anything else.'
57
- },
58
- {
59
- role: 'user',
60
- content: prompt
61
- }
62
- ],
63
- model: 'llama-3.3-70b-versatile',
64
- response_format: { type: 'json_object' }
65
- });
66
- const text = completion.choices[0]?.message?.content;
67
- if (text) {
68
- const parsed = JSON.parse(text);
69
- return {
70
- ...element,
71
- ...parsed
72
- };
73
- }
74
- throw new Error("No text in response");
75
- }
76
- catch (error) {
77
- if (error.status === 503 || error.status === 429 || error.message?.includes('503') || error.message?.includes('429')) {
78
- console.warn(`[WARN] Rate limited. Retries left: ${retries - 1}. Retrying in ${delayMs}ms...`);
79
- retries--;
80
- if (retries === 0) {
81
- return {
82
- ...element,
83
- isAccessible: false,
84
- error: "Failed to analyze due to API rate limits."
85
- };
86
- }
87
- await delay(delayMs);
88
- delayMs *= 2;
89
- }
90
- else {
91
- return {
92
- ...element,
93
- isAccessible: false,
94
- error: error.message || "Unknown API error"
95
- };
96
- }
97
- }
98
- }
99
- return {
100
- ...element,
101
- isAccessible: false,
102
- error: "Exhausted retries"
103
- };
104
- }
105
- async function analyzeElements(elements) {
106
- const results = [];
107
- console.log(`Analyzing ${elements.length} elements using Groq API...`);
108
- for (let i = 0; i < elements.length; i++) {
109
- // Log progress
110
- console.log(`Analyzing element ${i + 1}/${elements.length}: <${elements[i].tagName}>...`);
111
- const result = await analyzeElement(elements[i]);
112
- results.push(result);
113
- // Wait briefly between requests to avoid rate limits
114
- if (i < elements.length - 1) {
115
- await delay(1000);
116
- }
117
- }
118
- return results;
119
- }
package/dist/src/index.js DELETED
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const commander_1 = require("commander");
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const scanner_1 = require("./scanner");
10
- const analyzer_1 = require("./analyzer");
11
- const reporter_1 = require("./reporter");
12
- const program = new commander_1.Command();
13
- program
14
- .name('a11y-audit')
15
- .description('Context-Aware Accessibility Linter CLI')
16
- .version('1.0.0')
17
- .requiredOption('-u, --url <url>', 'URL to scan (e.g., http://localhost:3000)', 'http://localhost:3000')
18
- .option('-o, --output <path>', 'Output file path', './caal-report.md')
19
- .option('-f, --format <format>', 'Output format (json or md)', 'md')
20
- .action(async (options) => {
21
- try {
22
- console.log(chalk_1.default.blue(`Starting accessibility audit for: ${options.url}`));
23
- // Step 1: Scan page and extract elements
24
- const scannedElements = await (0, scanner_1.scanPage)(options.url);
25
- if (scannedElements.length === 0) {
26
- console.log(chalk_1.default.yellow('No relevant elements found to analyze.'));
27
- return;
28
- }
29
- // Step 2: Analyze with LLM
30
- if (!process.env.GROQ_API_KEY) {
31
- console.error(chalk_1.default.red('Error: GROQ_API_KEY environment variable is not set.'));
32
- process.exit(1);
33
- }
34
- const results = await (0, analyzer_1.analyzeElements)(scannedElements);
35
- // Step 3: Report
36
- const format = options.format.toLowerCase();
37
- if (format === 'json' || options.output.endsWith('.json')) {
38
- (0, reporter_1.generateJsonReport)(results, options.output);
39
- }
40
- else {
41
- (0, reporter_1.generateMarkdownReport)(results, options.output);
42
- }
43
- // Step 4: Exit with error code if issues found (useful for CI/CD)
44
- const failedElements = results.filter(r => !r.isAccessible);
45
- if (failedElements.length > 0) {
46
- console.log(chalk_1.default.red(`\nFound ${failedElements.length} accessibility issues!`));
47
- process.exit(1);
48
- }
49
- else {
50
- console.log(chalk_1.default.green('\nAll checks passed! πŸŽ‰'));
51
- process.exit(0);
52
- }
53
- }
54
- catch (error) {
55
- console.error(chalk_1.default.red('Audit failed:'), error);
56
- process.exit(1);
57
- }
58
- });
59
- program.parse(process.argv);
@@ -1,48 +0,0 @@
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.generateJsonReport = generateJsonReport;
7
- exports.generateMarkdownReport = generateMarkdownReport;
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
- function generateJsonReport(results, outputPath) {
11
- const outputDir = path_1.default.dirname(outputPath);
12
- if (!fs_1.default.existsSync(outputDir)) {
13
- fs_1.default.mkdirSync(outputDir, { recursive: true });
14
- }
15
- fs_1.default.writeFileSync(outputPath, JSON.stringify(results, null, 2));
16
- console.log(`JSON report generated at ${outputPath}`);
17
- }
18
- function generateMarkdownReport(results, outputPath) {
19
- const outputDir = path_1.default.dirname(outputPath);
20
- if (!fs_1.default.existsSync(outputDir)) {
21
- fs_1.default.mkdirSync(outputDir, { recursive: true });
22
- }
23
- const failedElements = results.filter(r => !r.isAccessible);
24
- let md = `# Accessibility Audit Report\n\n`;
25
- md += `**Total Elements Scanned:** ${results.length}\n`;
26
- md += `**Accessibility Issues Found:** ${failedElements.length}\n\n`;
27
- if (failedElements.length === 0) {
28
- md += `πŸŽ‰ Congratulations! No accessibility issues were found.\n`;
29
- }
30
- else {
31
- md += `## Issues Detected\n\n`;
32
- failedElements.forEach((el, i) => {
33
- md += `### ${i + 1}. <${el.tagName}>: ${el.issueTitle || 'Issue'}\n\n`;
34
- if (el.error) {
35
- md += `**Error during analysis:** ${el.error}\n\n`;
36
- }
37
- else {
38
- md += `**Explanation:** ${el.explanation}\n\n`;
39
- md += `**Original Code:**\n\`\`\`html\n${el.elementHtml}\n\`\`\`\n\n`;
40
- md += `**Suggested Fix:**\n\`\`\`html\n${el.suggestedFixCode}\n\`\`\`\n\n`;
41
- md += `**Why this works:** ${el.fixReasoning}\n\n`;
42
- }
43
- md += `---\n\n`;
44
- });
45
- }
46
- fs_1.default.writeFileSync(outputPath, md);
47
- console.log(`Markdown report generated at ${outputPath}`);
48
- }
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.scanPage = scanPage;
4
- const playwright_1 = require("playwright");
5
- async function scanPage(url) {
6
- console.log(`\nNavigating to ${url}...`);
7
- // Launch headless chromium
8
- const browser = await playwright_1.chromium.launch({ headless: true });
9
- const page = await browser.newPage();
10
- try {
11
- await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
12
- console.log('Page loaded. Extracting elements...');
13
- // Wait a tiny bit extra for framework rendering if needed
14
- await page.waitForTimeout(2000);
15
- // Evaluate extraction logic in the page context
16
- const extractedElements = await page.evaluate(() => {
17
- function extractContext(element) {
18
- const parent = element.parentElement;
19
- let parentHtml = '';
20
- if (parent) {
21
- const clone = parent.cloneNode(true);
22
- clone.querySelectorAll('script, style').forEach(el => el.remove());
23
- parentHtml = clone.outerHTML;
24
- if (parentHtml.length > 15000) {
25
- parentHtml = parentHtml.substring(0, 15000) + '\\n... [TRUNCATED]';
26
- }
27
- }
28
- return {
29
- elementHtml: element.outerHTML,
30
- parentHtml
31
- };
32
- }
33
- const elements = Array.from(document.querySelectorAll('button, img, input, a, [role="button"], [role="link"], [role="img"]'));
34
- return elements.map((el, index) => {
35
- const context = extractContext(el);
36
- return {
37
- id: index.toString(),
38
- tagName: el.tagName.toLowerCase(),
39
- elementHtml: context.elementHtml,
40
- parentHtml: context.parentHtml
41
- };
42
- });
43
- });
44
- console.log(`Found ${extractedElements.length} elements to analyze.`);
45
- return extractedElements;
46
- }
47
- catch (error) {
48
- console.error(`Error scanning page: ${error}`);
49
- throw error;
50
- }
51
- finally {
52
- await browser.close();
53
- }
54
- }