eaa-kit 0.2.1 → 0.3.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/README.md +2 -1
- package/dist/astro/index.d.ts +7 -34
- package/dist/astro/index.js +6 -26
- package/dist/audit/runners/worker.js +1 -1
- package/dist/audit-CuG2hYyo.js +2 -0
- package/dist/audit-DcL73mOC.js +635 -0
- package/dist/{baseline-CV_3lbER.js → baseline-CuKFq4IF.js} +1 -1
- package/dist/{baseline-CgBmzFTr.js → baseline-s9F3fXTN.js} +16 -26
- package/dist/cli/index.js +80 -143
- package/dist/collect-BkAQ0viT.js +123 -0
- package/dist/command-Dxpa00Ha.js +77 -0
- package/dist/component-7kEBjv_y.js +111 -0
- package/dist/{crawl-CtJbMNNb.js → crawl-Oxt2Gaqo.js} +13 -23
- package/dist/frameworks-BYa3tULg.js +243 -0
- package/dist/frameworks-DaDqrJOw.js +2 -0
- package/dist/fs-BmPtmFke.js +40 -0
- package/dist/{html-BLEuzep6.js → html-DKiI_3gs.js} +167 -86
- package/dist/{impact-DvgBjupx.js → impact-YdoOtFqm.js} +15 -1
- package/dist/index.js +2 -2
- package/dist/{init-DRKIdpK1.js → init-DiLiYvN1.js} +13 -10
- package/dist/{jsdom-BEu6Ra_2.js → jsdom-BjpF-2V-.js} +2 -7
- package/dist/jsdom-X4KYfTp8.js +3 -0
- package/dist/json-B0Y7rNjt.js +2 -0
- package/dist/{json-C9xS1PNC.js → json-Cnv9nd6U.js} +13 -21
- package/dist/{load-sCkKsvGQ.js → load-UYXLqGV9.js} +2 -8
- package/dist/manual-Vz-oX1I_.js +239 -0
- package/dist/{playwright-DSRnXmcd.js → playwright-DYFsGUNd.js} +70 -23
- package/dist/{pool-DixLeu8L.js → pool-BWkWZiJW.js} +4 -12
- package/dist/{project-CufCqIE2.js → project-DW08TseF.js} +8 -21
- package/dist/{render-BO0nVrrZ.js → render-DI_aCnAZ.js} +7 -15
- package/dist/{result-2aZPfM8w.js → result-DLxd2Eip.js} +38 -1
- package/dist/{routes-BxbSZKXC.js → routes-C2Cgf6Ko.js} +4 -8
- package/dist/run-BMASMmwO.d.ts +45 -0
- package/dist/run-BW6CVuND.js +36 -0
- package/dist/{sarif-eSCuI0eX.js → sarif-DB3WG7T9.js} +11 -33
- package/dist/text-BFmNtMsV.js +43 -0
- package/dist/vite/index.d.ts +39 -0
- package/dist/vite/index.js +40 -0
- package/package.json +8 -3
- package/dist/audit-B2dIKpJ5.js +0 -2
- package/dist/audit-DXpKkXsC.js +0 -950
- package/dist/escape-Dm1o_RAk.js +0 -21
- package/dist/jsdom-C6dIyaxN.js +0 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/integration/run.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown to fail the build. Its own class so a consumer can tell an audit
|
|
4
|
+
* failure from the build tool falling over.
|
|
5
|
+
*/
|
|
6
|
+
var BuildAuditError = class extends Error {
|
|
7
|
+
name = "BuildAuditError";
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Audit a finished build and decide whether it may proceed.
|
|
11
|
+
*
|
|
12
|
+
* Returns normally when the build should continue, and throws BuildAuditError
|
|
13
|
+
* when it should not.
|
|
14
|
+
*/
|
|
15
|
+
async function auditBuild(directory, options, logger) {
|
|
16
|
+
const { enabled, failBuild, ...auditOptions } = options;
|
|
17
|
+
if (enabled === false) {
|
|
18
|
+
logger.info("skipped (enabled: false)");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const { runAuditCommand } = await import("./audit-CuG2hYyo.js");
|
|
22
|
+
const { exitCode } = await runAuditCommand(directory, auditOptions);
|
|
23
|
+
if (exitCode === 0) {
|
|
24
|
+
logger.info("no violations at or above the threshold");
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const message = exitCode === 2 ? "the audit could not be completed, so this build was not checked" : "accessibility violations at or above the threshold";
|
|
28
|
+
if (failBuild === false) {
|
|
29
|
+
logger.warn(`${message} (failBuild: false, so the build continues)`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
logger.error(message);
|
|
33
|
+
throw new BuildAuditError(`eaa-kit: ${message}`);
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { auditBuild as n, BuildAuditError as t };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as isImpactLevel } from "./impact-YdoOtFqm.js";
|
|
2
|
+
import { a as standardsReference } from "./text-BFmNtMsV.js";
|
|
2
3
|
import { t as TOOL_VERSION } from "./version-B3v4rNoG.js";
|
|
3
4
|
import { t as elementFingerprint } from "./fingerprint-DRoneAjj.js";
|
|
5
|
+
import { a as ruleOutcomes, r as findingElements } from "./result-DLxd2Eip.js";
|
|
4
6
|
import path from "node:path";
|
|
5
7
|
//#region src/audit/report/sarif.ts
|
|
6
8
|
const SARIF_VERSION = "2.1.0";
|
|
@@ -92,29 +94,16 @@ const SUPPRESSED = [{
|
|
|
92
94
|
function toResults(finding, ruleIndex, uri, suppressions) {
|
|
93
95
|
const level = toSarifLevel(finding.impact);
|
|
94
96
|
const location = { physicalLocation: { artifactLocation: { uri } } };
|
|
95
|
-
|
|
97
|
+
return findingElements(finding).map(({ selector, html }) => ({
|
|
96
98
|
ruleId: finding.ruleId,
|
|
97
99
|
ruleIndex,
|
|
98
100
|
level,
|
|
99
101
|
kind: "fail",
|
|
100
|
-
message: { text: finding.help },
|
|
102
|
+
message: { text: selector === "" ? finding.help : `${finding.help}. Element: ${selector}` },
|
|
101
103
|
locations: [location],
|
|
102
|
-
partialFingerprints: fingerprint(finding.ruleId,
|
|
104
|
+
partialFingerprints: fingerprint(finding.ruleId, selector, html),
|
|
103
105
|
...suppressions ? { suppressions } : {}
|
|
104
|
-
}
|
|
105
|
-
return finding.nodes.map((node) => {
|
|
106
|
-
const selector = node.target.join(" ");
|
|
107
|
-
return {
|
|
108
|
-
ruleId: finding.ruleId,
|
|
109
|
-
ruleIndex,
|
|
110
|
-
level,
|
|
111
|
-
kind: "fail",
|
|
112
|
-
message: { text: `${finding.help}. Element: ${selector}` },
|
|
113
|
-
locations: [location],
|
|
114
|
-
partialFingerprints: fingerprint(finding.ruleId, selector, node.html),
|
|
115
|
-
...suppressions ? { suppressions } : {}
|
|
116
|
-
};
|
|
117
|
-
});
|
|
106
|
+
}));
|
|
118
107
|
}
|
|
119
108
|
/**
|
|
120
109
|
* Identifies an alert across runs. Deliberately excludes the file path, so that
|
|
@@ -127,25 +116,14 @@ function fingerprint(ruleId, selector, html) {
|
|
|
127
116
|
/** Every rule the run knows about, so the catalogue is complete in GitHub. */
|
|
128
117
|
function buildRules(audits) {
|
|
129
118
|
const rules = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const audit of audits) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
...audit.accepted ?? [],
|
|
134
|
-
...audit.incomplete,
|
|
135
|
-
...audit.passes,
|
|
136
|
-
...audit.inapplicable
|
|
137
|
-
];
|
|
138
|
-
for (const outcome of outcomes) {
|
|
139
|
-
if (rules.has(outcome.ruleId)) continue;
|
|
140
|
-
rules.set(outcome.ruleId, toSarifRule(outcome));
|
|
141
|
-
}
|
|
119
|
+
for (const audit of audits) for (const outcome of ruleOutcomes(audit)) {
|
|
120
|
+
if (rules.has(outcome.ruleId)) continue;
|
|
121
|
+
rules.set(outcome.ruleId, toSarifRule(outcome));
|
|
142
122
|
}
|
|
143
123
|
return [...rules.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
144
124
|
}
|
|
145
125
|
function toSarifRule(outcome) {
|
|
146
|
-
const
|
|
147
|
-
const clauses = outcome.enClauses.map((clause) => `EN 301 549 ${clause}`);
|
|
148
|
-
const references = [...criteria, ...clauses].join(", ");
|
|
126
|
+
const references = standardsReference(outcome.successCriteria, outcome.enClauses);
|
|
149
127
|
return {
|
|
150
128
|
id: outcome.ruleId,
|
|
151
129
|
shortDescription: { text: outcome.help },
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region src/text.ts
|
|
2
|
+
/**
|
|
3
|
+
* Text this package writes: escaping, counting and the standards references
|
|
4
|
+
* that appear in every report and in the statement.
|
|
5
|
+
*
|
|
6
|
+
* Both documents eaa-kit produces embed text it did not write: an issue
|
|
7
|
+
* description from a config file, axe-core's help text, and — in the audit
|
|
8
|
+
* report — the markup of the element that failed, which is by definition
|
|
9
|
+
* arbitrary HTML from somebody's build. Getting the escaping wrong in the
|
|
10
|
+
* report would mean a page that fails an accessibility audit for having a stray
|
|
11
|
+
* `<script>` hands that script to whoever opens the report.
|
|
12
|
+
*/
|
|
13
|
+
/** For text nodes. Leaves quotes alone, which are fine between tags. */
|
|
14
|
+
function escapeText(value) {
|
|
15
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
16
|
+
}
|
|
17
|
+
/** For attribute values, where a quote would end the attribute. */
|
|
18
|
+
function escapeAttribute(value) {
|
|
19
|
+
return escapeText(value).replace(/"/g, """).replace(/'/g, "'");
|
|
20
|
+
}
|
|
21
|
+
/** `plural(1, 'page')` is `page`, `plural(2, 'page')` is `pages`. */
|
|
22
|
+
function plural(value, noun) {
|
|
23
|
+
return value === 1 ? noun : `${noun}s`;
|
|
24
|
+
}
|
|
25
|
+
/** `count(2, 'page')` is `2 pages`. */
|
|
26
|
+
function count(value, noun) {
|
|
27
|
+
return `${value} ${plural(value, noun)}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Element markup on one line, optionally bounded: one minified page must not be
|
|
31
|
+
* able to fill a report or run past a terminal.
|
|
32
|
+
*/
|
|
33
|
+
function collapse(html, max) {
|
|
34
|
+
const flat = html.replace(/\s+/g, " ").trim();
|
|
35
|
+
if (max === void 0 || flat.length <= max) return flat;
|
|
36
|
+
return `${flat.slice(0, max - 1)}…`;
|
|
37
|
+
}
|
|
38
|
+
/** `WCAG 1.1.1, EN 301 549 9.1.1.1`, in that order, or an empty string. */
|
|
39
|
+
function standardsReference(successCriteria, enClauses) {
|
|
40
|
+
return [...successCriteria.map((criterion) => `WCAG ${criterion}`), ...enClauses.map((clause) => `EN 301 549 ${clause}`)].join(", ");
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { standardsReference as a, escapeText as i, count as n, escapeAttribute as r, collapse as t };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { n as IntegrationOptions, t as BuildAuditError } from "../run-BMASMmwO.js";
|
|
2
|
+
//#region src/vite/index.d.ts
|
|
3
|
+
interface EaaKitPluginOptions extends IntegrationOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Directory to audit, relative to the Vite root. Defaults to the build's own
|
|
6
|
+
* `outDir`, which is the only place the plugin can know the files went.
|
|
7
|
+
*/
|
|
8
|
+
directory?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The parts of Vite this plugin touches, described structurally.
|
|
12
|
+
*
|
|
13
|
+
* So the published .d.ts does not reference vite, and installing eaa-kit in a
|
|
14
|
+
* project that has none costs nothing and still typechecks. The same reasoning
|
|
15
|
+
* as the Astro integration and the Playwright runner.
|
|
16
|
+
*/
|
|
17
|
+
interface ResolvedConfigLike {
|
|
18
|
+
root: string;
|
|
19
|
+
build: {
|
|
20
|
+
outDir: string;
|
|
21
|
+
};
|
|
22
|
+
logger?: {
|
|
23
|
+
info(message: string): void;
|
|
24
|
+
warn(message: string): void;
|
|
25
|
+
error(message: string): void;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
interface VitePluginLike {
|
|
29
|
+
name: string;
|
|
30
|
+
/** Build only: a dev server writes nothing to audit. */
|
|
31
|
+
apply: 'build';
|
|
32
|
+
/** Runs after the bundle is written, which is the earliest the files exist. */
|
|
33
|
+
enforce?: 'post';
|
|
34
|
+
configResolved(config: ResolvedConfigLike): void;
|
|
35
|
+
closeBundle(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
declare function eaaKit(options?: EaaKitPluginOptions): VitePluginLike;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { BuildAuditError, EaaKitPluginOptions, ResolvedConfigLike, VitePluginLike, eaaKit as default };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { n as auditBuild, t as BuildAuditError } from "../run-BW6CVuND.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/vite/index.ts
|
|
4
|
+
function eaaKit(options = {}) {
|
|
5
|
+
let directory;
|
|
6
|
+
let logger;
|
|
7
|
+
return {
|
|
8
|
+
name: "eaa-kit",
|
|
9
|
+
apply: "build",
|
|
10
|
+
enforce: "post",
|
|
11
|
+
configResolved(config) {
|
|
12
|
+
directory = path.resolve(config.root, options.directory ?? config.build.outDir);
|
|
13
|
+
logger = prefixed(config.logger);
|
|
14
|
+
},
|
|
15
|
+
async closeBundle() {
|
|
16
|
+
if (directory === void 0 || logger === void 0) throw new BuildAuditError("eaa-kit: the plugin was not given a resolved Vite config");
|
|
17
|
+
await auditBuild(directory, options, logger);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Vite's own logger where there is one, the console otherwise.
|
|
23
|
+
*
|
|
24
|
+
* Prefixed either way: a line about accessibility in the middle of a build log
|
|
25
|
+
* needs to say what produced it.
|
|
26
|
+
*/
|
|
27
|
+
function prefixed(logger) {
|
|
28
|
+
const write = (level, message) => {
|
|
29
|
+
const line = `eaa-kit: ${message}`;
|
|
30
|
+
if (logger === void 0) process.stderr.write(`${line}\n`);
|
|
31
|
+
else logger[level](line);
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
info: (message) => write("info", message),
|
|
35
|
+
warn: (message) => write("warn", message),
|
|
36
|
+
error: (message) => write("error", message)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { BuildAuditError, eaaKit as default };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eaa-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "WCAG 2.2 AA auditor and EU accessibility statement generator (EAA / BFSG / BaFG). Audits a static build or a running site from the command line.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"types": "./dist/astro/index.d.ts",
|
|
41
41
|
"import": "./dist/astro/index.js"
|
|
42
42
|
},
|
|
43
|
+
"./vite": {
|
|
44
|
+
"types": "./dist/vite/index.d.ts",
|
|
45
|
+
"import": "./dist/vite/index.js"
|
|
46
|
+
},
|
|
43
47
|
"./package.json": "./package.json"
|
|
44
48
|
},
|
|
45
49
|
"files": [
|
|
@@ -54,7 +58,8 @@
|
|
|
54
58
|
"lint": "biome check .",
|
|
55
59
|
"format": "biome check --write .",
|
|
56
60
|
"smoke": "pnpm build && node dist/cli/index.js audit tests/fixtures/site --include about/** --format json && node dist/cli/index.js audit tests/fixtures/site --include \"about/**\" \"blog/**\" \"drafts/**\" \"legacy.htm\" --concurrency 2 && node dist/cli/index.js audit tests/fixtures/site --include about/** --format html && node dist/cli/index.js audit tests/fixtures/site --baseline examples/baseline.json && node -e \"import('./dist/astro/index.js').then(m => { const i = m.default(); if (i.name !== 'eaa-kit' || typeof i.hooks['astro:build:done'] !== 'function') { throw new Error('astro entry point is not an integration') } console.log('astro entry ok') })\" && node dist/cli/index.js statement --config examples/eaa.config.json && node dist/cli/index.js statement --config examples/eaa.config.json --audit examples/report.json --format html",
|
|
57
|
-
"examples": "pnpm build && node scripts/generate-examples.mjs"
|
|
61
|
+
"examples": "pnpm build && node scripts/generate-examples.mjs",
|
|
62
|
+
"test:packaged": "pnpm build && node scripts/test-packaged.mjs"
|
|
58
63
|
},
|
|
59
64
|
"peerDependencies": {
|
|
60
65
|
"playwright": ">=1.40.0",
|
package/dist/audit-B2dIKpJ5.js
DELETED