equall-cli 0.1.2 → 0.1.3

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 CHANGED
@@ -11,17 +11,26 @@ equall scan .
11
11
  ```
12
12
  ◆ EQUALL — Accessibility Score
13
13
 
14
- 58 ~A WCAG 2.2
14
+ 56 ~A WCAG 2.2
15
15
 
16
16
  POUR Breakdown
17
- P Perceivable ███████████████████░ 97
18
- O Operable █████████░░░░░░░░░░░ 46
17
+ P Perceivable ██████████████████░░ 89
18
+ O Operable ███████████████░░░░░ 76
19
19
  U Understandable ░░░░░░░░░░░░░░░░░░░░ n/a
20
- R Robust ██████████████████░░ 91
20
+ R Robust █████████████████░░░ 85
21
21
 
22
22
  Summary
23
- 33 files scanned · 18 WCAG violations · 5 best-practice issues
24
- 2 critical 2 serious 19 moderate 0 minor
23
+ 33 files scanned · 15 WCAG violations · 19 best-practice issues
24
+ 2 critical 11 serious 19 moderate 0 minor
25
+ 2 issues ignored via equall-ignore
26
+ Score 56/100 · 18/30 Level A criteria checked (60%) · 5 failed
27
+ 29/57 Level AA criteria checked (51%) · 6 failed
28
+
29
+ ⓘ You're failing 5 Level A criteria (1.3.1, 2.1.1, 2.4.2, 2.4.4, 4.1.2).
30
+ Level A is the legal minimum — fix these first.
31
+
32
+ Scanners: axe-core@4.11.1 (23 issues), eslint-jsx-a11y@6.10.2 (13 issues)
33
+ Completed in 0.8s
25
34
  ```
26
35
 
27
36
  ## Install
@@ -102,6 +111,40 @@ The POUR metrics (Perceivable, Operable, Understandable, Robust) strictly follow
102
111
  ### Conformance Level
103
112
  Conformance (A / AA / AAA) is evaluated strictly against your `--level` target. If you target `AA`, any `AAA` rules incidentally flagged by the scanners will not downgrade your conformance status.
104
113
 
