@staticforge/checker 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +6 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +28 -0
- package/dist/cache.js.map +1 -0
- package/dist/check.d.ts +79 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +336 -0
- package/dist/check.js.map +1 -0
- package/dist/headless.d.ts +25 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +209 -0
- package/dist/headless.js.map +1 -0
- package/dist/html-validate.d.ts +4 -0
- package/dist/html-validate.d.ts.map +1 -0
- package/dist/html-validate.js +103 -0
- package/dist/html-validate.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/output-rules.d.ts +25 -0
- package/dist/output-rules.d.ts.map +1 -0
- package/dist/output-rules.js +265 -0
- package/dist/output-rules.js.map +1 -0
- package/dist/report/index.d.ts +5 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +5 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/junit.d.ts +8 -0
- package/dist/report/junit.d.ts.map +1 -0
- package/dist/report/junit.js +51 -0
- package/dist/report/junit.js.map +1 -0
- package/dist/report/pretty.d.ts +9 -0
- package/dist/report/pretty.d.ts.map +1 -0
- package/dist/report/pretty.js +92 -0
- package/dist/report/pretty.js.map +1 -0
- package/dist/report/sarif.d.ts +14 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +65 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/schema.d.ts +3 -0
- package/dist/report/schema.d.ts.map +1 -0
- package/dist/report/schema.js +57 -0
- package/dist/report/schema.js.map +1 -0
- package/dist/source-rules.d.ts +24 -0
- package/dist/source-rules.d.ts.map +1 -0
- package/dist/source-rules.js +327 -0
- package/dist/source-rules.js.map +1 -0
- package/dist/tasks.d.ts +36 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +30 -0
- package/dist/tasks.js.map +1 -0
- package/dist/text.d.ts +32 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +55 -0
- package/dist/text.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +62 -3
package/dist/check.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { createTwoFilesPatch } from 'diff';
|
|
5
|
+
import { fromHtml } from 'hast-util-from-html';
|
|
6
|
+
import { MANIFEST_PATH, buildSite, hashOf, memoized, stableJson, } from '@staticforge/build';
|
|
7
|
+
import { ErrorCode, SforgeError, countBySeverity, createWorkerPool, mapInPool, poolSize, sortDiagnostics, } from '@staticforge/core';
|
|
8
|
+
import { INSTALL_BROWSER_COMMAND, checkRendered } from './headless.js';
|
|
9
|
+
import { checkOutput, idsOf } from './output-rules.js';
|
|
10
|
+
import { pageUrlMap } from './source-rules.js';
|
|
11
|
+
import { MIN_PAGES_FOR_WORKERS, SOURCE_TASK, VALIDATE_TASK, checkSources, validatePages, } from './tasks.js';
|
|
12
|
+
import { applyEdits } from './text.js';
|
|
13
|
+
/**
|
|
14
|
+
* Rules checked completely by more than one layer: on a page, findings from
|
|
15
|
+
* the most precise layer replace the others.
|
|
16
|
+
*/
|
|
17
|
+
const ONE_PER_PAGE = new Set([
|
|
18
|
+
'document-title',
|
|
19
|
+
'heading-order',
|
|
20
|
+
'html-lang',
|
|
21
|
+
'iframe-title',
|
|
22
|
+
'img-alt',
|
|
23
|
+
'meta-viewport',
|
|
24
|
+
'video-captions',
|
|
25
|
+
]);
|
|
26
|
+
/**
|
|
27
|
+
* Hash of what output rules see of the whole site: its files and every page's
|
|
28
|
+
* ids. Undefined (no caching) when the build has no manifest to list its files.
|
|
29
|
+
*/
|
|
30
|
+
function siteKeyOf(outDir, ids, trailingSlash) {
|
|
31
|
+
let files;
|
|
32
|
+
try {
|
|
33
|
+
files = JSON.parse(readFileSync(join(outDir, MANIFEST_PATH), 'utf8'))
|
|
34
|
+
.files;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return hashOf(stableJson(Object.keys(files).sort()), stableJson(ids), trailingSlash);
|
|
40
|
+
}
|
|
41
|
+
/** Most precise first: source positions beat output selectors beat rendered ones. */
|
|
42
|
+
const PRECISION = { source: 0, build: 1, output: 2, rendered: 3 };
|
|
43
|
+
/** `/blog/**` matches every page under /blog/; `content/docs/*.md` a set of sources. */
|
|
44
|
+
export function globToRegExp(glob) {
|
|
45
|
+
let pattern = '';
|
|
46
|
+
for (let i = 0; i < glob.length; i++) {
|
|
47
|
+
const char = glob[i];
|
|
48
|
+
if (char === '*') {
|
|
49
|
+
if (glob[i + 1] === '*') {
|
|
50
|
+
pattern += '.*';
|
|
51
|
+
i++;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
pattern += '[^/]*';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (char === '?') {
|
|
58
|
+
pattern += '[^/]';
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
pattern += char.replace(/[\\^$.+()|{}[\]]/g, '\\$&');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return new RegExp(`^${pattern}$`);
|
|
65
|
+
}
|
|
66
|
+
const ignored = (ruleId, patterns) => patterns.some((p) => (p.endsWith('*') ? ruleId.startsWith(p.slice(0, -1)) : ruleId === p));
|
|
67
|
+
/**
|
|
68
|
+
* Drop findings that a more precise layer already reports: the same rule on
|
|
69
|
+
* the same page and, for rules that identify their target (links), the same target.
|
|
70
|
+
*/
|
|
71
|
+
export function dedupe(findings) {
|
|
72
|
+
// Candidates by rule and page (file or URL), so the lookup stays linear.
|
|
73
|
+
const index = new Map();
|
|
74
|
+
const keysOf = (f) => [
|
|
75
|
+
f.diagnostic.file !== undefined ? `f\u0000${f.diagnostic.file}` : undefined,
|
|
76
|
+
f.diagnostic.url !== undefined ? `u\u0000${f.diagnostic.url}` : undefined,
|
|
77
|
+
]
|
|
78
|
+
.filter((k) => k !== undefined)
|
|
79
|
+
.map((k) => `${f.diagnostic.ruleId}\u0000${k}`);
|
|
80
|
+
for (const f of findings) {
|
|
81
|
+
for (const k of keysOf(f))
|
|
82
|
+
index.set(k, [...(index.get(k) ?? []), f]);
|
|
83
|
+
}
|
|
84
|
+
return findings.filter((f) => !keysOf(f).some((k) => (index.get(k) ?? []).some((other) => PRECISION[other.layer] < PRECISION[f.layer] &&
|
|
85
|
+
(ONE_PER_PAGE.has(f.diagnostic.ruleId) ||
|
|
86
|
+
(other.match !== undefined && other.match === f.match)))));
|
|
87
|
+
}
|
|
88
|
+
/** De-duplicate, drop ignored rules (`check.ignore`, `axe/*` style prefixes allowed) and sort. */
|
|
89
|
+
export function finalizeDiagnostics(findings, ignore) {
|
|
90
|
+
return sortDiagnostics(dedupe(findings)
|
|
91
|
+
.map((f) => f.diagnostic)
|
|
92
|
+
.filter((d) => !ignored(d.ruleId, ignore)));
|
|
93
|
+
}
|
|
94
|
+
/** Check a finished build: its own diagnostics plus the source, output and rendered rules. */
|
|
95
|
+
export async function checkBuild(options) {
|
|
96
|
+
if (options.pool !== undefined)
|
|
97
|
+
return check(options, options.pool);
|
|
98
|
+
// Workers start on first use, so a small site never pays for them.
|
|
99
|
+
const size = poolSize(options.config.build.workers);
|
|
100
|
+
const pool = size > 0 ? createWorkerPool(size) : undefined;
|
|
101
|
+
try {
|
|
102
|
+
return await check(options, pool);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await pool?.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function check(options, pool) {
|
|
109
|
+
const { rootDir, config, build, logger, cache } = options;
|
|
110
|
+
cache?.begin();
|
|
111
|
+
const findings = build.diagnostics.map((diagnostic) => ({
|
|
112
|
+
layer: 'build',
|
|
113
|
+
diagnostic,
|
|
114
|
+
}));
|
|
115
|
+
const pages = build.pages.map((p) => ({
|
|
116
|
+
url: p.url,
|
|
117
|
+
source: p.source,
|
|
118
|
+
output: p.output,
|
|
119
|
+
kind: p.source === undefined ? 'generated' : p.layout === 'document' ? 'document' : 'fragment',
|
|
120
|
+
}));
|
|
121
|
+
const pageUrls = pageUrlMap(pages);
|
|
122
|
+
// Source rules (in workers for large sites).
|
|
123
|
+
const sourceItems = pages.flatMap((page) => page.source === undefined
|
|
124
|
+
? []
|
|
125
|
+
: [
|
|
126
|
+
{
|
|
127
|
+
page: { ...page, source: page.source },
|
|
128
|
+
text: readFileSync(join(rootDir, page.source), 'utf8'),
|
|
129
|
+
},
|
|
130
|
+
]);
|
|
131
|
+
const sourceContext = {
|
|
132
|
+
language: config.site.language,
|
|
133
|
+
pageUrls,
|
|
134
|
+
trailingSlash: config.build.trailingSlash,
|
|
135
|
+
};
|
|
136
|
+
const sourceKey = cache && hashOf(stableJson(sourceContext));
|
|
137
|
+
const sourceFindings = await memoized(sourceItems, cache?.sources, (item) => hashOf(sourceKey ?? '', stableJson(item.page), item.text), (missing) => mapInPool(pool, SOURCE_TASK, missing, sourceContext, checkSources, {
|
|
138
|
+
minItems: MIN_PAGES_FOR_WORKERS,
|
|
139
|
+
}), () => true);
|
|
140
|
+
if (cache)
|
|
141
|
+
cache.stats.sources = sourceFindings.computed;
|
|
142
|
+
findings.push(...sourceFindings.results.flat());
|
|
143
|
+
// Output rules. Links can point at fragment ids on any page, so every page's ids are needed.
|
|
144
|
+
const htmls = new Map();
|
|
145
|
+
const trees = new Map();
|
|
146
|
+
const treeOf = (output) => {
|
|
147
|
+
let tree = trees.get(output);
|
|
148
|
+
if (tree === undefined) {
|
|
149
|
+
tree = fromHtml(htmls.get(output) ?? '');
|
|
150
|
+
trees.set(output, tree);
|
|
151
|
+
}
|
|
152
|
+
return tree;
|
|
153
|
+
};
|
|
154
|
+
const ids = new Map();
|
|
155
|
+
for (const page of pages) {
|
|
156
|
+
const html = readFileSync(join(build.outDir, page.output), 'utf8');
|
|
157
|
+
htmls.set(page.output, html);
|
|
158
|
+
const key = cache && hashOf(html);
|
|
159
|
+
let pageIds = key === undefined ? undefined : cache?.ids.get(key);
|
|
160
|
+
if (pageIds === undefined) {
|
|
161
|
+
pageIds = idsOf(treeOf(page.output));
|
|
162
|
+
if (key !== undefined)
|
|
163
|
+
cache?.ids.set(key, pageIds);
|
|
164
|
+
}
|
|
165
|
+
ids.set(page.output, pageIds);
|
|
166
|
+
}
|
|
167
|
+
const filters = (options.pages ?? []).map(globToRegExp);
|
|
168
|
+
const selected = pages.filter((p) => filters.length === 0 ||
|
|
169
|
+
filters.some((re) => re.test(p.url) || (p.source !== undefined && re.test(p.source))));
|
|
170
|
+
// The search index may still be being written (`sforge dev` defers it).
|
|
171
|
+
const exists = (path) => {
|
|
172
|
+
if (config.search.enabled && path.startsWith('pagefind/'))
|
|
173
|
+
return true;
|
|
174
|
+
const full = join(build.outDir, path);
|
|
175
|
+
return existsSync(full) && statSync(full).isFile();
|
|
176
|
+
};
|
|
177
|
+
const pageKey = (page) => stableJson(page) + hashOf(htmls.get(page.output) ?? '');
|
|
178
|
+
// html-validate (in workers for large sites).
|
|
179
|
+
const validated = await memoized(selected, cache?.validated, (page) => hashOf(pageKey(page)), (missing) => mapInPool(pool, VALIDATE_TASK, missing.map((page) => ({ page, html: htmls.get(page.output) ?? '' })), undefined, validatePages, { minItems: MIN_PAGES_FOR_WORKERS }), () => true);
|
|
180
|
+
if (cache)
|
|
181
|
+
cache.stats.validated = validated.computed;
|
|
182
|
+
// staticforge's output rules, here: they need every page's ids. Their results
|
|
183
|
+
// depend on the files and ids of the whole site.
|
|
184
|
+
const siteKey = cache && siteKeyOf(build.outDir, ids, config.build.trailingSlash);
|
|
185
|
+
const output = await memoized(selected, siteKey === undefined ? undefined : cache?.output, (page) => hashOf(siteKey ?? '', pageKey(page)), async (missing) => missing.map((page) => checkOutput({
|
|
186
|
+
page,
|
|
187
|
+
tree: treeOf(page.output),
|
|
188
|
+
exists,
|
|
189
|
+
idsOf: (target) => ids.get(target),
|
|
190
|
+
trailingSlash: config.build.trailingSlash,
|
|
191
|
+
})), () => true);
|
|
192
|
+
if (cache)
|
|
193
|
+
cache.stats.output = output.computed;
|
|
194
|
+
for (const [i] of selected.entries()) {
|
|
195
|
+
findings.push(...(output.results[i] ?? []), ...(validated.results[i] ?? []));
|
|
196
|
+
}
|
|
197
|
+
cache?.commit();
|
|
198
|
+
// Rendered rules.
|
|
199
|
+
let rendered = 'off';
|
|
200
|
+
if (options.headless && selected.length > 0) {
|
|
201
|
+
try {
|
|
202
|
+
findings.push(...(await checkRendered({
|
|
203
|
+
dir: build.outDir,
|
|
204
|
+
pages: selected,
|
|
205
|
+
trailingSlash: config.build.trailingSlash,
|
|
206
|
+
wcag: config.check.wcag,
|
|
207
|
+
...(options.executablePath !== undefined && { executablePath: options.executablePath }),
|
|
208
|
+
...(logger && { logger }),
|
|
209
|
+
})));
|
|
210
|
+
rendered = 'ran';
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
if (!(error instanceof SforgeError) || error.code !== ErrorCode.BrowserMissing)
|
|
214
|
+
throw error;
|
|
215
|
+
if (options.requireBrowser)
|
|
216
|
+
throw error;
|
|
217
|
+
rendered = 'skipped';
|
|
218
|
+
findings.push({
|
|
219
|
+
layer: 'rendered',
|
|
220
|
+
diagnostic: {
|
|
221
|
+
ruleId: 'headless-skipped',
|
|
222
|
+
severity: 'warning',
|
|
223
|
+
message: 'Rendered checks (contrast, target size, focus) were skipped: Chromium is not installed',
|
|
224
|
+
hint: `Run \`${INSTALL_BROWSER_COMMAND}\`, or set check.headless to false.`,
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (filters.length === 0)
|
|
230
|
+
return { findings, pages: selected.length, rendered };
|
|
231
|
+
// Drop build and source findings that belong to pages outside the selection.
|
|
232
|
+
const unselected = pages.filter((p) => !selected.includes(p));
|
|
233
|
+
const urls = new Set(unselected.map((p) => p.url));
|
|
234
|
+
const sources = new Set(unselected.flatMap((p) => (p.source === undefined ? [] : [p.source])));
|
|
235
|
+
return {
|
|
236
|
+
findings: findings.filter(({ diagnostic: d }) => !(d.url !== undefined && urls.has(d.url)) && !(d.file !== undefined && sources.has(d.file))),
|
|
237
|
+
pages: selected.length,
|
|
238
|
+
rendered,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function buildAndCheck(options, headless) {
|
|
242
|
+
const scratch = mkdtempSync(join(tmpdir(), 'sforge-check-'));
|
|
243
|
+
try {
|
|
244
|
+
const build = await buildSite({
|
|
245
|
+
rootDir: options.rootDir,
|
|
246
|
+
config: options.config,
|
|
247
|
+
scratchDir: scratch,
|
|
248
|
+
...(options.pool && { pool: options.pool }),
|
|
249
|
+
...(options.drafts !== undefined && { drafts: options.drafts }),
|
|
250
|
+
...(options.generator !== undefined && { generator: options.generator }),
|
|
251
|
+
...(options.logger && { logger: options.logger }),
|
|
252
|
+
});
|
|
253
|
+
return await checkBuild({ ...options, build, headless });
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function collectFixes(rootDir, findings) {
|
|
260
|
+
const byFile = new Map();
|
|
261
|
+
for (const f of findings) {
|
|
262
|
+
if (f.edits === undefined || f.diagnostic.file === undefined)
|
|
263
|
+
continue;
|
|
264
|
+
const entry = byFile.get(f.diagnostic.file) ?? { edits: [], ruleIds: new Set() };
|
|
265
|
+
entry.edits.push(...f.edits);
|
|
266
|
+
entry.ruleIds.add(f.diagnostic.ruleId);
|
|
267
|
+
byFile.set(f.diagnostic.file, entry);
|
|
268
|
+
}
|
|
269
|
+
const files = [];
|
|
270
|
+
const writes = [];
|
|
271
|
+
let diff = '';
|
|
272
|
+
for (const [file, { edits, ruleIds }] of [...byFile].sort(([a], [b]) => a.localeCompare(b))) {
|
|
273
|
+
const path = join(rootDir, file);
|
|
274
|
+
const before = readFileSync(path, 'utf8');
|
|
275
|
+
const { text, skipped } = applyEdits(before, edits);
|
|
276
|
+
if (text === before)
|
|
277
|
+
continue;
|
|
278
|
+
files.push({ file, ruleIds: [...ruleIds].sort(), edits: edits.length - skipped.length });
|
|
279
|
+
writes.push({ path, text });
|
|
280
|
+
diff += createTwoFilesPatch(`a/${file}`, `b/${file}`, before, text, '', '', { context: 3 });
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
files,
|
|
284
|
+
diff,
|
|
285
|
+
write: () => {
|
|
286
|
+
for (const { path, text } of writes)
|
|
287
|
+
writeFileSync(path, text);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/** Build the site into a temporary directory and check it, optionally fixing what it safely can. */
|
|
292
|
+
export async function checkSite(options) {
|
|
293
|
+
if (options.pool !== undefined)
|
|
294
|
+
return checkSiteWith(options);
|
|
295
|
+
// One pool for every build and check of the run (a fix runs both twice).
|
|
296
|
+
const size = poolSize(options.config.build.workers);
|
|
297
|
+
const pool = size > 0 ? createWorkerPool(size) : undefined;
|
|
298
|
+
try {
|
|
299
|
+
return await checkSiteWith({ ...options, ...(pool && { pool }) });
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
await pool?.close();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function checkSiteWith(options) {
|
|
306
|
+
const started = performance.now();
|
|
307
|
+
const { config } = options;
|
|
308
|
+
const headless = options.headless ?? config.check.headless;
|
|
309
|
+
const failOn = options.failOn ?? config.check.failOn;
|
|
310
|
+
let fixes;
|
|
311
|
+
if (options.fix === 'apply') {
|
|
312
|
+
// Fixes come from source rules, so the first pass needs no browser.
|
|
313
|
+
const first = await buildAndCheck(options, false);
|
|
314
|
+
const collected = collectFixes(options.rootDir, first.findings);
|
|
315
|
+
collected.write();
|
|
316
|
+
fixes = { applied: true, files: collected.files, diff: collected.diff };
|
|
317
|
+
}
|
|
318
|
+
const result = await buildAndCheck(options, headless);
|
|
319
|
+
if (options.fix === 'dry-run') {
|
|
320
|
+
const collected = collectFixes(options.rootDir, result.findings);
|
|
321
|
+
fixes = { applied: false, files: collected.files, diff: collected.diff };
|
|
322
|
+
}
|
|
323
|
+
const diagnostics = finalizeDiagnostics(result.findings, config.check.ignore);
|
|
324
|
+
const summary = countBySeverity(diagnostics);
|
|
325
|
+
return {
|
|
326
|
+
ok: failOn === 'error' ? summary.error === 0 : summary.error + summary.warning === 0,
|
|
327
|
+
failOn,
|
|
328
|
+
pages: result.pages,
|
|
329
|
+
rendered: result.rendered,
|
|
330
|
+
diagnostics,
|
|
331
|
+
summary,
|
|
332
|
+
...(fixes && { fixes }),
|
|
333
|
+
durationMs: Math.round(performance.now() - started),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAEL,aAAa,EACb,SAAS,EACT,MAAM,EACN,QAAQ,EACR,UAAU,GACX,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAEL,SAAS,EAGT,WAAW,EAEX,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,QAAQ,EACR,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EACL,qBAAqB,EACrB,WAAW,EAEX,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAiB,UAAU,EAAE,MAAM,WAAW,CAAC;AAqEtD;;;GAGG;AACH,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC;IAC3B,gBAAgB;IAChB,eAAe;IACf,WAAW;IACX,cAAc;IACd,SAAS;IACT,eAAe;IACf,gBAAgB;CACjB,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAS,SAAS,CAChB,MAAc,EACd,GAA6B,EAC7B,aAAqB;IAErB,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,CAAC,CAAwB;aAC1F,KAAK,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;AACjG,CAAC;AAED,qFAAqF;AACrF,MAAM,SAAS,GAAqC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAEpG,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;QAC/B,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxB,OAAO,IAAI,IAAI,CAAC;gBAChB,CAAC,EAAE,CAAC;YACN,CAAC;iBAAM,CAAC;gBACN,OAAO,IAAI,OAAO,CAAC;YACrB,CAAC;QACH,CAAC;aAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACxB,OAAO,IAAI,MAAM,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,QAAkB,EAAW,EAAE,CAC9D,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC;AAE7F;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,QAAmB;IACxC,yEAAyE;IACzE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,CAAU,EAAY,EAAE,CACtC;QACE,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS;QAC3E,CAAC,CAAC,UAAU,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;KAC1E;SACE,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,SAAS,CAAC,EAAE,CAAC,CAAC;IACpD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACpB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CACvB,CAAC,KAAK,EAAE,EAAE,CACR,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;QAC3C,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;YACpC,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAC5D,CACF,CACJ,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,mBAAmB,CAAC,QAAmB,EAAE,MAAgB;IACvE,OAAO,eAAe,CACpB,MAAM,CAAC,QAAQ,CAAC;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA0B;IACzD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,mEAAmE;IACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,EAAE,KAAK,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,KAAK,CAClB,OAA0B,EAC1B,IAA4B;IAE5B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1D,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,QAAQ,GAAc,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACjE,KAAK,EAAE,OAAO;QACd,UAAU;KACX,CAAC,CAAC,CAAC;IAEJ,MAAM,KAAK,GAAgB,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACjD,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU;KAC/F,CAAC,CAAC,CAAC;IACJ,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEnC,6CAA6C;IAC7C,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAgB,EAAE,CACvD,IAAI,CAAC,MAAM,KAAK,SAAS;QACvB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;YACE;gBACE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;gBACtC,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;aACvD;SACF,CACN,CAAC;IACF,MAAM,aAAa,GAAG;QACpB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ;QAC9B,QAAQ;QACR,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;KAC1C,CAAC;IACF,MAAM,SAAS,GAAG,KAAK,IAAI,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC;IAC7D,MAAM,cAAc,GAAG,MAAM,QAAQ,CACnC,WAAW,EACX,KAAK,EAAE,OAAO,EACd,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,EACnE,CAAC,OAAO,EAAE,EAAE,CACV,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE;QACjE,QAAQ,EAAE,qBAAqB;KAChC,CAAC,EACJ,GAAG,EAAE,CAAC,IAAI,CACX,CAAC;IACF,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC;IACzD,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAEhD,6FAA6F;IAC7F,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IACtC,MAAM,MAAM,GAAG,CAAC,MAAc,EAAQ,EAAE;QACtC,IAAI,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACzC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACnE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,OAAO,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACrC,IAAI,GAAG,KAAK,SAAS;gBAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACxD,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAC3B,CAAC,CAAC,EAAE,EAAE,CACJ,OAAO,CAAC,MAAM,KAAK,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CACxF,CAAC;IACF,wEAAwE;IACxE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAW,EAAE;QACvC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QACvE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACtC,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IACrD,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,CAAC,IAAe,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAE7F,8CAA8C;IAC9C,MAAM,SAAS,GAAG,MAAM,QAAQ,CAC9B,QAAQ,EACR,KAAK,EAAE,SAAS,EAChB,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAC/B,CAAC,OAAO,EAAE,EAAE,CACV,SAAS,CACP,IAAI,EACJ,aAAa,EACb,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EACrE,SAAS,EACT,aAAa,EACb,EAAE,QAAQ,EAAE,qBAAqB,EAAE,CACpC,EACH,GAAG,EAAE,CAAC,IAAI,CACX,CAAC;IACF,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC;IAEtD,8EAA8E;IAC9E,iDAAiD;IACjD,MAAM,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAClF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,QAAQ,EACR,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,EACjD,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAC9C,KAAK,EAAE,OAAO,EAAE,EAAE,CAChB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACnB,WAAW,CAAC;QACV,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACzB,MAAM;QACN,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;QAClC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;KAC1C,CAAC,CACH,EACH,GAAG,EAAE,CAAC,IAAI,CACX,CAAC;IACF,IAAI,KAAK;QAAE,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;IAChD,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,KAAK,EAAE,MAAM,EAAE,CAAC;IAEhB,kBAAkB;IAClB,IAAI,QAAQ,GAAiC,KAAK,CAAC;IACnD,IAAI,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,QAAQ,CAAC,IAAI,CACX,GAAG,CAAC,MAAM,aAAa,CAAC;gBACtB,GAAG,EAAE,KAAK,CAAC,MAAM;gBACjB,KAAK,EAAE,QAAQ;gBACf,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa;gBACzC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI;gBACvB,GAAG,CAAC,OAAO,CAAC,cAAc,KAAK,SAAS,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;gBACvF,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;aAC1B,CAAC,CAAC,CACJ,CAAC;YACF,QAAQ,GAAG,KAAK,CAAC;QACnB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC;YAC5F,IAAI,OAAO,CAAC,cAAc;gBAAE,MAAM,KAAK,CAAC;YACxC,QAAQ,GAAG,SAAS,CAAC;YACrB,QAAQ,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,UAAU;gBACjB,UAAU,EAAE;oBACV,MAAM,EAAE,kBAAkB;oBAC1B,QAAQ,EAAE,SAAS;oBACnB,OAAO,EACL,wFAAwF;oBAC1F,IAAI,EAAE,SAAS,uBAAuB,qCAAqC;iBAC5E;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IAEhF,6EAA6E;IAC7E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,MAAM,CACvB,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CACpB,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAC9F;QACD,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,OAAqB,EAAE,QAAiB;IACnE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC;YAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,UAAU,EAAE,OAAO;YACnB,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;YAC3C,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;YAC/D,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;YACxE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,OAAO,MAAM,UAAU,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CACnB,OAAe,EACf,QAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuD,CAAC;IAC9E,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACvE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QACjF,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAqC,EAAE,CAAC;IACpD,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QACpD,IAAI,IAAI,KAAK,MAAM;YAAE,SAAS;QAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACzF,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5B,IAAI,IAAI,mBAAmB,CAAC,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO;QACL,KAAK;QACL,IAAI;QACJ,KAAK,EAAE,GAAG,EAAE;YACV,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM;gBAAE,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC;AAED,oGAAoG;AACpG,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAqB;IACnD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9D,yEAAyE;IACzE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACpE,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,EAAE,KAAK,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,OAAqB;IAChD,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC3D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;IAErD,IAAI,KAA2B,CAAC;IAChC,IAAI,OAAO,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;QAC5B,oEAAoE;QACpE,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChE,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,KAAK,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;IAC1E,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjE,KAAK,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,WAAW,GAAG,mBAAmB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9E,MAAM,OAAO,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC7C,OAAO;QACL,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,OAAO,KAAK,CAAC;QACpF,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,WAAW;QACX,OAAO;QACP,GAAG,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,CAAC;QACvB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;KACpD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Logger } from '@staticforge/core';
|
|
2
|
+
import type { CheckPage, Finding } from './types.js';
|
|
3
|
+
export type WcagLevel = '2.1-AA' | '2.2-AA' | '2.2-AAA';
|
|
4
|
+
export interface HeadlessOptions {
|
|
5
|
+
/** Built site to serve. */
|
|
6
|
+
dir: string;
|
|
7
|
+
pages: CheckPage[];
|
|
8
|
+
trailingSlash: 'always' | 'never';
|
|
9
|
+
wcag: WcagLevel;
|
|
10
|
+
/** Chromium binary to use instead of Playwright's (e.g. a system Chrome). */
|
|
11
|
+
executablePath?: string;
|
|
12
|
+
/** Pages checked in parallel. */
|
|
13
|
+
concurrency?: number;
|
|
14
|
+
timeoutMs?: number;
|
|
15
|
+
logger?: Logger;
|
|
16
|
+
}
|
|
17
|
+
export declare const PLAYWRIGHT_VERSION: string;
|
|
18
|
+
/** Command that downloads the browser this version of the checker drives. */
|
|
19
|
+
export declare const INSTALL_BROWSER_COMMAND: string;
|
|
20
|
+
/**
|
|
21
|
+
* Load each page in headless Chromium and run axe-core: the checks that need
|
|
22
|
+
* layout and computed styles (contrast, target size, focus, reflow).
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkRendered(options: HeadlessOptions): Promise<Finding[]>;
|
|
25
|
+
//# sourceMappingURL=headless.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless.d.ts","sourceRoot":"","sources":["../src/headless.ts"],"names":[],"mappings":"AASA,OAAO,EAAa,KAAK,MAAM,EAAe,MAAM,mBAAmB,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,aAAa,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmCD,eAAO,MAAM,kBAAkB,QACrB,CAAC;AAEX,6EAA6E;AAC7E,eAAO,MAAM,uBAAuB,QAA4E,CAAC;AA4DjH;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAsChF"}
|
package/dist/headless.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { chromium, } from 'playwright-core';
|
|
4
|
+
import { ErrorCode, SforgeError } from '@staticforge/core';
|
|
5
|
+
import { startStaticServer } from '@staticforge/server';
|
|
6
|
+
/** The page is checked once in each of these, so contrast is verified in both color schemes. */
|
|
7
|
+
const MODES = [
|
|
8
|
+
{ name: 'desktop', options: { viewport: { width: 1280, height: 800 }, colorScheme: 'light' } },
|
|
9
|
+
{ name: 'dark', options: { viewport: { width: 1280, height: 800 }, colorScheme: 'dark' } },
|
|
10
|
+
{
|
|
11
|
+
name: 'mobile',
|
|
12
|
+
options: {
|
|
13
|
+
viewport: { width: 375, height: 812 },
|
|
14
|
+
colorScheme: 'light',
|
|
15
|
+
isMobile: true,
|
|
16
|
+
hasTouch: true,
|
|
17
|
+
deviceScaleFactor: 2,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
const TAGS = {
|
|
22
|
+
'2.1-AA': ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'],
|
|
23
|
+
'2.2-AA': ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'],
|
|
24
|
+
'2.2-AAA': [
|
|
25
|
+
'wcag2a',
|
|
26
|
+
'wcag2aa',
|
|
27
|
+
'wcag2aaa',
|
|
28
|
+
'wcag21a',
|
|
29
|
+
'wcag21aa',
|
|
30
|
+
'wcag21aaa',
|
|
31
|
+
'wcag22aa',
|
|
32
|
+
'wcag22aaa',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
export const PLAYWRIGHT_VERSION = require('playwright-core/package.json')
|
|
37
|
+
.version;
|
|
38
|
+
/** Command that downloads the browser this version of the checker drives. */
|
|
39
|
+
export const INSTALL_BROWSER_COMMAND = `npx playwright-core@${PLAYWRIGHT_VERSION} install --only-shell chromium`;
|
|
40
|
+
let axeSource;
|
|
41
|
+
/** axe rules that check the same thing as a staticforge rule report under its ID. */
|
|
42
|
+
const ALIASES = {
|
|
43
|
+
'document-title': 'document-title',
|
|
44
|
+
'frame-title': 'iframe-title',
|
|
45
|
+
'html-has-lang': 'html-lang',
|
|
46
|
+
'image-alt': 'img-alt',
|
|
47
|
+
'meta-viewport': 'meta-viewport',
|
|
48
|
+
'video-caption': 'video-captions',
|
|
49
|
+
};
|
|
50
|
+
const severityOf = (impact) => impact === 'critical' || impact === 'serious' ? 'error' : 'warning';
|
|
51
|
+
const wcagOf = (tags) => {
|
|
52
|
+
for (const tag of tags) {
|
|
53
|
+
const match = /^wcag(\d)(\d)(\d+)$/.exec(tag);
|
|
54
|
+
if (match)
|
|
55
|
+
return `${match[1]}.${match[2]}.${match[3]}`;
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* "Fix any of the following:\n Element has insufficient contrast…" → its
|
|
61
|
+
* first reason. The alternatives that follow add length, not information.
|
|
62
|
+
*/
|
|
63
|
+
const summarize = (summary) => (summary ?? '')
|
|
64
|
+
.split('\n')
|
|
65
|
+
.map((l) => l.trim())
|
|
66
|
+
.find((l) => l !== '' && !/^Fix (any|all) of the following:$/.test(l));
|
|
67
|
+
async function launch(executablePath) {
|
|
68
|
+
try {
|
|
69
|
+
return await chromium.launch({ headless: true, ...(executablePath && { executablePath }) });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
+
if (/Executable doesn't exist|executable.*not found|ENOENT/i.test(message)) {
|
|
74
|
+
throw new SforgeError(ErrorCode.BrowserMissing, 'Chromium is not installed for headless checks', {
|
|
75
|
+
hint: `Run \`${INSTALL_BROWSER_COMMAND}\`, set SFORGE_CHROMIUM_PATH to a Chrome/Chromium binary, or pass --no-headless.`,
|
|
76
|
+
cause: error,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Load each page in headless Chromium and run axe-core: the checks that need
|
|
84
|
+
* layout and computed styles (contrast, target size, focus, reflow).
|
|
85
|
+
*/
|
|
86
|
+
export async function checkRendered(options) {
|
|
87
|
+
axeSource ??= readFileSync(require.resolve('axe-core/axe.min.js'), 'utf8');
|
|
88
|
+
const timeout = options.timeoutMs ?? 30_000;
|
|
89
|
+
const browser = await launch(options.executablePath);
|
|
90
|
+
let server;
|
|
91
|
+
const contexts = [];
|
|
92
|
+
try {
|
|
93
|
+
server = await startStaticServer({
|
|
94
|
+
root: () => options.dir,
|
|
95
|
+
host: '127.0.0.1',
|
|
96
|
+
port: 0,
|
|
97
|
+
trailingSlash: options.trailingSlash,
|
|
98
|
+
});
|
|
99
|
+
const baseUrl = server.url;
|
|
100
|
+
for (const mode of MODES) {
|
|
101
|
+
contexts.push({
|
|
102
|
+
name: mode.name,
|
|
103
|
+
context: await browser.newContext({ ...mode.options, reducedMotion: 'reduce' }),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const findings = [];
|
|
107
|
+
const queue = [...options.pages];
|
|
108
|
+
const worker = async () => {
|
|
109
|
+
for (let page = queue.shift(); page !== undefined; page = queue.shift()) {
|
|
110
|
+
options.logger?.debug(`axe: ${page.url}`);
|
|
111
|
+
findings.push(...(await checkPage(page, contexts, baseUrl, options.wcag, timeout)));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
await Promise.all(Array.from({ length: options.concurrency ?? 4 }, worker));
|
|
115
|
+
return findings;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
try {
|
|
119
|
+
await browser.close();
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await server?.close();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function checkPage(page, contexts, baseUrl, wcag, timeout) {
|
|
127
|
+
const seen = new Map();
|
|
128
|
+
const errors = new Map();
|
|
129
|
+
for (const { name, context } of contexts) {
|
|
130
|
+
const tab = await context.newPage();
|
|
131
|
+
tab.setDefaultTimeout(timeout);
|
|
132
|
+
tab.on('pageerror', (error) => {
|
|
133
|
+
const modes = errors.get(error.message) ?? [];
|
|
134
|
+
errors.set(error.message, [...modes, name]);
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
await tab.goto(baseUrl + page.url, { waitUntil: 'load' });
|
|
138
|
+
await tab.evaluate(axeSource ?? '');
|
|
139
|
+
const results = await tab.evaluate(async (tags) => {
|
|
140
|
+
const { axe, document } = globalThis;
|
|
141
|
+
const known = new Set(axe.getRules().flatMap((rule) => rule.tags));
|
|
142
|
+
return axe.run(document, {
|
|
143
|
+
runOnly: { type: 'tag', values: tags.filter((tag) => known.has(tag)) },
|
|
144
|
+
resultTypes: ['violations'],
|
|
145
|
+
});
|
|
146
|
+
}, TAGS[wcag]);
|
|
147
|
+
collect(results, page, name, seen);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
await tab.close();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const findings = [];
|
|
154
|
+
for (const { finding, modes, help } of seen.values()) {
|
|
155
|
+
const partial = modes.length < contexts.length ? ` (${modes.join(', ')} only)` : '';
|
|
156
|
+
finding.diagnostic.message = help + partial;
|
|
157
|
+
findings.push(finding);
|
|
158
|
+
}
|
|
159
|
+
for (const [message, modes] of errors) {
|
|
160
|
+
const partial = modes.length < contexts.length ? ` (${modes.join(', ')} only)` : '';
|
|
161
|
+
findings.push({
|
|
162
|
+
layer: 'rendered',
|
|
163
|
+
diagnostic: {
|
|
164
|
+
ruleId: 'script-error',
|
|
165
|
+
severity: 'error',
|
|
166
|
+
message: `Uncaught error in page script: ${message}${partial}`,
|
|
167
|
+
...(page.source !== undefined && { file: page.source }),
|
|
168
|
+
url: page.url,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return findings;
|
|
173
|
+
}
|
|
174
|
+
function collect(results, page, mode, seen) {
|
|
175
|
+
for (const violation of results.violations) {
|
|
176
|
+
for (const node of violation.nodes) {
|
|
177
|
+
const selector = node.target
|
|
178
|
+
.map((part) => (Array.isArray(part) ? part.join(' >>> ') : String(part)))
|
|
179
|
+
.join(' ');
|
|
180
|
+
const key = `${violation.id}|${selector}`;
|
|
181
|
+
const existing = seen.get(key);
|
|
182
|
+
if (existing !== undefined) {
|
|
183
|
+
existing.modes.push(mode);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const wcag = wcagOf(violation.tags);
|
|
187
|
+
const hint = summarize(node.failureSummary);
|
|
188
|
+
seen.set(key, {
|
|
189
|
+
modes: [mode],
|
|
190
|
+
help: violation.help,
|
|
191
|
+
finding: {
|
|
192
|
+
layer: 'rendered',
|
|
193
|
+
diagnostic: {
|
|
194
|
+
ruleId: ALIASES[violation.id] ?? `axe/${violation.id}`,
|
|
195
|
+
severity: severityOf(node.impact ?? violation.impact),
|
|
196
|
+
message: violation.help,
|
|
197
|
+
...(page.source !== undefined && { file: page.source }),
|
|
198
|
+
url: page.url,
|
|
199
|
+
selector,
|
|
200
|
+
...(wcag !== undefined && { wcag }),
|
|
201
|
+
...(hint !== undefined && { hint }),
|
|
202
|
+
helpUrl: violation.helpUrl,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=headless.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless.js","sourceRoot":"","sources":["../src/headless.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-validate.d.ts","sourceRoot":"","sources":["../src/html-validate.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAuFrD,kDAAkD;AAClD,wBAAsB,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA6BpF"}
|