@diegovelasquezweb/a11y-engine 0.5.0 → 0.6.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/package.json +1 -1
- package/src/cli/audit.mjs +3 -0
- package/src/index.d.mts +11 -0
- package/src/index.mjs +14 -1
- package/src/pipeline/dom-scanner.mjs +59 -26
package/package.json
CHANGED
package/src/cli/audit.mjs
CHANGED
|
@@ -46,6 +46,7 @@ Execution & Emulation:
|
|
|
46
46
|
--skip-reports Omit HTML and PDF report generation (default).
|
|
47
47
|
--skip-patterns Skip source code pattern scanning even if --project-dir is set.
|
|
48
48
|
--affected-only Re-scan only routes that had violations in the previous scan (faster re-audits).
|
|
49
|
+
--engines <csv> Comma-separated engines to run: axe,cdp,pa11y (default: all).
|
|
49
50
|
--wait-ms <num> Time to wait after page load (default: 2000).
|
|
50
51
|
--timeout-ms <num> Network timeout (default: 30000).
|
|
51
52
|
-h, --help Show this help.
|
|
@@ -154,6 +155,7 @@ async function main() {
|
|
|
154
155
|
const colorScheme = getArgValue("color-scheme");
|
|
155
156
|
const target = getArgValue("target");
|
|
156
157
|
const headless = !argv.includes("--headed");
|
|
158
|
+
const enginesArg = getArgValue("engines");
|
|
157
159
|
|
|
158
160
|
const onlyRule = getArgValue("only-rule");
|
|
159
161
|
const skipReports = argv.includes("--skip-reports") || !argv.includes("--with-reports");
|
|
@@ -257,6 +259,7 @@ async function main() {
|
|
|
257
259
|
scanArgs.push("--viewport", `${viewport.width}x${viewport.height}`);
|
|
258
260
|
}
|
|
259
261
|
if (axeTags) scanArgs.push("--axe-tags", axeTags);
|
|
262
|
+
if (enginesArg) scanArgs.push("--engines", enginesArg);
|
|
260
263
|
|
|
261
264
|
await runScript("../pipeline/dom-scanner.mjs", scanArgs, childEnv);
|
|
262
265
|
|
package/src/index.d.mts
CHANGED
|
@@ -209,6 +209,16 @@ export interface SourcePatternOptions {
|
|
|
209
209
|
onlyPattern?: string;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Engine selection
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
|
|
216
|
+
export interface EngineSelection {
|
|
217
|
+
axe?: boolean;
|
|
218
|
+
cdp?: boolean;
|
|
219
|
+
pa11y?: boolean;
|
|
220
|
+
}
|
|
221
|
+
|
|
212
222
|
// ---------------------------------------------------------------------------
|
|
213
223
|
// Audit options
|
|
214
224
|
// ---------------------------------------------------------------------------
|
|
@@ -232,6 +242,7 @@ export interface RunAuditOptions {
|
|
|
232
242
|
projectDir?: string;
|
|
233
243
|
skipPatterns?: boolean;
|
|
234
244
|
screenshotsDir?: string;
|
|
245
|
+
engines?: EngineSelection;
|
|
235
246
|
onProgress?: (step: string, status: string, extra?: Record<string, unknown>) => void;
|
|
236
247
|
}
|
|
237
248
|
|
package/src/index.mjs
CHANGED
|
@@ -447,6 +447,7 @@ export function getOverview(findings, payload = null) {
|
|
|
447
447
|
* framework?: string,
|
|
448
448
|
* projectDir?: string,
|
|
449
449
|
* skipPatterns?: boolean,
|
|
450
|
+
* engines?: { axe?: boolean, cdp?: boolean, pa11y?: boolean },
|
|
450
451
|
* onProgress?: (step: string, status: string, extra?: object) => void,
|
|
451
452
|
* }} options
|
|
452
453
|
* @returns {Promise<{ findings: object[], metadata: object, incomplete_findings?: object[] }>}
|
|
@@ -459,7 +460,14 @@ export async function runAudit(options) {
|
|
|
459
460
|
|
|
460
461
|
const onProgress = options.onProgress || null;
|
|
461
462
|
|
|
462
|
-
//
|
|
463
|
+
// Normalize engines — default all enabled
|
|
464
|
+
const engines = {
|
|
465
|
+
axe: options.engines?.axe !== false,
|
|
466
|
+
cdp: options.engines?.cdp !== false,
|
|
467
|
+
pa11y: options.engines?.pa11y !== false,
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// Step 1: DOM scan (selected engines)
|
|
463
471
|
if (onProgress) onProgress("page", "running");
|
|
464
472
|
|
|
465
473
|
const scanPayload = await runDomScanner(
|
|
@@ -479,6 +487,7 @@ export async function runAudit(options) {
|
|
|
479
487
|
excludeSelectors: options.excludeSelectors,
|
|
480
488
|
screenshotsDir: options.screenshotsDir,
|
|
481
489
|
projectDir: options.projectDir,
|
|
490
|
+
engines,
|
|
482
491
|
},
|
|
483
492
|
{ onProgress },
|
|
484
493
|
);
|
|
@@ -531,6 +540,10 @@ export async function runAudit(options) {
|
|
|
531
540
|
|
|
532
541
|
if (onProgress) onProgress("intelligence", "done");
|
|
533
542
|
|
|
543
|
+
// Attach active engines to metadata so consumers know which ran
|
|
544
|
+
findingsPayload.metadata = findingsPayload.metadata || {};
|
|
545
|
+
findingsPayload.metadata.engines = engines;
|
|
546
|
+
|
|
534
547
|
return findingsPayload;
|
|
535
548
|
}
|
|
536
549
|
|
|
@@ -63,6 +63,7 @@ Options:
|
|
|
63
63
|
--crawl-depth <number> How deep to follow links during discovery (1-3, default: 2)
|
|
64
64
|
--wait-until <value> Page load strategy: domcontentloaded|load|networkidle (default: domcontentloaded)
|
|
65
65
|
--viewport <WxH> Viewport dimensions as WIDTHxHEIGHT (e.g., 375x812)
|
|
66
|
+
--engines <csv> Engines to run: axe,cdp,pa11y (default: all)
|
|
66
67
|
-h, --help Show this help
|
|
67
68
|
`);
|
|
68
69
|
}
|
|
@@ -95,6 +96,7 @@ function parseArgs(argv) {
|
|
|
95
96
|
crawlDepth: DEFAULTS.crawlDepth,
|
|
96
97
|
viewport: null,
|
|
97
98
|
axeTags: null,
|
|
99
|
+
engines: { axe: true, cdp: true, pa11y: true },
|
|
98
100
|
};
|
|
99
101
|
|
|
100
102
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -121,6 +123,10 @@ function parseArgs(argv) {
|
|
|
121
123
|
if (key === "--color-scheme") args.colorScheme = value;
|
|
122
124
|
if (key === "--screenshots-dir") args.screenshotsDir = value;
|
|
123
125
|
if (key === "--axe-tags") args.axeTags = value.split(",").map((s) => s.trim());
|
|
126
|
+
if (key === "--engines") {
|
|
127
|
+
const active = value.split(",").map((s) => s.trim().toLowerCase());
|
|
128
|
+
args.engines = { axe: active.includes("axe"), cdp: active.includes("cdp"), pa11y: active.includes("pa11y") };
|
|
129
|
+
}
|
|
124
130
|
if (key === "--viewport") {
|
|
125
131
|
const [w, h] = value.split("x").map(Number);
|
|
126
132
|
if (w && h) args.viewport = { width: w, height: h };
|
|
@@ -945,6 +951,11 @@ export async function runDomScanner(options = {}, callbacks = {}) {
|
|
|
945
951
|
viewport: options.viewport || null,
|
|
946
952
|
axeTags: options.axeTags || null,
|
|
947
953
|
projectDir: options.projectDir || null,
|
|
954
|
+
engines: {
|
|
955
|
+
axe: options.engines?.axe !== false,
|
|
956
|
+
cdp: options.engines?.cdp !== false,
|
|
957
|
+
pa11y: options.engines?.pa11y !== false,
|
|
958
|
+
},
|
|
948
959
|
};
|
|
949
960
|
|
|
950
961
|
if (!args.baseUrl) throw new Error("Missing required option: baseUrl");
|
|
@@ -1151,36 +1162,57 @@ async function _runDomScannerInternal(args) {
|
|
|
1151
1162
|
|
|
1152
1163
|
writeProgress("page", "done");
|
|
1153
1164
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1165
|
+
let result = { url: targetUrl, violations: [], incomplete: [], passes: [], metadata: {} };
|
|
1166
|
+
let cdpViolations = [];
|
|
1167
|
+
let pa11yViolations = [];
|
|
1168
|
+
|
|
1169
|
+
// Step 1: axe-core (conditional)
|
|
1170
|
+
if (args.engines.axe) {
|
|
1171
|
+
writeProgress("axe", "running");
|
|
1172
|
+
result = await analyzeRoute(
|
|
1173
|
+
tabPage,
|
|
1174
|
+
targetUrl,
|
|
1175
|
+
args.waitMs,
|
|
1176
|
+
args.excludeSelectors,
|
|
1177
|
+
args.onlyRule,
|
|
1178
|
+
args.timeoutMs,
|
|
1179
|
+
2,
|
|
1180
|
+
args.waitUntil,
|
|
1181
|
+
args.axeTags,
|
|
1182
|
+
);
|
|
1183
|
+
const axeViolationCount = result.violations?.length || 0;
|
|
1184
|
+
writeProgress("axe", "done", { found: axeViolationCount });
|
|
1185
|
+
log.info(`axe-core: ${axeViolationCount} violation(s) found`);
|
|
1186
|
+
} else {
|
|
1187
|
+
// Navigate for CDP/pa11y even if axe is off
|
|
1188
|
+
await tabPage.goto(targetUrl, { waitUntil: args.waitUntil, timeout: args.timeoutMs });
|
|
1189
|
+
await tabPage.waitForLoadState("networkidle", { timeout: args.waitMs }).catch(() => {});
|
|
1190
|
+
result.metadata = await tabPage.evaluate(() => ({ title: document.title }));
|
|
1191
|
+
log.info("axe-core: skipped (disabled)");
|
|
1192
|
+
}
|
|
1170
1193
|
|
|
1171
|
-
// Step 2: CDP checks
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1194
|
+
// Step 2: CDP checks (conditional)
|
|
1195
|
+
if (args.engines.cdp) {
|
|
1196
|
+
writeProgress("cdp", "running");
|
|
1197
|
+
cdpViolations = await runCdpChecks(tabPage);
|
|
1198
|
+
writeProgress("cdp", "done", { found: cdpViolations.length });
|
|
1199
|
+
log.info(`CDP checks: ${cdpViolations.length} issue(s) found`);
|
|
1200
|
+
} else {
|
|
1201
|
+
log.info("CDP checks: skipped (disabled)");
|
|
1202
|
+
}
|
|
1176
1203
|
|
|
1177
|
-
// Step 3: pa11y
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1204
|
+
// Step 3: pa11y (conditional)
|
|
1205
|
+
if (args.engines.pa11y) {
|
|
1206
|
+
writeProgress("pa11y", "running");
|
|
1207
|
+
pa11yViolations = await runPa11yChecks(targetUrl, args.axeTags);
|
|
1208
|
+
writeProgress("pa11y", "done", { found: pa11yViolations.length });
|
|
1209
|
+
log.info(`pa11y: ${pa11yViolations.length} issue(s) found`);
|
|
1210
|
+
} else {
|
|
1211
|
+
log.info("pa11y: skipped (disabled)");
|
|
1212
|
+
}
|
|
1182
1213
|
|
|
1183
1214
|
// Step 4: Merge results
|
|
1215
|
+
const axeViolationCount = result.violations?.length || 0;
|
|
1184
1216
|
writeProgress("merge", "running");
|
|
1185
1217
|
const mergedViolations = mergeViolations(
|
|
1186
1218
|
result.violations || [],
|
|
@@ -1220,6 +1252,7 @@ async function _runDomScannerInternal(args) {
|
|
|
1220
1252
|
generated_at: new Date().toISOString(),
|
|
1221
1253
|
base_url: baseUrl,
|
|
1222
1254
|
onlyRule: args.onlyRule || null,
|
|
1255
|
+
engines: args.engines,
|
|
1223
1256
|
projectContext,
|
|
1224
1257
|
routes: results,
|
|
1225
1258
|
};
|