@diegovelasquezweb/a11y-engine 0.11.3 → 0.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.11.3",
3
+ "version": "0.11.5",
4
4
  "description": "WCAG 2.2 accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -888,7 +888,10 @@ function buildFindings(inputPayload, cliArgs) {
888
888
  primary_selector: bestSelector,
889
889
  actual: (() => {
890
890
  const raw = firstNode?.failureSummary || `Found ${nodes.length} instance(s).`;
891
- return raw.replace(/^Fix any of the following:\s*/i, "").trim();
891
+ return raw
892
+ .replace(/^Fix (any|all) of the following:\s*/i, "")
893
+ .replace(/^(See|Reference):\s*https?:\/\/\S+\s*/i, "")
894
+ .trim();
892
895
  })(),
893
896
  primary_failure_mode: failureInsights.primaryFailureMode,
894
897
  relationship_hint: failureInsights.relationshipHint,
@@ -1003,10 +1003,26 @@ async function runPa11yChecks(routeUrl, axeTags, sharedBrowser = null, includeWa
1003
1003
 
1004
1004
  const results = await pa11y(routeUrl, pa11yOptions);
1005
1005
 
1006
+ // Group issues by ruleId so each rule produces one violation (axe-style)
1007
+ const groupedByRule = new Map();
1008
+
1009
+ /**
1010
+ * Cleans raw pa11y messages — strips URL references, "Fix all/any of the following:" prefixes,
1011
+ * and other boilerplate that produces ugly titles in reports.
1012
+ */
1013
+ function cleanPa11yMessage(msg) {
1014
+ if (!msg) return "Accessibility issue";
1015
+ return msg
1016
+ .replace(/^(See|Reference):\s*https?:\/\/\S+\s*/i, "")
1017
+ .replace(/^Fix (all|any) of the following:\s*/i, "")
1018
+ .trim();
1019
+ }
1020
+
1006
1021
  for (const issue of results.issues || []) {
1007
1022
  if (issue.type === "notice") continue;
1008
1023
 
1009
1024
  const impact = impactMap[issue.typeCode] || "moderate";
1025
+ const isWarning = issue.type === "warning";
1010
1026
 
1011
1027
  let wcagCriterion = "";
1012
1028
  const wcagMatch = issue.code?.match(/Guideline(\d+)_(\d+)\.(\d+)_(\d+)_(\d+)/);
@@ -1026,35 +1042,47 @@ async function runPa11yChecks(routeUrl, axeTags, sharedBrowser = null, includeWa
1026
1042
 
1027
1043
  const ruleId = axeEquivId || `pa11y-${((issue.code || "unknown").split(".").pop() || "unknown").toLowerCase()}`;
1028
1044
  const originalCode = issue.code || "unknown";
1029
-
1030
- violations.push({
1031
- id: ruleId,
1032
- impact,
1033
- tags: ["pa11y-check", ...(wcagCriterion ? [`wcag${wcagCriterion.replace(/\./g, "")}`] : [])],
1034
- description: issue.message || "pa11y detected an accessibility issue",
1035
- help: issue.message?.split(".")[0] || "Accessibility issue detected by HTML CodeSniffer",
1036
- helpUrl: wcagCriterion
1037
- ? `https://www.w3.org/WAI/WCAG21/Understanding/${wcagCriterion.replace(/\./g, "")}`
1038
- : "https://squizlabs.github.io/HTML_CodeSniffer/",
1039
- source: "pa11y",
1040
- source_rule_id: originalCode,
1041
- nodes: [{
1042
- any: [],
1043
- all: [{
1044
- id: "pa11y-check",
1045
- data: { code: originalCode, context: issue.context?.slice(0, 200) },
1046
- relatedNodes: [],
1047
- impact,
1048
- message: issue.message || "",
1049
- }],
1050
- none: [],
1045
+ const cleanMessage = cleanPa11yMessage(issue.message);
1046
+
1047
+ const node = {
1048
+ any: [],
1049
+ all: [{
1050
+ id: "pa11y-check",
1051
+ data: { code: originalCode, context: issue.context?.slice(0, 200) },
1052
+ relatedNodes: [],
1051
1053
  impact,
1052
- html: issue.context || "",
1053
- target: issue.selector ? [issue.selector] : [],
1054
- failureSummary: `Fix all of the following:\n ${issue.message || "Accessibility issue"}`,
1054
+ message: cleanMessage,
1055
1055
  }],
1056
- });
1056
+ none: [],
1057
+ impact,
1058
+ html: issue.context || "",
1059
+ target: issue.selector ? [issue.selector] : [],
1060
+ failureSummary: cleanMessage,
1061
+ };
1062
+
1063
+ if (groupedByRule.has(ruleId)) {
1064
+ // Add node to existing violation
1065
+ groupedByRule.get(ruleId).nodes.push(node);
1066
+ } else {
1067
+ // Create new violation for this rule
1068
+ groupedByRule.set(ruleId, {
1069
+ id: ruleId,
1070
+ impact,
1071
+ tags: ["pa11y-check", ...(wcagCriterion ? [`wcag${wcagCriterion.replace(/\./g, "")}`] : [])],
1072
+ description: cleanMessage,
1073
+ help: cleanMessage.split(".")[0] || "Accessibility issue detected by HTML CodeSniffer",
1074
+ helpUrl: wcagCriterion
1075
+ ? `https://www.w3.org/WAI/WCAG21/Understanding/${wcagCriterion.replace(/\./g, "")}`
1076
+ : "https://squizlabs.github.io/HTML_CodeSniffer/",
1077
+ source: "pa11y",
1078
+ source_rule_id: originalCode,
1079
+ needs_verification: isWarning,
1080
+ nodes: [node],
1081
+ });
1082
+ }
1057
1083
  }
1084
+
1085
+ violations.push(...groupedByRule.values());
1058
1086
  } catch (err) {
1059
1087
  log.warn(`pa11y checks failed (non-fatal): ${err.message}`);
1060
1088
  }