UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

348 lines (347 loc) 11.3 kB
"use strict"; /** * Accessibility rule definitions using the rule-based system * Each rule contains metadata and validation logic */ Object.defineProperty(exports, "__esModule", { value: true }); exports.A11yRuleManager = exports.MULTI_ELEMENT_RULES = exports.SEVERITY_LEVELS = exports.WCAG_LEVELS = exports.RULE_CATEGORIES = exports.A11Y_RULES = void 0; const constants_1 = require("../constants"); const ruleValidators_1 = require("../validators/ruleValidators"); exports.A11Y_RULES = { // Image Accessibility Rules "img-alt": { id: "img-alt", name: "Images must have alt text", description: "All img elements must have an alt attribute for screen readers", severity: "error", wcagLevel: "A", wcagCriterion: "1.1.1", selector: { tagName: "img", }, validator: ruleValidators_1.RuleValidators.validateImageAlt, }, "img-alt-meaningful": { id: "img-alt-meaningful", name: "Image alt text should be meaningful", description: 'Alt text should describe the image content, not generic terms like "image" or "picture"', severity: "warning", wcagLevel: "A", wcagCriterion: "1.1.1", selector: { tagName: "img", }, validator: ruleValidators_1.RuleValidators.validateImageAlt, }, // Form Accessibility Rules "input-label": { id: "input-label", name: "Form inputs must have labels", description: "All form inputs must have associated labels or ARIA labeling", severity: "error", wcagLevel: "A", wcagCriterion: "1.3.1", selector: { tagName: "input", }, validator: ruleValidators_1.RuleValidators.validateInputLabel, }, "fieldset-legend": { id: "fieldset-legend", name: "Fieldsets should have legends", description: "Fieldset elements should contain a legend for better form structure", severity: "warning", wcagLevel: "A", wcagCriterion: "1.3.1", selector: { tagName: "fieldset", }, validator: ruleValidators_1.RuleValidators.validateFormStructure, }, // Interactive Element Rules "button-text": { id: "button-text", name: "Buttons must have accessible text", description: "Button elements must have text content or ARIA labeling", severity: "error", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { tagName: "button", }, validator: ruleValidators_1.RuleValidators.validateButtonText, }, "link-text": { id: "link-text", name: "Links must have accessible text", description: "Link elements must have text content or ARIA labeling", severity: "error", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { tagName: "a", }, validator: ruleValidators_1.RuleValidators.validateLinkText, }, "link-text-descriptive": { id: "link-text-descriptive", name: "Link text should be descriptive", description: "Link text should describe the destination or purpose, avoid generic terms", severity: "warning", wcagLevel: "AA", wcagCriterion: "2.4.4", selector: { tagName: "a", }, validator: ruleValidators_1.RuleValidators.validateLinkText, }, "interactive-role": { id: "interactive-role", name: "Interactive elements need proper roles", description: "Elements with click handlers should have appropriate ARIA roles", severity: "warning", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { hasAnyProp: constants_1.INTERACTIVE_EVENT_HANDLERS, }, validator: ruleValidators_1.RuleValidators.validateInteractiveRole, }, // ARIA Rules "aria-role-valid": { id: "aria-role-valid", name: "ARIA roles must be valid", description: "Role attributes must contain valid ARIA role values", severity: "error", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { hasProps: ["role"], }, validator: ruleValidators_1.RuleValidators.validateAriaRole, }, "aria-hidden-value": { id: "aria-hidden-value", name: "aria-hidden must have valid value", description: 'aria-hidden attribute must be "true" or "false"', severity: "error", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { hasProps: ["aria-hidden"], }, validator: ruleValidators_1.RuleValidators.validateAriaAttributes, }, "aria-expanded-value": { id: "aria-expanded-value", name: "aria-expanded must have valid value", description: 'aria-expanded attribute must be "true", "false", or "undefined"', severity: "error", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { hasProps: ["aria-expanded"], }, validator: ruleValidators_1.RuleValidators.validateAriaAttributes, }, "aria-empty-value": { id: "aria-empty-value", name: "ARIA labels should not be empty", description: "ARIA labeling attributes should have meaningful values", severity: "warning", wcagLevel: "A", wcagCriterion: "4.1.2", selector: { hasAnyProp: constants_1.ARIA_LABELING_ATTRIBUTES, }, validator: ruleValidators_1.RuleValidators.validateAriaAttributes, }, // Document Structure Rules "html-lang": { id: "html-lang", name: "HTML must have lang attribute", description: "The html element must have a lang attribute to identify the page language", severity: "error", wcagLevel: "A", wcagCriterion: "3.1.1", selector: { tagName: "html", }, validator: ruleValidators_1.RuleValidators.validateLangAttribute, }, // Multi-element validation rules (handled separately) "heading-hierarchy-start": { id: "heading-hierarchy-start", name: "Page should start with h1", description: "The first heading should be an h1 element", severity: "warning", wcagLevel: "AA", wcagCriterion: "1.3.1", selector: { tagNames: constants_1.HEADING_TAGS, }, validator: () => null, // Handled by multi-element validator }, "heading-hierarchy-skip": { id: "heading-hierarchy-skip", name: "Do not skip heading levels", description: "Heading levels should not be skipped (e.g., h1 followed by h3)", severity: "warning", wcagLevel: "AA", wcagCriterion: "1.3.1", selector: { tagNames: constants_1.HEADING_TAGS, }, validator: () => null, // Handled by multi-element validator }, "duplicate-id": { id: "duplicate-id", name: "IDs must be unique", description: "Element IDs must be unique within the component", severity: "error", wcagLevel: "A", wcagCriterion: "4.1.1", selector: { hasProps: ["id"], }, validator: () => null, // Handled by multi-element validator }, }; // Rule categories for better organization and filtering exports.RULE_CATEGORIES = { IMAGES: ["img-alt", "img-alt-meaningful"], FORMS: ["input-label", "fieldset-legend"], INTERACTIVE: [ "button-text", "link-text", "link-text-descriptive", "interactive-role", ], ARIA: [ "aria-role-valid", "aria-hidden-value", "aria-expanded-value", "aria-empty-value", ], STRUCTURE: [ "html-lang", "heading-hierarchy-start", "heading-hierarchy-skip", "duplicate-id", ], }; // WCAG level groupings exports.WCAG_LEVELS = { A: [ "img-alt", "img-alt-meaningful", "input-label", "fieldset-legend", "button-text", "link-text", "interactive-role", "aria-role-valid", "aria-hidden-value", "aria-expanded-value", "aria-empty-value", "html-lang", "duplicate-id", ], AA: [ "link-text-descriptive", "heading-hierarchy-start", "heading-hierarchy-skip", ], AAA: [], // No AAA rules in current set }; // Severity groupings exports.SEVERITY_LEVELS = { error: [ "img-alt", "input-label", "button-text", "link-text", "aria-role-valid", "aria-hidden-value", "aria-expanded-value", "html-lang", "duplicate-id", ], warning: [ "img-alt-meaningful", "fieldset-legend", "link-text-descriptive", "interactive-role", "aria-empty-value", "heading-hierarchy-start", "heading-hierarchy-skip", ], info: [], // No info level rules in current set }; // Multi-element validation rules that require special handling exports.MULTI_ELEMENT_RULES = [ "heading-hierarchy-start", "heading-hierarchy-skip", "duplicate-id", ]; // Helper functions for rule management class A11yRuleManager { /** * Get all rules for a specific WCAG level */ static getRulesByWCAGLevel(level) { const ruleIds = exports.WCAG_LEVELS[level]; return ruleIds.map((id) => exports.A11Y_RULES[id]); } /** * Get all rules for a specific severity level */ static getRulesBySeverity(severity) { const ruleIds = exports.SEVERITY_LEVELS[severity]; return ruleIds.map((id) => exports.A11Y_RULES[id]); } /** * Get all rules in a specific category */ static getRulesByCategory(category) { const ruleIds = exports.RULE_CATEGORIES[category]; return ruleIds.map((id) => exports.A11Y_RULES[id]); } /** * Get all rule IDs */ static getAllRuleIds() { return Object.keys(exports.A11Y_RULES); } /** * Check if a rule requires multi-element validation */ static isMultiElementRule(ruleId) { return exports.MULTI_ELEMENT_RULES.includes(ruleId); } /** * Get rules that apply to a specific element tag */ static getRulesForElement(tagName) { return Object.values(exports.A11Y_RULES).filter((rule) => { const selector = rule.selector; return (selector.tagName === tagName || selector.tagNames?.includes(tagName)); }); } /** * Get rules that check for specific props */ static getRulesForProps(props) { return Object.values(exports.A11Y_RULES).filter((rule) => { const selector = rule.selector; if (selector.hasProps) { return selector.hasProps.some((prop) => props.includes(prop)); } if (selector.hasAnyProp) { return selector.hasAnyProp.some((prop) => props.includes(prop)); } return false; }); } } exports.A11yRuleManager = A11yRuleManager;