114
+ ## Ignoring issues
115
+
116
+ Some issues are false positives (e.g. an orphan `<li>` in a component that's always rendered inside a `<ul>`). Suppress them with inline comments:
117
+
118
+ ```tsx
119
+ // equall-ignore-next-line
120
+ <li>{item.name}</li>
121
+
122
+ // equall-ignore-next-line jsx-a11y/alt-text
123
+ <img src={logo} />
124
+
125
+ {/* equall-ignore-next-line */}
126
+ <div onClick={handler}>...</div>
127
+ ```
128
+
129
+ ```html
130
+ <!-- equall-ignore-next-line -->
131
+ <img src="decorative.png" />
132
+ ```
133
+
134
+ Add `// equall-ignore-file` in the first 5 lines to ignore an entire file.
135
+
136
+ Or use the CLI to inject/manage comments without opening the file:
137
+
138
+ ```bash
139
+ equall ignore src/Modal.tsx:89 # ignore all rules at line 89
140
+ equall ignore src/Modal.tsx:89 jsx-a11y/alt-text # ignore a specific rule
141
+ equall ignore . # list all ignores
142
+ equall ignore --remove src/Modal.tsx:89 # remove an ignore
143
+ equall ignore --clear # remove all ignores
144
+ ```
145
+
146
+ Ignored issues are excluded from the score. Use `equall scan . -i` to show them, or `--json` to get them with `"ignored": true`.
147
+
105
148
  ## Programmatic API
106
149
 
107
150
  ```typescript
@@ -124,6 +167,10 @@ console.log(result.issues.length) // 12
124
167
  - `1` — score < 50 (useful for CI gates)
125
168
  - `2` — scan error
126
169
 
170
+ ## Contributing
171
+
172
+ Issues and PRs welcome — https://github.com/GotaBird/equall/issues
173
+
127
174
  ## License
128
175
 
129
176
  MIT
@@ -39268,7 +39268,8 @@ function computeSummary(issues, filesScanned) {
39268
39268
  by_severity: bySeverity,
39269
39269
  by_scanner: byScanner,
39270
39270
  criteria_tested: [...criteriaSet].sort(),
39271
- criteria_failed: [...failedCriteriaSet].sort()
39271
+ criteria_failed: [...failedCriteriaSet].sort(),
39272
+ ignored_count: 0
39272
39273
  };
39273
39274
  }
39274
39275
  function computeScore(issues, filesScanned) {
@@ -40373,9 +40374,9 @@ async function runScan(options = {}) {
40373
40374
  );
40374
40375
  const allIssues = [];
40375
40376
  const scannersUsed = [];
40376
- for (const result of scannerResults) {
40377
- if (result.status === "fulfilled") {
40378
- const { scanner, issues } = result.value;
40377
+ for (const result2 of scannerResults) {
40378
+ if (result2.status === "fulfilled") {
40379
+ const { scanner, issues } = result2.value;
40379
40380
  allIssues.push(...issues);
40380
40381
  scannersUsed.push({
40381
40382
  name: scanner.name,
@@ -40385,16 +40386,63 @@ async function runScan(options = {}) {
40385
40386
  issues_found: issues.length
40386
40387
  });
40387
40388
  } else {
40388
- const err = result.reason instanceof Error ? result.reason.message : String(result.reason);
40389
+ const err = result2.reason instanceof Error ? result2.reason.message : String(result2.reason);
40389
40390
  console.warn(` [scanner] Failed: ${err.slice(0, 120)}`);
40390
40391
  }
40391
40392
  }
40392
40393
  const deduped = deduplicateIssues(allIssues);
40394
+ const { active, ignored } = applyIgnoreComments(deduped, files);
40393
40395
  const criteriaCovered = [...new Set(scanners.flatMap((s) => s.coveredCriteria))].sort();
40394
40396
  const WCAG_TOTAL = { A: 30, AA: 57, AAA: 78 };
40395
40397
  const criteriaTotal = WCAG_TOTAL[scanOptions.wcag_level] ?? 57;
40396
40398
  const durationMs = Date.now() - startTime;
40397
- return computeScanResult(deduped, files.length, scannersUsed, durationMs, scanOptions.wcag_level, criteriaCovered, criteriaTotal);
40399
+ const result = computeScanResult(active, files.length, scannersUsed, durationMs, scanOptions.wcag_level, criteriaCovered, criteriaTotal);
40400
+ result.issues = [...active, ...ignored];
40401
+ result.summary.ignored_count = ignored.length;
40402
+ return result;
40403
+ }
40404
+ function applyIgnoreComments(issues, files) {
40405
+ const fileContentMap = /* @__PURE__ */ new Map();
40406
+ for (const file of files) {
40407
+ const lines = file.content.split("\n");
40408
+ fileContentMap.set(file.path, lines);
40409
+ }
40410
+ const ignoredFiles = /* @__PURE__ */ new Set();
40411
+ for (const [filePath, lines] of fileContentMap) {
40412
+ const header = lines.slice(0, 5);
40413
+ if (header.some((line) => line.includes("equall-ignore-file"))) {
40414
+ ignoredFiles.add(filePath);
40415
+ }
40416
+ }
40417
+ const active = [];
40418
+ const ignored = [];
40419
+ for (const issue of issues) {
40420
+ if (ignoredFiles.has(issue.file_path)) {
40421
+ ignored.push({ ...issue, ignored: true });
40422
+ continue;
40423
+ }
40424
+ if (issue.line != null && issue.line > 1) {
40425
+ const lines = fileContentMap.get(issue.file_path);
40426
+ if (lines) {
40427
+ const prevLine = lines[issue.line - 2] ?? "";
40428
+ if (prevLine.includes("equall-ignore-next-line")) {
40429
+ const stripped = prevLine.replace(/\s*(?:-->|\*\/\}?)\s*$/g, "");
40430
+ const match = stripped.match(/equall-ignore-next-line\s+([\w\-/.]+)/);
40431
+ if (match) {
40432
+ if (match[1] === issue.scanner_rule_id) {
40433
+ ignored.push({ ...issue, ignored: true });
40434
+ continue;
40435
+ }
40436
+ } else {
40437
+ ignored.push({ ...issue, ignored: true });
40438
+ continue;
40439
+ }
40440
+ }
40441
+ }
40442
+ }
40443
+ active.push(issue);
40444
+ }
40445
+ return { active, ignored };
40398
40446
  }
40399
40447
  function deduplicateIssues(issues) {
40400
40448
  const seen = /* @__PURE__ */ new Set();
@@ -40416,6 +40464,7 @@ export {
40416
40464
  __commonJS,
40417
40465
  __export,
40418
40466
  __toESM,
40467
+ globby,
40419
40468
  computeScanResult,
40420
40469
  runScan
40421
40470
  };
package/dist/cli.js CHANGED
@@ -5,8 +5,9 @@ import {
5
5
  __export,
6
6
  __require,
7
7
  __toESM,
8
+ globby,
8
9
  runScan
9
- } from "./chunk-JMFHJMBB.js";
10
+ } from "./chunk-5YPLWCOT.js";
10
11
 
11
12
  // node_modules/commander/lib/error.js
12
13
  var require_error = __commonJS({
@@ -3009,7 +3010,7 @@ var require_commander = __commonJS({
3009
3010
  });
3010
3011
 
3011
3012
  // src/cli.ts
3012
- import { resolve, basename } from "path";
3013
+ import { resolve as resolve2, basename } from "path";
3013
3014
 
3014
3015
  // node_modules/commander/esm.mjs
3015
3016
  var import_index = __toESM(require_commander(), 1);
@@ -6512,7 +6513,7 @@ function bar(value, width = 20) {
6512
6513
  const color = scoreColor(value);
6513
6514
  return `${color}${"\u2588".repeat(filled)}${DIM}${"\u2591".repeat(empty)}${RESET} ${color}${value}${RESET}`;
6514
6515
  }
6515
- function printResult(result) {
6516
+ function printResult(result, options = {}) {
6516
6517
  const { score, conformance_level, pour_scores, summary, scanners_used, duration_ms } = result;
6517
6518
  console.log();
6518
6519
  console.log(`${BOLD} \u25C6 EQUALL \u2014 Accessibility Score${RESET}`);
@@ -6531,6 +6532,9 @@ function printResult(result) {
6531
6532
  const bpIssuesCount = result.issues.length - wcagIssuesCount;
6532
6533
  console.log(` ${summary.files_scanned} files scanned \xB7 ${wcagIssuesCount} WCAG violations \xB7 ${bpIssuesCount} best-practice issues`);
6533
6534
  console.log(` ${RED}${summary.by_severity.critical} critical${RESET} ${YELLOW}${summary.by_severity.serious} serious${RESET} ${CYAN}${summary.by_severity.moderate} moderate${RESET} ${DIM}${summary.by_severity.minor} minor${RESET}`);
6535
+ if (summary.ignored_count > 0) {
6536
+ console.log(` ${DIM}${summary.ignored_count} issue${summary.ignored_count > 1 ? "s" : ""} ignored via equall-ignore${RESET}`);
6537
+ }
6534
6538
  if (result.criteria_total > 0) {
6535
6539
  const covered = result.criteria_covered.length;
6536
6540
  const total = result.criteria_total;
@@ -6559,8 +6563,9 @@ function printResult(result) {
6559
6563
  console.log();
6560
6564
  printCoaching(result);
6561
6565
  console.log();
6562
- const wcagIssues = result.issues.filter((i) => i.wcag_criteria.length > 0);
6563
- const bpIssues = result.issues.filter((i) => i.wcag_criteria.length === 0);
6566
+ const visibleIssues = result.issues.filter((i) => !i.ignored);
6567
+ const wcagIssues = visibleIssues.filter((i) => i.wcag_criteria.length > 0);
6568
+ const bpIssues = visibleIssues.filter((i) => i.wcag_criteria.length === 0);
6564
6569
  if (wcagIssues.length > 0) {
6565
6570
  console.log(` ${BOLD}WCAG Violations${RESET}`);
6566
6571
  const grouped = groupByCriterion(wcagIssues);
@@ -6597,6 +6602,17 @@ function printResult(result) {
6597
6602
  }
6598
6603
  console.log();
6599
6604
  }
6605
+ if (options.showIgnored) {
6606
+ const ignoredIssues = result.issues.filter((i) => i.ignored);
6607
+ if (ignoredIssues.length > 0) {
6608
+ console.log(` ${BOLD}Ignored${RESET}`);
6609
+ for (const issue of ignoredIssues) {
6610
+ const location = issue.line ? `:${issue.line}` : "";
6611
+ console.log(` ${DIM}\u2298${RESET} ${DIM}${issue.file_path}${location}${RESET} ${issue.scanner_rule_id}`);
6612
+ }
6613
+ console.log();
6614
+ }
6615
+ }
6600
6616
  console.log(` ${DIM}Scanners: ${scanners_used.map((s) => `${s.name}@${s.version} (${s.issues_found} issues)`).join(", ")}${RESET}`);
6601
6617
  console.log(` ${DIM}Completed in ${(duration_ms / 1e3).toFixed(1)}s${RESET}`);
6602
6618
  console.log();
@@ -6660,16 +6676,148 @@ function printJson(result) {
6660
6676
  console.log(JSON.stringify(result, null, 2));
6661
6677
  }
6662
6678
 
6679
+ // src/ignores.ts
6680
+ import { readFile, writeFile } from "fs/promises";
6681
+ import { resolve } from "path";
6682
+ var IGNORE_PATTERN = /equall-ignore-(next-line|file)(?:\s+(\S+))?/;
6683
+ async function findIgnores(rootPath) {
6684
+ const paths = await globby(
6685
+ ["**/*.{html,htm,jsx,tsx,vue,svelte,astro}"],
6686
+ {
6687
+ cwd: rootPath,
6688
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.next/**"],
6689
+ absolute: false,
6690
+ gitignore: true
6691
+ }
6692
+ );
6693
+ const entries = [];
6694
+ for (const relativePath of paths) {
6695
+ const absolutePath = resolve(rootPath, relativePath);
6696
+ let content;
6697
+ try {
6698
+ content = await readFile(absolutePath, "utf-8");
6699
+ } catch {
6700
+ continue;
6701
+ }
6702
+ const lines = content.split("\n");
6703
+ for (let i = 0; i < lines.length; i++) {
6704
+ const match = lines[i].match(IGNORE_PATTERN);
6705
+ if (!match) continue;
6706
+ const type = match[1];
6707
+ if (type === "file" && i >= 5) continue;
6708
+ entries.push({
6709
+ file_path: relativePath,
6710
+ line: i + 1,
6711
+ type,
6712
+ rule_id: match[2] ?? null,
6713
+ raw: lines[i].trim()
6714
+ });
6715
+ }
6716
+ }
6717
+ return entries.sort((a, b) => a.file_path.localeCompare(b.file_path) || a.line - b.line);
6718
+ }
6719
+ async function removeIgnore(rootPath, target) {
6720
+ const ignores = await findIgnores(rootPath);
6721
+ const colonIdx = target.lastIndexOf(":");
6722
+ let targetFile;
6723
+ let targetLine = null;
6724
+ if (colonIdx > 0) {
6725
+ const maybeLine = parseInt(target.slice(colonIdx + 1), 10);
6726
+ if (!isNaN(maybeLine)) {
6727
+ targetFile = target.slice(0, colonIdx);
6728
+ targetLine = maybeLine;
6729
+ } else {
6730
+ targetFile = target;
6731
+ }
6732
+ } else {
6733
+ targetFile = target;
6734
+ }
6735
+ const matches = ignores.filter((e) => {
6736
+ if (e.file_path !== targetFile) return false;
6737
+ if (targetLine !== null) return e.line === targetLine;
6738
+ return true;
6739
+ });
6740
+ if (matches.length === 0) {
6741
+ return { removed: [], notFound: true };
6742
+ }
6743
+ const byFile = /* @__PURE__ */ new Map();
6744
+ for (const m of matches) {
6745
+ const lines = byFile.get(m.file_path) ?? [];
6746
+ lines.push(m.line);
6747
+ byFile.set(m.file_path, lines);
6748
+ }
6749
+ for (const [filePath, lineNumbers] of byFile) {
6750
+ const absolutePath = resolve(rootPath, filePath);
6751
+ const content = await readFile(absolutePath, "utf-8");
6752
+ const lines = content.split("\n");
6753
+ const toRemove = new Set(lineNumbers.map((l) => l - 1));
6754
+ const newLines = lines.filter((_, i) => !toRemove.has(i));
6755
+ await writeFile(absolutePath, newLines.join("\n"), "utf-8");
6756
+ }
6757
+ return { removed: matches, notFound: false };
6758
+ }
6759
+ async function addIgnore(rootPath, target, ruleId) {
6760
+ const colonIdx = target.lastIndexOf(":");
6761
+ if (colonIdx <= 0) return null;
6762
+ const targetFile = target.slice(0, colonIdx);
6763
+ const targetLine = parseInt(target.slice(colonIdx + 1), 10);
6764
+ if (isNaN(targetLine) || targetLine < 1) return null;
6765
+ const absolutePath = resolve(rootPath, targetFile);
6766
+ let content;
6767
+ try {
6768
+ content = await readFile(absolutePath, "utf-8");
6769
+ } catch {
6770
+ return null;
6771
+ }
6772
+ const lines = content.split("\n");
6773
+ if (targetLine > lines.length) return null;
6774
+ const targetContent = lines[targetLine - 1];
6775
+ const indent = targetContent.match(/^(\s*)/)?.[1] ?? "";
6776
+ const ext = targetFile.split(".").pop()?.toLowerCase();
6777
+ const ruleSuffix = ruleId ? ` ${ruleId}` : "";
6778
+ let comment;
6779
+ if (ext === "html" || ext === "htm") {
6780
+ comment = `${indent}<!-- equall-ignore-next-line${ruleSuffix} -->`;
6781
+ } else if (ext === "jsx" || ext === "tsx") {
6782
+ const inJsx = targetContent.trimStart().startsWith("<") || targetContent.includes(">");
6783
+ comment = inJsx ? `${indent}{/* equall-ignore-next-line${ruleSuffix} */}` : `${indent}// equall-ignore-next-line${ruleSuffix}`;
6784
+ } else {
6785
+ comment = `${indent}// equall-ignore-next-line${ruleSuffix}`;
6786
+ }
6787
+ lines.splice(targetLine - 1, 0, comment);
6788
+ await writeFile(absolutePath, lines.join("\n"), "utf-8");
6789
+ return { file: targetFile, line: targetLine, comment };
6790
+ }
6791
+ async function clearAllIgnores(rootPath) {
6792
+ const ignores = await findIgnores(rootPath);
6793
+ if (ignores.length === 0) return [];
6794
+ const byFile = /* @__PURE__ */ new Map();
6795
+ for (const entry of ignores) {
6796
+ const lines = byFile.get(entry.file_path) ?? [];
6797
+ lines.push(entry.line);
6798
+ byFile.set(entry.file_path, lines);
6799
+ }
6800
+ for (const [filePath, lineNumbers] of byFile) {
6801
+ const absolutePath = resolve(rootPath, filePath);
6802
+ const content = await readFile(absolutePath, "utf-8");
6803
+ const lines = content.split("\n");
6804
+ const toRemove = new Set(lineNumbers.map((l) => l - 1));
6805
+ const newLines = lines.filter((_, i) => !toRemove.has(i));
6806
+ await writeFile(absolutePath, newLines.join("\n"), "utf-8");
6807
+ }
6808
+ return ignores;
6809
+ }
6810
+
6663
6811
  // src/cli.ts
