@skip-ai/scanner 0.2.0 → 0.5.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/dist/cli.d.ts +16 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +51 -9
- package/dist/cli.js.map +1 -1
- package/dist/index.js +158 -46
- package/dist/index.js.map +1 -1
- package/dist/reporter.d.ts +13 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +5 -2
- package/dist/reporter.js.map +1 -1
- package/dist/scanner/guided-flows.d.ts +23 -0
- package/dist/scanner/guided-flows.d.ts.map +1 -0
- package/dist/scanner/guided-flows.js +61 -0
- package/dist/scanner/guided-flows.js.map +1 -0
- package/dist/scanner/history.d.ts +23 -0
- package/dist/scanner/history.d.ts.map +1 -0
- package/dist/scanner/history.js +38 -0
- package/dist/scanner/history.js.map +1 -0
- package/dist/scanner/score.d.ts +19 -0
- package/dist/scanner/score.d.ts.map +1 -0
- package/dist/scanner/score.js +50 -0
- package/dist/scanner/score.js.map +1 -0
- package/dist/scanner/wcag.d.ts +94 -0
- package/dist/scanner/wcag.d.ts.map +1 -0
- package/dist/scanner/wcag.js +487 -0
- package/dist/scanner/wcag.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export const WCAG_RULES_VERSION = '1.1.0';
|
|
3
|
+
// Índices (início e fim) de cada comentário JSX no conteúdo.
|
|
4
|
+
function findJsxComments(content) {
|
|
5
|
+
const ranges = [];
|
|
6
|
+
const re = /\{\/[\s\S]*?\/\}/g;
|
|
7
|
+
let m;
|
|
8
|
+
while ((m = re.exec(content)) !== null) {
|
|
9
|
+
ranges.push([m.index, m.index + m[0].length]);
|
|
10
|
+
}
|
|
11
|
+
return ranges;
|
|
12
|
+
}
|
|
13
|
+
function findTags(content, tagNames) {
|
|
14
|
+
const out = [];
|
|
15
|
+
const comments = findJsxComments(content);
|
|
16
|
+
const isInComment = (idx) => comments.some(([s, e]) => idx >= s && idx < e);
|
|
17
|
+
const re = new RegExp(`<(${tagNames.join('|')})\\b`, 'gi');
|
|
18
|
+
let m;
|
|
19
|
+
while ((m = re.exec(content)) !== null) {
|
|
20
|
+
const lt = m.index;
|
|
21
|
+
if (isInComment(lt))
|
|
22
|
+
continue; // ignora tags dentro de comentários JSX
|
|
23
|
+
const tag = m[1];
|
|
24
|
+
let i = lt + 1 + tag.length;
|
|
25
|
+
let brace = 0;
|
|
26
|
+
let quote = null;
|
|
27
|
+
while (i < content.length) {
|
|
28
|
+
const ch = content[i];
|
|
29
|
+
if (quote) {
|
|
30
|
+
if (ch === quote)
|
|
31
|
+
quote = null;
|
|
32
|
+
i++;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (ch === '"' || ch === "'") {
|
|
36
|
+
quote = ch;
|
|
37
|
+
i++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (ch === '{') {
|
|
41
|
+
brace++;
|
|
42
|
+
i++;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (ch === '}') {
|
|
46
|
+
brace--;
|
|
47
|
+
i++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (brace === 0 && ch === '>') {
|
|
51
|
+
out.push({
|
|
52
|
+
full: content.substring(lt, i + 1),
|
|
53
|
+
tag,
|
|
54
|
+
attrs: content.substring(lt + 1 + tag.length, i).trim(),
|
|
55
|
+
contentStart: i + 1,
|
|
56
|
+
startIndex: lt,
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** Lê um atributo, aceitando tanto `for` quanto `htmlFor` (React). */
|
|
66
|
+
function attr(attrs, name) {
|
|
67
|
+
const m = attrs.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, 'i'));
|
|
68
|
+
return m ? m[1] : null;
|
|
69
|
+
}
|
|
70
|
+
/** Lê um atributo aceitando variações (for, htmlFor). */
|
|
71
|
+
function attrAny(attrs, names) {
|
|
72
|
+
for (const n of names) {
|
|
73
|
+
const v = attr(attrs, n);
|
|
74
|
+
if (v)
|
|
75
|
+
return v;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
/** Texto visível depois da tag até o próximo '<' (com suporte a span/sr-only). */
|
|
80
|
+
function visibleText(content, from) {
|
|
81
|
+
if (from < 0)
|
|
82
|
+
return '';
|
|
83
|
+
const slice = content.substring(from, from + 200);
|
|
84
|
+
// Texto direto após '>'
|
|
85
|
+
const direct = slice.match(/^\s*([^<{]+)/);
|
|
86
|
+
if (direct && direct[1].trim())
|
|
87
|
+
return direct[1].trim().replace(/\s+/g, ' ').slice(0, 80);
|
|
88
|
+
// Texto dentro de <span> filho imediato (inclui sr-only)
|
|
89
|
+
const span = slice.match(/^\s*<span[^>]*>([^<]+)/i);
|
|
90
|
+
if (span && span[1].trim())
|
|
91
|
+
return span[1].trim().slice(0, 80);
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
/** Calcula linha/coluna (1-based) a partir de um índice no conteúdo. */
|
|
95
|
+
function lineCol(content, index) {
|
|
96
|
+
let line = 1;
|
|
97
|
+
let col = 1;
|
|
98
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
99
|
+
if (content[i] === '\n') {
|
|
100
|
+
line++;
|
|
101
|
+
col = 1;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
col++;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { line, column: col };
|
|
108
|
+
}
|
|
109
|
+
/** Normaliza um seletor pra fingerprint (remove variações irrelevantes). */
|
|
110
|
+
function normalizeSelector(selector) {
|
|
111
|
+
return selector.replace(/['"`]/g, '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
112
|
+
}
|
|
113
|
+
/** Calcula fingerprint estável de uma violação. */
|
|
114
|
+
function makeFingerprint(ruleId, filePath, selector) {
|
|
115
|
+
const norm = normalizeSelector(selector);
|
|
116
|
+
const filePathNorm = filePath.replace(/\\/g, '/').toLowerCase();
|
|
117
|
+
return createHash('sha256').update(`${ruleId}|${filePathNorm}|${norm}`).digest('hex').slice(0, 16);
|
|
118
|
+
}
|
|
119
|
+
function buildSelectorFromTag(tag, attrs, inputName) {
|
|
120
|
+
const id = attr(attrs, 'id');
|
|
121
|
+
if (id)
|
|
122
|
+
return `#${id}`;
|
|
123
|
+
const anchor = attr(attrs, 'data-skip-anchor');
|
|
124
|
+
if (anchor)
|
|
125
|
+
return `[data-skip-anchor='${anchor}']`;
|
|
126
|
+
if (inputName)
|
|
127
|
+
return `${tag}[name='${inputName}']`;
|
|
128
|
+
const aria = attrAny(attrs, ['aria-label', 'aria-labelledby']);
|
|
129
|
+
if (aria)
|
|
130
|
+
return `${tag}[aria-label='${aria}']`;
|
|
131
|
+
return tag;
|
|
132
|
+
}
|
|
133
|
+
// ---- Helpers de construção de violação ----
|
|
134
|
+
function buildViolation(ctx, rule, tag, selector, severityOverride) {
|
|
135
|
+
const { line, column } = tag ? lineCol(ctx.content, tag.startIndex) : { line: 1, column: 1 };
|
|
136
|
+
const filePath = ctx.screen.filePath;
|
|
137
|
+
return {
|
|
138
|
+
fingerprint: makeFingerprint(rule.id, filePath, selector),
|
|
139
|
+
id: `${rule.id}-${ctx.screen.id}`,
|
|
140
|
+
ruleId: rule.id,
|
|
141
|
+
rule: rule.rule,
|
|
142
|
+
level: rule.level,
|
|
143
|
+
severity: severityOverride ?? rule.severity,
|
|
144
|
+
impact: rule.impact,
|
|
145
|
+
title: rule.title,
|
|
146
|
+
description: `${rule.title} em ${ctx.screen.route} (${filePath}).`,
|
|
147
|
+
screen: ctx.screen.route,
|
|
148
|
+
filePath,
|
|
149
|
+
selector,
|
|
150
|
+
fix: rule.defaultFix,
|
|
151
|
+
wcagUrl: rule.wcagUrl,
|
|
152
|
+
source: { filePath, line, column },
|
|
153
|
+
affectedScreens: [ctx.screen.route],
|
|
154
|
+
occurrenceCount: 1,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const VALID_ROLES = new Set([
|
|
158
|
+
'alert', 'alertdialog', 'banner', 'button', 'checkbox', 'complementary',
|
|
159
|
+
'contentinfo', 'dialog', 'document', 'form', 'grid', 'gridcell', 'heading',
|
|
160
|
+
'img', 'link', 'list', 'listbox', 'listitem', 'main', 'menu', 'menubar',
|
|
161
|
+
'menuitem', 'navigation', 'none', 'note', 'option', 'presentation', 'progressbar',
|
|
162
|
+
'radio', 'radiogroup', 'region', 'row', 'rowgroup', 'rowheader', 'scrollbar',
|
|
163
|
+
'search', 'separator', 'slider', 'spinbutton', 'status', 'tab', 'tablist',
|
|
164
|
+
'tabpanel', 'textbox', 'timer', 'toolbar', 'tooltip', 'tree', 'treegrid', 'treeitem',
|
|
165
|
+
]);
|
|
166
|
+
// ---- Definição das regras WCAG ----
|
|
167
|
+
export const RULES = [
|
|
168
|
+
// 1.1.1 — Imagem sem alt
|
|
169
|
+
{
|
|
170
|
+
id: 'img-alt-missing',
|
|
171
|
+
rule: '1.1.1 Non-text Content',
|
|
172
|
+
level: 'A',
|
|
173
|
+
severity: 'critical',
|
|
174
|
+
impact: 'Leitores de tela não conseguem descrever a imagem para usuários cegos.',
|
|
175
|
+
title: 'Imagem sem texto alternativo',
|
|
176
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#non-text-content',
|
|
177
|
+
defaultFix: "Adicione alt='...' descrevendo a imagem, ou alt='' se for puramente decorativa.",
|
|
178
|
+
check: (ctx, rule) => {
|
|
179
|
+
const violations = [];
|
|
180
|
+
const imgs = findTags(ctx.content, ['img']);
|
|
181
|
+
for (const t of imgs) {
|
|
182
|
+
if (!/\balt\s*=/.test(t.attrs)) {
|
|
183
|
+
violations.push(buildViolation(ctx, rule, t, buildSelectorFromTag('img', t.attrs, null)));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { violations, elementsChecked: imgs.length };
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
// 4.1.2 — Button sem nome acessível (texto, aria-label, span, sr-only)
|
|
190
|
+
{
|
|
191
|
+
id: 'button-no-name',
|
|
192
|
+
rule: '4.1.2 Name, Role, Value',
|
|
193
|
+
level: 'A',
|
|
194
|
+
severity: 'serious',
|
|
195
|
+
impact: 'Usuários de leitor de tela ouvem apenas "botão" sem saber sua função.',
|
|
196
|
+
title: 'Botão sem nome acessível',
|
|
197
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#name-role-value',
|
|
198
|
+
defaultFix: "Adicione texto visível, aria-label='...', aria-labelledby='...' ou um <span> com texto.",
|
|
199
|
+
check: (ctx, rule) => {
|
|
200
|
+
const violations = [];
|
|
201
|
+
const btns = findTags(ctx.content, ['button']);
|
|
202
|
+
for (const t of btns) {
|
|
203
|
+
const aria = attrAny(t.attrs, ['aria-label', 'aria-labelledby']);
|
|
204
|
+
const text = visibleText(ctx.content, t.contentStart);
|
|
205
|
+
if (!aria && !text) {
|
|
206
|
+
violations.push(buildViolation(ctx, rule, t, buildSelectorFromTag('button', t.attrs, null)));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return { violations, elementsChecked: btns.length };
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
// 1.3.1 / 3.3.2 — Input sem label (aceita for, htmlFor, aria-label, label envolvente)
|
|
213
|
+
{
|
|
214
|
+
id: 'input-no-label',
|
|
215
|
+
rule: '1.3.1 Info and Relationships / 3.3.2 Labels or Instructions',
|
|
216
|
+
level: 'A',
|
|
217
|
+
severity: 'serious',
|
|
218
|
+
impact: 'Usuários não sabem o que preencher; leitores de tela anunciam "campo de texto" sem contexto.',
|
|
219
|
+
title: 'Campo de formulário sem label associada',
|
|
220
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#labels-or-instructions',
|
|
221
|
+
defaultFix: "Associe <label htmlFor='id'> (ou <label for='id'>), use <label> envolvente, ou aria-label='...'.",
|
|
222
|
+
check: (ctx, rule) => {
|
|
223
|
+
const violations = [];
|
|
224
|
+
const inputs = findTags(ctx.content, ['input', 'textarea', 'select']);
|
|
225
|
+
// Coleta IDs referenciados por <label for> OU <label htmlFor>
|
|
226
|
+
const labelFors = new Set();
|
|
227
|
+
const labelRe = /<label\b[^>]*?\b(?:for|htmlFor)\s*=\s*["']([^"']+)["']/gi;
|
|
228
|
+
let lm;
|
|
229
|
+
while ((lm = labelRe.exec(ctx.content)) !== null)
|
|
230
|
+
labelFors.add(lm[1]);
|
|
231
|
+
for (const t of inputs) {
|
|
232
|
+
if (t.attrs.includes('type="hidden"') || t.attrs.includes("type='hidden'"))
|
|
233
|
+
continue;
|
|
234
|
+
const id = attr(t.attrs, 'id');
|
|
235
|
+
const aria = attrAny(t.attrs, ['aria-label', 'aria-labelledby']);
|
|
236
|
+
const labelledByFor = id && labelFors.has(id);
|
|
237
|
+
// Detecta <label> envolvente: procura <label ...> antes do input sem </label> entre eles
|
|
238
|
+
const labelWrap = isInsideLabel(ctx.content, t.startIndex);
|
|
239
|
+
if (!labelledByFor && !aria && !labelWrap) {
|
|
240
|
+
const name = attr(t.attrs, 'name');
|
|
241
|
+
violations.push(buildViolation(ctx, rule, t, buildSelectorFromTag(t.tag, t.attrs, name)));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { violations, elementsChecked: inputs.length };
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
// 2.4.4 — Link sem destino válido (reconhece <a href> E <Link to> do React Router)
|
|
248
|
+
{
|
|
249
|
+
id: 'link-href-empty',
|
|
250
|
+
rule: '2.4.4 Link Purpose (In Context)',
|
|
251
|
+
level: 'A',
|
|
252
|
+
severity: 'moderate',
|
|
253
|
+
impact: "Links com href='#' ou vazio não levam a lugar algum, confundindo navegação.",
|
|
254
|
+
title: 'Link sem destino válido',
|
|
255
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#link-purpose-in-context',
|
|
256
|
+
defaultFix: "Use um href/to válido apontando para a rota correta, ou transforme em <button> se for uma ação.",
|
|
257
|
+
check: (ctx, rule) => {
|
|
258
|
+
const violations = [];
|
|
259
|
+
const anchors = findTags(ctx.content, ['a', 'Link']);
|
|
260
|
+
for (const t of anchors) {
|
|
261
|
+
const href = attrAny(t.attrs, ['href', 'to']);
|
|
262
|
+
// Link sem href E sem to = problema. href="#" ou "" = problema.
|
|
263
|
+
if (href === '#' || href === '' || href == null) {
|
|
264
|
+
const domTag = t.tag === 'Link' ? 'a' : t.tag;
|
|
265
|
+
violations.push(buildViolation(ctx, rule, t, buildSelectorFromTag(domTag, t.attrs, null)));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return { violations, elementsChecked: anchors.length };
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
// 2.4.3 — tabindex positivo
|
|
272
|
+
{
|
|
273
|
+
id: 'tabindex-positive',
|
|
274
|
+
rule: '2.4.3 Focus Order',
|
|
275
|
+
level: 'A',
|
|
276
|
+
severity: 'moderate',
|
|
277
|
+
impact: 'tabindex positivo quebra a ordem natural de navegação por teclado.',
|
|
278
|
+
title: 'tabindex positivo quebra ordem de foco',
|
|
279
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#focus-order',
|
|
280
|
+
defaultFix: "Remova o tabindex positivo; use ordem natural do DOM. Use tabindex='0' se precisar tornar focável.",
|
|
281
|
+
check: (ctx, rule) => {
|
|
282
|
+
const violations = [];
|
|
283
|
+
const re = /<(\w+)\b([^>]*?)\btabindex\s*=\s*["']([1-9]\d*)["']/gi;
|
|
284
|
+
let count = 0;
|
|
285
|
+
let m;
|
|
286
|
+
while ((m = re.exec(ctx.content)) !== null) {
|
|
287
|
+
count++;
|
|
288
|
+
const sel = `${m[1]}[tabindex='${m[3]}']`;
|
|
289
|
+
violations.push(buildViolation(ctx, rule, null, sel));
|
|
290
|
+
}
|
|
291
|
+
return { violations, elementsChecked: count };
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
// 1.3.1 — ordem de headings
|
|
295
|
+
{
|
|
296
|
+
id: 'heading-order',
|
|
297
|
+
rule: '1.3.1 Info and Relationships',
|
|
298
|
+
level: 'A',
|
|
299
|
+
severity: 'moderate',
|
|
300
|
+
impact: 'Pular níveis de heading (ex.: h1→h3) confunde a estrutura do documento para leitores de tela.',
|
|
301
|
+
title: 'Ordem de headings inconsistente',
|
|
302
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#info-and-relationships',
|
|
303
|
+
defaultFix: 'Não pule níveis: após h1 use h2, após h2 use h3, etc.',
|
|
304
|
+
check: (ctx, rule) => {
|
|
305
|
+
const violations = [];
|
|
306
|
+
const re = /<h([1-6])\b/gi;
|
|
307
|
+
let prev = 0;
|
|
308
|
+
let count = 0;
|
|
309
|
+
let m;
|
|
310
|
+
while ((m = re.exec(ctx.content)) !== null) {
|
|
311
|
+
count++;
|
|
312
|
+
const lvl = parseInt(m[1], 10);
|
|
313
|
+
if (prev !== 0 && lvl > prev + 1) {
|
|
314
|
+
violations.push(buildViolation(ctx, rule, null, `h${lvl}`));
|
|
315
|
+
}
|
|
316
|
+
prev = lvl;
|
|
317
|
+
}
|
|
318
|
+
return { violations, elementsChecked: count };
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
// 4.1.2 — role inválida
|
|
322
|
+
{
|
|
323
|
+
id: 'aria-invalid-role',
|
|
324
|
+
rule: '4.1.2 Name, Role, Value',
|
|
325
|
+
level: 'A',
|
|
326
|
+
severity: 'serious',
|
|
327
|
+
impact: 'role inválida faz leitores de tela anunciarem o elemento incorretamente.',
|
|
328
|
+
title: 'atributo role inválido',
|
|
329
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#name-role-value',
|
|
330
|
+
defaultFix: 'Use um valor de role válido da especificação ARIA, ou remova o atributo.',
|
|
331
|
+
check: (ctx, rule) => {
|
|
332
|
+
const violations = [];
|
|
333
|
+
const re = /<(\w+)\b([^>]*?)\brole\s*=\s*["']([^"']+)["']/gi;
|
|
334
|
+
let count = 0;
|
|
335
|
+
let m;
|
|
336
|
+
while ((m = re.exec(ctx.content)) !== null) {
|
|
337
|
+
count++;
|
|
338
|
+
if (!VALID_ROLES.has(m[3])) {
|
|
339
|
+
violations.push(buildViolation(ctx, rule, null, `${m[1]}[role='${m[3]}']`));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return { violations, elementsChecked: count };
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
// 3.1.1 — html sem lang (checado UMA vez globalmente, não por tela)
|
|
346
|
+
{
|
|
347
|
+
id: 'lang-missing',
|
|
348
|
+
rule: '3.1.1 Language of Page',
|
|
349
|
+
level: 'A',
|
|
350
|
+
severity: 'serious',
|
|
351
|
+
impact: 'Sem lang no <html>, leitores de tela usam idioma errado pra pronunciar.',
|
|
352
|
+
title: 'Documento sem idioma declarado',
|
|
353
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#language-of-page',
|
|
354
|
+
defaultFix: "Adicione lang='pt-BR' (ou o idioma correto) na tag <html>.",
|
|
355
|
+
check: (ctx, rule) => {
|
|
356
|
+
// Só dispara na tela raiz "/" — evita repetir em cada rota de SPA.
|
|
357
|
+
if (ctx.screen.route !== '/')
|
|
358
|
+
return { violations: [], elementsChecked: 0 };
|
|
359
|
+
const htmlMatch = ctx.content.match(/<html\b([^>]*)>/i);
|
|
360
|
+
if (htmlMatch && !/\blang\s*=/.test(htmlMatch[1])) {
|
|
361
|
+
return { violations: [buildViolation(ctx, rule, null, 'html')], elementsChecked: 1 };
|
|
362
|
+
}
|
|
363
|
+
return { violations: [], elementsChecked: 1 };
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
// 2.4.1 — sem skip link
|
|
367
|
+
{
|
|
368
|
+
id: 'skip-link-missing',
|
|
369
|
+
rule: '2.4.1 Bypass Blocks',
|
|
370
|
+
level: 'A',
|
|
371
|
+
severity: 'moderate',
|
|
372
|
+
impact: 'Usuários de teclado precisam tabular por toda a navegação repetitiva a cada página.',
|
|
373
|
+
title: 'Sem link de pular para o conteúdo',
|
|
374
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#bypass-blocks',
|
|
375
|
+
defaultFix: "Adicione <a href='#main' className='skip-link'>Pular para o conteúdo</a> como primeiro elemento focável.",
|
|
376
|
+
check: (ctx, rule) => {
|
|
377
|
+
const hasSkip = /href\s*=\s*["']#(?:main|content)["']/i.test(ctx.content);
|
|
378
|
+
const hasMain = /id\s*=\s*["']main["']/i.test(ctx.content) || /<main\b/i.test(ctx.content);
|
|
379
|
+
if (!hasSkip && hasMain) {
|
|
380
|
+
return { violations: [buildViolation(ctx, rule, null, 'body')], elementsChecked: 1 };
|
|
381
|
+
}
|
|
382
|
+
return { violations: [], elementsChecked: 1 };
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
// 1.4.3 — contraste (heurística estática → unknown / needs-review)
|
|
386
|
+
{
|
|
387
|
+
id: 'low-contrast-hint',
|
|
388
|
+
rule: '1.4.3 Contrast (Minimum)',
|
|
389
|
+
level: 'AA',
|
|
390
|
+
severity: 'unknown', // heurística sem cálculo real → não confirma falha
|
|
391
|
+
impact: 'Possível baixo contraste texto/fundo dificulta leitura para usuários com baixa visão.',
|
|
392
|
+
title: 'Possível baixo contraste de cor',
|
|
393
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#contrast-minimum',
|
|
394
|
+
defaultFix: 'Verifique a razão de contraste (mínimo 4.5:1 para texto normal). Evite tons muito claros em fundos brancos.',
|
|
395
|
+
check: (ctx, rule) => {
|
|
396
|
+
const violations = [];
|
|
397
|
+
const suspect = /(text-(?:gray|slate|zinc|neutral)-(?:200|300))|(text-white\/[1-4]\d)|(opacity-(?:[1-3]\d|40))/gi;
|
|
398
|
+
let count = 0;
|
|
399
|
+
let m;
|
|
400
|
+
while ((m = suspect.exec(ctx.content)) !== null) {
|
|
401
|
+
count++;
|
|
402
|
+
violations.push(buildViolation(ctx, rule, null, m[0]));
|
|
403
|
+
}
|
|
404
|
+
return { violations, elementsChecked: count };
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
// 3.3.2 — form sem instrução
|
|
408
|
+
{
|
|
409
|
+
id: 'form-no-instruction',
|
|
410
|
+
rule: '3.3.2 Labels or Instructions',
|
|
411
|
+
level: 'A',
|
|
412
|
+
severity: 'moderate',
|
|
413
|
+
impact: 'Formulários sem instruções aumentam chance de erro do usuário.',
|
|
414
|
+
title: 'Formulário sem instrução de preenchimento',
|
|
415
|
+
wcagUrl: 'https://www.w3.org/WAI/WCAG22/quickref/#labels-or-instructions',
|
|
416
|
+
defaultFix: 'Adicione uma instrução curta antes do formulário, ou aria-describedby nos campos.',
|
|
417
|
+
check: (ctx, rule) => {
|
|
418
|
+
const forms = findTags(ctx.content, ['form']);
|
|
419
|
+
if (forms.length === 0)
|
|
420
|
+
return { violations: [], elementsChecked: 0 };
|
|
421
|
+
const hasInstruction = /(por favor|preencha|informe|digite|required|obrigat)/i.test(ctx.content);
|
|
422
|
+
if (!hasInstruction) {
|
|
423
|
+
return { violations: [buildViolation(ctx, rule, null, 'form')], elementsChecked: forms.length };
|
|
424
|
+
}
|
|
425
|
+
return { violations: [], elementsChecked: forms.length };
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
];
|
|
429
|
+
/** Verifica se um índice está dentro de um <label>...</label> (label envolvente). */
|
|
430
|
+
function isInsideLabel(content, index) {
|
|
431
|
+
// Procura o último <label ...> aberto antes do índice, sem </label> entre eles.
|
|
432
|
+
const before = content.substring(0, index);
|
|
433
|
+
const lastOpen = before.lastIndexOf('<label');
|
|
434
|
+
if (lastOpen === -1)
|
|
435
|
+
return false;
|
|
436
|
+
const segment = content.substring(lastOpen, index);
|
|
437
|
+
// Se há </label> entre a abertura e o índice, o input não está dentro.
|
|
438
|
+
return !segment.includes('</label>');
|
|
439
|
+
}
|
|
440
|
+
// ---- Orquestrador da auditoria ----
|
|
441
|
+
/**
|
|
442
|
+
* Aplica todas as regras WCAG a um conjunto de telas.
|
|
443
|
+
* Os conteúdos dos arquivos são lidos localmente e NÃO são enviados ao servidor.
|
|
444
|
+
* Realiza deduplicação por fingerprint: o mesmo problema em N telas vira 1 violação
|
|
445
|
+
* com occurrenceCount = N e affectedScreens = [...].
|
|
446
|
+
*/
|
|
447
|
+
export function auditWcag(screens, contentsByFile) {
|
|
448
|
+
const raw = [];
|
|
449
|
+
const checks = { total: 0, passed: 0, failed: 0, needsReview: 0 };
|
|
450
|
+
for (const screen of screens) {
|
|
451
|
+
const content = contentsByFile[screen.filePath] ?? '';
|
|
452
|
+
const ctx = { screen, content };
|
|
453
|
+
for (const rule of RULES) {
|
|
454
|
+
const result = rule.check(ctx, rule);
|
|
455
|
+
checks.total += result.elementsChecked;
|
|
456
|
+
checks.failed += result.violations.filter((v) => v.severity !== 'unknown').length;
|
|
457
|
+
checks.needsReview += result.violations.filter((v) => v.severity === 'unknown').length;
|
|
458
|
+
for (const v of result.violations)
|
|
459
|
+
raw.push(v);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// Deduplicação por fingerprint
|
|
463
|
+
const byFingerprint = new Map();
|
|
464
|
+
for (const v of raw) {
|
|
465
|
+
const existing = byFingerprint.get(v.fingerprint);
|
|
466
|
+
if (existing) {
|
|
467
|
+
existing.occurrenceCount += 1;
|
|
468
|
+
if (!existing.affectedScreens.includes(v.screen)) {
|
|
469
|
+
existing.affectedScreens.push(v.screen);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
byFingerprint.set(v.fingerprint, { ...v });
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const violations = [...byFingerprint.values()];
|
|
477
|
+
checks.passed = Math.max(0, checks.total - checks.failed - checks.needsReview);
|
|
478
|
+
return { violations, checks };
|
|
479
|
+
}
|
|
480
|
+
/** Conta violações por severidade (após deduplicação). */
|
|
481
|
+
export function summarize(violations) {
|
|
482
|
+
const s = { critical: 0, serious: 0, moderate: 0, minor: 0, unknown: 0 };
|
|
483
|
+
for (const v of violations)
|
|
484
|
+
s[v.severity]++;
|
|
485
|
+
return s;
|
|
486
|
+
}
|
|
487
|
+
//# sourceMappingURL=wcag.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wcag.js","sourceRoot":"","sources":["../../src/scanner/wcag.ts"],"names":[],"mappings":"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"}
|