6664
6812
  var program2 = new Command();
6665
6813
  program2.name("equall").description("Open-source accessibility scoring \u2014 aggregates axe-core, eslint-plugin-jsx-a11y, and more.").version("0.1.0");
6666
- program2.command("scan").description("Scan a project for accessibility issues").argument("[path]", "Path to project root", ".").option("-l, --level <level>", "WCAG conformance target: A, AA, or AAA", "AA").option("--include <patterns...>", "Glob patterns to include").option("--exclude <patterns...>", "Glob patterns to exclude").option("--json", "Output results as JSON").option("--no-color", "Disable colored output").action(async (path, opts) => {
6814
+ program2.command("scan").description("Scan a project for accessibility issues").argument("[path]", "Path to project root", ".").option("-l, --level <level>", "WCAG conformance target: A, AA, or AAA", "AA").option("--include <patterns...>", "Glob patterns to include").option("--exclude <patterns...>", "Glob patterns to exclude").option("--json", "Output results as JSON").option("-i, --show-ignored", "Show ignored issues in output").option("--no-color", "Disable colored output").action(async (path, opts) => {
6667
6815
  const level = opts.level.toUpperCase();
6668
6816
  if (!["A", "AA", "AAA"].includes(level)) {
6669
6817
  console.error(`Invalid level "${opts.level}". Use A, AA, or AAA.`);
6670
6818
  process.exit(1);
6671
6819
  }
6672
- const displayName = basename(resolve(path));
6820
+ const displayName = basename(resolve2(path));
6673
6821
  const spinner = opts.json ? null : ora({ text: `Scanning ${displayName}`, indent: 2 }).start();
6674
6822
  try {
6675
6823
  const result = await runScan({
@@ -6691,7 +6839,7 @@ program2.command("scan").description("Scan a project for accessibility issues").
6691
6839
  if (opts.json) {
6692
6840
  printJson(result);
6693
6841
  } else {
6694
- printResult(result);
6842
+ printResult(result, { showIgnored: opts.showIgnored });
6695
6843
  }
6696
6844
  if (result.score < 50) process.exit(1);
6697
6845
  } catch (error2) {
@@ -6703,4 +6851,68 @@ program2.command("scan").description("Scan a project for accessibility issues").
6703
6851
  process.exit(2);
6704
6852
  }
6705
6853
  });
6854
+ var DIM2 = "\x1B[2m";
6855
+ var BOLD2 = "\x1B[1m";
6856
+ var RESET2 = "\x1B[0m";
6857
+ var GREEN2 = "\x1B[32m";
6858
+ var YELLOW2 = "\x1B[33m";
6859
+ program2.command("ignore").description("Add, list, or remove equall-ignore comments").argument("[target]", "File:line to ignore (e.g. src/Modal.tsx:89)").argument("[rule-id]", "Optional rule ID (e.g. jsx-a11y/alt-text)").option("-p, --path <path>", "Path to project root", ".").option("--remove <target>", "Remove ignore at file:line or all ignores in a file").option("--clear", "Remove all equall-ignore comments from the project").option("--list", "List all equall-ignore comments").action(async (target, ruleId, opts) => {
6860
+ const rootPath = resolve2(opts.path);
6861
+ if (opts.clear) {
6862
+ const removed = await clearAllIgnores(rootPath);
6863
+ if (removed.length === 0) {
6864
+ console.log("\n No equall-ignore comments found.\n");
6865
+ } else {
6866
+ console.log(`
6867
+ ${GREEN2}Removed ${removed.length} ignore comment${removed.length > 1 ? "s" : ""}${RESET2}
6868
+ `);
6869
+ }
6870
+ return;
6871
+ }
6872
+ if (opts.remove) {
6873
+ const { removed, notFound } = await removeIgnore(rootPath, opts.remove);
6874
+ if (notFound) {
6875
+ console.error(`
6876
+ No equall-ignore found at ${opts.remove}
6877
+ `);
6878
+ process.exit(1);
6879
+ }
6880
+ for (const entry of removed) {
6881
+ const location = entry.type === "file" ? "" : `:${entry.line}`;
6882
+ console.log(` ${GREEN2}Removed${RESET2} ${entry.file_path}${location} ${DIM2}${entry.raw}${RESET2}`);
6883
+ }
6884
+ console.log();
6885
+ return;
6886
+ }
6887
+ if (target && target.includes(":")) {
6888
+ const result = await addIgnore(rootPath, target, ruleId);
6889
+ if (!result) {
6890
+ console.error(`
6891
+ Could not add ignore at ${target}. Check that the file and line exist.
6892
+ `);
6893
+ process.exit(1);
6894
+ }
6895
+ console.log(`
6896
+ ${GREEN2}Added${RESET2} ${result.file}:${result.line}`);
6897
+ console.log(` ${DIM2}${result.comment.trim()}${RESET2}
6898
+ `);
6899
+ return;
6900
+ }
6901
+ const listPath = target ? resolve2(target) : rootPath;
6902
+ const ignores = await findIgnores(listPath);
6903
+ if (ignores.length === 0) {
6904
+ console.log("\n No equall-ignore comments found.\n");
6905
+ return;
6906
+ }
6907
+ console.log(`
6908
+ ${BOLD2}${ignores.length} ignore${ignores.length > 1 ? "s" : ""}${RESET2}
6909
+ `);
6910
+ for (const entry of ignores) {
6911
+ const location = `:${entry.line}`;
6912
+ const type = entry.type === "file" ? `${YELLOW2}equall-ignore-file${RESET2}` : `equall-ignore-next-line`;
6913
+ const rule = entry.rule_id ? ` ${DIM2}${entry.rule_id}${RESET2}` : ` ${DIM2}(all rules)${RESET2}`;
6914
+ console.log(` ${entry.file_path}${location}${" ".repeat(Math.max(1, 40 - entry.file_path.length - location.length))}${type}${rule}`);
6915
+ }
6916
+ console.log();
6917
+ });
6706
6918
  program2.parse();
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ interface GladosIssue {
12
12
  message: string;
13
13
  help_url: string | null;
14
14
  suggestion: string | null;
15
+ ignored?: boolean;
15
16
  }
16
17
  type WcagLevel = 'A' | 'AA' | 'AAA';
17
18
  type PourPrinciple = 'perceivable' | 'operable' | 'understandable' | 'robust';
@@ -66,6 +67,7 @@ interface ScanSummary {
66
67
  by_scanner: Record<string, number>;
67
68
  criteria_tested: string[];
68
69
  criteria_failed: string[];
70
+ ignored_count: number;
69
71
  }
70
72
  interface ScannerInfo {
71
73
  name: string;
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from 'module'; import { fileURLToPat
2
2
  import {
3
3
  computeScanResult,
4
4
  runScan
5
- } from "./chunk-JMFHJMBB.js";
5
+ } from "./chunk-5YPLWCOT.js";
6
6
  export {
7
7
  computeScanResult,
8
8
  runScan
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "equall-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Open-source accessibility scoring CLI — aggregates axe-core, eslint-plugin-jsx-a11y, and more into a unified score.",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -65,4 +65,4 @@
65
65
  "engines": {
66
66
  "node": ">=18.0.0"
67
67
  }
68
- }
68
+ }