bettera11y 0.2.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dom.ts","../src/utils/hash.ts","../src/utils/fingerprint.ts","../src/utils/positions.ts","../src/contracts/session.ts","../src/engine.ts","../src/reporters.ts","../src/adapters.ts","../src/rules/core/helpers.ts","../src/rules/core/forms-media-rules.ts","../src/rules/core/structure-rules.ts","../src/rules/core/aria-keyboard-rules.ts","../src/rules/core/landmark-rules.ts","../src/rules/default-rules.ts","../src/presets/index.ts"],"sourcesContent":["import { JSDOM } from \"jsdom\";\nimport type { AuditInput } from \"./contracts\";\nimport { createRangeLocation } from \"./utils\";\n\nexport function createDocumentFromHtml(html: string): Document {\n return new JSDOM(html).window.document;\n}\n\nexport function normalizeInputToHtml(input: AuditInput): string | null {\n if (input.kind === \"html\" || input.kind === \"dom-snapshot\") {\n return input.html;\n }\n\n if (input.kind === \"virtual-file\") {\n return input.content;\n }\n\n return input.fallbackHtml ?? null;\n}\n\nexport function createElementSelector(element: Element): string {\n const id = element.getAttribute(\"id\");\n if (id) {\n return `#${id}`;\n }\n\n const parts: string[] = [];\n let current: Element | null = element;\n\n while (current && current.tagName.toLowerCase() !== \"html\") {\n const tag = current.tagName.toLowerCase();\n const parent: Element | null = current.parentElement;\n if (!parent) {\n parts.unshift(tag);\n break;\n }\n\n const siblings = Array.from(parent.children).filter(\n (child: Element) => child.tagName === current?.tagName,\n );\n const index = siblings.indexOf(current) + 1;\n parts.unshift(`${tag}:nth-of-type(${index})`);\n current = parent;\n }\n\n return parts.join(\" > \");\n}\n\nexport function locateElementInSource(\n source: string,\n element: Element,\n selector: string,\n sourcePath?: string,\n) {\n const tag = element.tagName.toLowerCase();\n const openTag = `<${tag}`;\n const offset = source.indexOf(openTag);\n if (offset === -1) {\n return { selector, sourcePath };\n }\n\n return createRangeLocation(\n source,\n offset,\n offset + openTag.length,\n selector,\n sourcePath,\n );\n}\n","import { createHash } from \"node:crypto\";\n\nexport function getSha256(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n","import type { AuditDiagnostic } from \"../contracts\";\nimport { getSha256 } from \"./hash\";\n\nexport function createDiagnosticFingerprint(\n diagnostic: Omit<AuditDiagnostic, \"id\">,\n): string {\n return getSha256(\n JSON.stringify({\n ruleId: diagnostic.ruleId,\n message: diagnostic.message,\n severity: diagnostic.severity,\n selector: diagnostic.location?.selector,\n start: diagnostic.location?.start,\n end: diagnostic.location?.end,\n }),\n ).slice(0, 16);\n}\n","import type { DiagnosticLocation, Position, Severity } from \"../contracts\";\n\nexport function positionFromOffset(source: string, offset: number): Position {\n const safeOffset = Math.max(0, Math.min(offset, source.length));\n let line = 1;\n let column = 1;\n\n for (let i = 0; i < safeOffset; i += 1) {\n if (source[i] === \"\\n\") {\n line += 1;\n column = 1;\n } else {\n column += 1;\n }\n }\n\n return { line, column, offset: safeOffset };\n}\n\nexport function createRangeLocation(\n source: string,\n startOffset: number,\n endOffset: number,\n selector?: string,\n sourcePath?: string,\n): DiagnosticLocation {\n return {\n sourcePath,\n selector,\n start: positionFromOffset(source, startOffset),\n end: positionFromOffset(source, endOffset),\n };\n}\n\nexport function selectorToSourceLocation(\n source: string,\n selector: string | undefined,\n sourcePath?: string,\n): DiagnosticLocation {\n if (!selector) {\n return { sourcePath };\n }\n\n const tag = selector.split(/[.#:\\s>]/).find(Boolean);\n if (!tag) {\n return { sourcePath, selector };\n }\n\n const openTag = `<${tag}`;\n const startOffset = source.indexOf(openTag);\n if (startOffset === -1) {\n return { sourcePath, selector };\n }\n\n const endOffset = startOffset + openTag.length;\n return createRangeLocation(\n source,\n startOffset,\n endOffset,\n selector,\n sourcePath,\n );\n}\n\nexport function translateSeverity(\n severity: Severity,\n target: \"eslint\" | \"vite\" | \"terminal\",\n): number | string {\n if (target === \"eslint\") {\n if (severity === \"error\") return 2;\n if (severity === \"warn\") return 1;\n return 0;\n }\n\n if (target === \"vite\") {\n if (severity === \"error\") return \"error\";\n if (severity === \"warn\") return \"warning\";\n return \"info\";\n }\n\n return severity.toUpperCase();\n}\n","import type { AuditResult } from \"./diagnostics\";\nimport type { AuditInput } from \"./input\";\nimport type { RuleEnabledMap, RuleSeverityOverrides } from \"./rules\";\n\nexport const BETTERA11Y_API_VERSION = \"1\";\n\nexport interface AuditEngineConfig {\n apiVersion?: typeof BETTERA11Y_API_VERSION;\n severityOverrides?: RuleSeverityOverrides;\n enabledRules?: RuleEnabledMap;\n}\n\nexport interface IncrementalAuditRequest {\n changes: AuditInput[];\n}\n\nexport interface InputProvider {\n nextInput: () => Promise<AuditInput | null>;\n}\n\nexport interface DiagnosticsSink {\n onResult: (result: AuditResult) => Promise<void> | void;\n}\n\nexport interface SessionTelemetryEvent {\n type: \"session-start\" | \"session-stop\" | \"audit-run\";\n durationMs?: number;\n cacheHit?: boolean;\n}\n\nexport interface SessionTelemetry {\n emit: (event: SessionTelemetryEvent) => void;\n}\n\nexport interface WatchSessionContract {\n start: () => Promise<void>;\n stop: () => Promise<void>;\n run: (input: AuditInput, signal?: AbortSignal) => Promise<AuditResult>;\n runIncremental: (\n request: IncrementalAuditRequest,\n signal?: AbortSignal,\n ) => Promise<AuditResult[]>;\n}\n","import {\n createDocumentFromHtml,\n createElementSelector,\n locateElementInSource,\n normalizeInputToHtml,\n} from \"./dom\";\nimport type {\n AuditDiagnostic,\n AuditEngineConfig,\n AuditInput,\n AuditResult,\n IncrementalAuditRequest,\n RuleDefinition,\n SessionTelemetry,\n WatchSessionContract,\n} from \"./contracts\";\nimport { BETTERA11Y_API_VERSION } from \"./contracts\";\nimport {\n createDiagnosticFingerprint,\n getSha256,\n selectorToSourceLocation,\n} from \"./utils\";\n\nexport interface AuditEngine {\n registerRule: (rule: RuleDefinition) => void;\n unregisterRule: (ruleId: string) => void;\n listRules: () => RuleDefinition[];\n run: (input: AuditInput, signal?: AbortSignal) => Promise<AuditResult>;\n runIncremental: (\n request: IncrementalAuditRequest,\n signal?: AbortSignal,\n ) => Promise<AuditResult[]>;\n createAuditSession: (telemetry?: SessionTelemetry) => WatchSessionContract;\n}\n\ninterface CacheEntry {\n result: AuditResult;\n createdAt: number;\n}\n\nexport function createEngine(\n initialRules: RuleDefinition[] = [],\n config: AuditEngineConfig = {},\n): AuditEngine {\n const ruleMap = new Map<string, RuleDefinition>();\n const cache = new Map<string, CacheEntry>();\n\n initialRules.forEach((rule) => {\n ruleMap.set(rule.meta.id, rule);\n });\n\n const listRules = (): RuleDefinition[] =>\n Array.from(ruleMap.values()).sort((a, b) =>\n a.meta.id.localeCompare(b.meta.id),\n );\n\n const run = async (\n input: AuditInput,\n signal?: AbortSignal,\n ): Promise<AuditResult> => {\n if (signal?.aborted) {\n throw new Error(\"Audit cancelled before start.\");\n }\n\n const start = performance.now();\n const html = normalizeInputToHtml(input);\n if (!html) {\n return {\n diagnostics: [],\n metadata: {\n inputId: input.id,\n sourcePath: input.source.path,\n cacheHit: false,\n durationMs: Number((performance.now() - start).toFixed(3)),\n ruleTimingsMs: {},\n },\n };\n }\n\n const rules = listRules().filter(\n (rule) => config.enabledRules?.[rule.meta.id] !== false,\n );\n const cacheKey = getSha256(\n `${config.apiVersion ?? BETTERA11Y_API_VERSION}:${input.source.contentHash ?? getSha256(html)}:${rules\n .map(\n (rule) =>\n `${rule.meta.id}:${config.severityOverrides?.[rule.meta.id] ?? \"default\"}`,\n )\n .join(\"|\")}`,\n );\n const cached = cache.get(cacheKey);\n if (cached) {\n return {\n ...cached.result,\n metadata: {\n ...cached.result.metadata,\n cacheHit: true,\n durationMs: Number((performance.now() - start).toFixed(3)),\n },\n };\n }\n\n const document = createDocumentFromHtml(html);\n const ruleTimingsMs: Record<string, number> = {};\n const diagnostics: AuditDiagnostic[] = [];\n\n for (const rule of rules) {\n if (signal?.aborted) {\n throw new Error(\"Audit cancelled.\");\n }\n const ruleStart = performance.now();\n const emitted = await rule.check({\n document,\n input,\n createSelector: createElementSelector,\n locate(element) {\n const selector = createElementSelector(element);\n return locateElementInSource(\n html,\n element,\n selector,\n input.source.path,\n );\n },\n signal,\n });\n ruleTimingsMs[rule.meta.id] = Number(\n (performance.now() - ruleStart).toFixed(3),\n );\n\n for (const item of emitted) {\n const severity =\n config.severityOverrides?.[rule.meta.id] ??\n item.severity ??\n rule.meta.defaultSeverity;\n const normalized = {\n ...item,\n severity,\n category: item.category ?? rule.meta.category,\n location:\n item.location ??\n selectorToSourceLocation(html, undefined, input.source.path),\n metadata: {\n docsUrl: rule.meta.docsUrl,\n tags: rule.meta.tags,\n ...item.metadata,\n },\n };\n diagnostics.push({\n ...normalized,\n id: createDiagnosticFingerprint(normalized),\n });\n }\n }\n\n const result: AuditResult = {\n diagnostics,\n metadata: {\n inputId: input.id,\n sourcePath: input.source.path,\n cacheHit: false,\n durationMs: Number((performance.now() - start).toFixed(3)),\n ruleTimingsMs,\n },\n };\n cache.set(cacheKey, { result, createdAt: Date.now() });\n return result;\n };\n\n const runIncremental = async (\n request: IncrementalAuditRequest,\n signal?: AbortSignal,\n ): Promise<AuditResult[]> => {\n const output: AuditResult[] = [];\n for (const change of request.changes) {\n output.push(await run(change, signal));\n }\n return output;\n };\n\n return {\n registerRule(rule) {\n ruleMap.set(rule.meta.id, rule);\n },\n unregisterRule(ruleId) {\n ruleMap.delete(ruleId);\n },\n listRules,\n run,\n runIncremental,\n createAuditSession(telemetry) {\n let started = false;\n return {\n async start() {\n started = true;\n telemetry?.emit({ type: \"session-start\" });\n },\n async stop() {\n started = false;\n telemetry?.emit({ type: \"session-stop\" });\n },\n async run(input, signal) {\n if (!started) {\n throw new Error(\"Audit session must be started before run.\");\n }\n const result = await run(input, signal);\n telemetry?.emit({\n type: \"audit-run\",\n durationMs: result.metadata.durationMs,\n cacheHit: result.metadata.cacheHit,\n });\n return result;\n },\n async runIncremental(request, signal) {\n if (!started) {\n throw new Error(\n \"Audit session must be started before runIncremental.\",\n );\n }\n return runIncremental(request, signal);\n },\n };\n },\n };\n}\n","import type { AuditResult } from \"./contracts\";\n\nexport interface DiagnosticsReporter {\n format: (result: AuditResult) => string;\n}\n\nexport function createJsonReporter(): DiagnosticsReporter {\n return {\n format(result) {\n return JSON.stringify(result, null, 2);\n },\n };\n}\n\nexport function createMachineReporter(): DiagnosticsReporter {\n return {\n format(result) {\n return JSON.stringify({\n diagnostics: result.diagnostics,\n metadata: result.metadata,\n });\n },\n };\n}\n\nexport function createPrettyReporter(): DiagnosticsReporter {\n return {\n format(result) {\n if (result.diagnostics.length === 0) {\n return \"No accessibility warnings detected.\";\n }\n\n const lines = result.diagnostics.map((diagnostic, index) => {\n const at = diagnostic.location?.selector\n ? ` at ${diagnostic.location.selector}`\n : \"\";\n const remediation = diagnostic.remediation\n ? `\\n remediation: ${diagnostic.remediation}`\n : \"\";\n return `${index + 1}. [${diagnostic.severity}] ${diagnostic.ruleId}: ${diagnostic.message}${at}${remediation}`;\n });\n\n return lines.join(\"\\n\");\n },\n };\n}\n","import type {\n AuditInput,\n AuditResult,\n DiagnosticsSink,\n InputProvider,\n} from \"./contracts\";\n\nexport interface ToolIntegrationAdapter {\n start: () => Promise<void> | void;\n stop: () => Promise<void> | void;\n onInput: (input: AuditInput) => Promise<void> | void;\n onDiagnostics: (result: AuditResult) => Promise<void> | void;\n}\n\nexport interface MockAdapterOptions {\n emit: (input: AuditInput) => Promise<AuditResult> | AuditResult;\n inputProvider?: InputProvider;\n diagnosticsSink?: DiagnosticsSink;\n}\n\nexport class MockAdapter implements ToolIntegrationAdapter {\n private readonly emit: MockAdapterOptions[\"emit\"];\n private readonly options: MockAdapterOptions;\n\n public readonly receivedInputs: AuditInput[] = [];\n public readonly diagnostics: AuditResult[] = [];\n public started = false;\n\n constructor(options: MockAdapterOptions) {\n this.options = options;\n this.emit = options.emit;\n }\n\n start(): void {\n this.started = true;\n }\n\n stop(): void {\n this.started = false;\n }\n\n async onInput(input: AuditInput): Promise<void> {\n this.receivedInputs.push(input);\n const result = await this.emit(input);\n await this.onDiagnostics(result);\n }\n\n async onDiagnostics(result: AuditResult): Promise<void> {\n this.diagnostics.push(result);\n await this.options.diagnosticsSink?.onResult(result);\n }\n\n async pump(): Promise<number> {\n if (!this.options.inputProvider) {\n return 0;\n }\n let processed = 0;\n while (this.started) {\n const input = await this.options.inputProvider.nextInput();\n if (!input) {\n break;\n }\n await this.onInput(input);\n processed += 1;\n }\n return processed;\n }\n}\n","export function hasAccessibleName(element: Element): boolean {\n const ariaLabel = element.getAttribute(\"aria-label\");\n if (ariaLabel && ariaLabel.trim()) return true;\n\n const ariaLabelledBy = element.getAttribute(\"aria-labelledby\");\n if (ariaLabelledBy && ariaLabelledBy.trim()) return true;\n\n const title = element.getAttribute(\"title\");\n if (title && title.trim()) return true;\n\n return Boolean(element.textContent?.trim());\n}\n\nexport function createSimpleDiagnostic(\n ruleId: string,\n message: string,\n severity: \"info\" | \"warn\" | \"error\",\n category:\n | \"semantics\"\n | \"structure\"\n | \"aria\"\n | \"forms\"\n | \"media\"\n | \"landmarks\"\n | \"keyboard\",\n remediation: string,\n location?: ReturnType<import(\"../../contracts\").RuleContext[\"locate\"]>,\n) {\n return { ruleId, message, severity, category, remediation, location };\n}\n","import type { RuleDefinition } from \"../../contracts\";\nimport { hasAccessibleName } from \"./helpers\";\n\nexport const imageAltRule: RuleDefinition = {\n meta: {\n id: \"image-alt\",\n description: \"Detect images without alternative text.\",\n category: \"media\",\n defaultSeverity: \"warn\",\n tags: [\"wcag-1.1.1\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n return Array.from(document.querySelectorAll(\"img\")).flatMap((img) => {\n if (img.hasAttribute(\"alt\")) return [];\n return [\n {\n ruleId: \"image-alt\",\n message: \"Image is missing an alt attribute.\",\n severity: \"warn\",\n category: \"media\",\n remediation:\n \"Provide meaningful alt text or empty alt for decorative images.\",\n location: locate(img),\n },\n ];\n });\n },\n};\n\nexport const formControlLabelRule: RuleDefinition = {\n meta: {\n id: \"form-control-label\",\n description: \"Detect form controls without labels.\",\n category: \"forms\",\n defaultSeverity: \"error\",\n tags: [\"forms\", \"wcag-3.3.2\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const controls = Array.from(\n document.querySelectorAll(\"input,select,textarea\"),\n ).filter((el) => el.getAttribute(\"type\")?.toLowerCase() !== \"hidden\");\n\n return controls.flatMap((control) => {\n const id = control.getAttribute(\"id\");\n const explicitLabel = id\n ? document.querySelector(`label[for=\"${id}\"]`)\n : null;\n const hasLabel =\n Boolean(explicitLabel) ||\n Boolean(control.closest(\"label\")) ||\n Boolean(control.getAttribute(\"aria-label\")) ||\n Boolean(control.getAttribute(\"aria-labelledby\"));\n if (hasLabel) return [];\n return [\n {\n ruleId: \"form-control-label\",\n message: \"Form control does not have an associated accessible label.\",\n severity: \"error\",\n category: \"forms\",\n remediation:\n \"Associate a <label> or provide aria-label/aria-labelledby.\",\n location: locate(control),\n },\n ];\n });\n },\n};\n\nexport const buttonAccessibleNameRule: RuleDefinition = {\n meta: {\n id: \"button-accessible-name\",\n description: \"Ensure buttons have accessible names.\",\n category: \"aria\",\n defaultSeverity: \"error\",\n tags: [\"controls\", \"wcag-4.1.2\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const buttons = Array.from(document.querySelectorAll(\"button\"));\n return buttons.flatMap((button) =>\n hasAccessibleName(button)\n ? []\n : [\n {\n ruleId: \"button-accessible-name\",\n message: \"Button is missing an accessible name.\",\n severity: \"error\",\n category: \"aria\",\n remediation:\n \"Provide text content, aria-label, or aria-labelledby.\",\n location: locate(button),\n },\n ],\n );\n },\n};\n","import type { RuleDefinition, RuleDiagnostic } from \"../../contracts\";\n\nexport const duplicateIdRule: RuleDefinition = {\n meta: {\n id: \"duplicate-id\",\n description: \"Detect duplicate element ids.\",\n category: \"structure\",\n defaultSeverity: \"error\",\n tags: [\"wcag-4.1.1\"],\n },\n check({ document, createSelector, locate }) {\n if (!document) return [];\n const idMap = new Map<string, Element[]>();\n document.querySelectorAll(\"[id]\").forEach((element) => {\n const id = element.getAttribute(\"id\");\n if (!id) return;\n const list = idMap.get(id) ?? [];\n list.push(element);\n idMap.set(id, list);\n });\n\n return Array.from(idMap.entries()).flatMap(([id, elements]) =>\n elements.length < 2\n ? []\n : elements.map((element) => {\n const selector = createSelector(element);\n return {\n ruleId: \"duplicate-id\",\n message: `Duplicate id \"${id}\" found.`,\n severity: \"error\",\n category: \"structure\",\n remediation: \"Ensure each id value is unique within a page.\",\n location: locate(element),\n metadata: { tags: [\"id\", \"structure\"] },\n };\n }),\n );\n },\n};\n\nexport const duplicateH1Rule: RuleDefinition = {\n meta: {\n id: \"duplicate-h1\",\n description: \"Detect multiple h1 elements in one document.\",\n category: \"structure\",\n defaultSeverity: \"warn\",\n tags: [\"headings\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const h1s = Array.from(document.querySelectorAll(\"h1\"));\n if (h1s.length <= 1) return [];\n return h1s.map((h1) => ({\n ruleId: \"duplicate-h1\",\n message: \"Multiple h1 elements found in the same document.\",\n severity: \"warn\",\n category: \"structure\",\n remediation:\n \"Use a single primary h1 and nest sections under lower heading levels.\",\n location: locate(h1),\n }));\n },\n};\n\nexport const headingOrderRule: RuleDefinition = {\n meta: {\n id: \"heading-order\",\n description: \"Detect heading level jumps.\",\n category: \"semantics\",\n defaultSeverity: \"warn\",\n tags: [\"headings\", \"wcag-1.3.1\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const headings = Array.from(document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\"));\n const diagnostics: RuleDiagnostic[] = [];\n let previous = 0;\n for (const heading of headings) {\n const level = Number(heading.tagName.toLowerCase().slice(1));\n if (previous > 0 && level > previous + 1) {\n diagnostics.push({\n ruleId: \"heading-order\",\n message: `Heading level jumps from h${previous} to h${level}.`,\n severity: \"warn\",\n category: \"semantics\",\n remediation:\n \"Use sequential heading levels to preserve document outline.\",\n location: locate(heading),\n });\n }\n previous = level;\n }\n return diagnostics;\n },\n};\n\nexport const htmlLangRule: RuleDefinition = {\n meta: {\n id: \"html-lang\",\n description: \"Ensure html element defines a language.\",\n category: \"semantics\",\n defaultSeverity: \"error\",\n tags: [\"wcag-3.1.1\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const html = document.documentElement;\n if (!html || html.getAttribute(\"lang\")) return [];\n return [\n {\n ruleId: \"html-lang\",\n message: \"The <html> element is missing a lang attribute.\",\n severity: \"error\",\n category: \"semantics\",\n remediation: 'Add a valid language code, for example <html lang=\"en\">.',\n location: locate(html),\n },\n ];\n },\n};\n","import type { RuleDefinition, RuleDiagnostic } from \"../../contracts\";\nimport { hasAccessibleName } from \"./helpers\";\n\nconst VALID_ARIA_ATTRIBUTES = new Set([\n \"aria-label\",\n \"aria-labelledby\",\n \"aria-describedby\",\n \"aria-hidden\",\n \"aria-expanded\",\n \"aria-controls\",\n \"aria-checked\",\n \"aria-pressed\",\n \"aria-current\",\n \"aria-live\",\n \"aria-invalid\",\n \"aria-required\",\n \"aria-selected\",\n \"aria-role-description\",\n]);\n\nconst BOOLEAN_ARIA_ATTRIBUTES = new Set([\n \"aria-hidden\",\n \"aria-expanded\",\n \"aria-checked\",\n \"aria-pressed\",\n \"aria-invalid\",\n \"aria-required\",\n \"aria-selected\",\n]);\n\nexport const invalidAriaRule: RuleDefinition = {\n meta: {\n id: \"invalid-aria\",\n description: \"Detect invalid aria attributes and values.\",\n category: \"aria\",\n defaultSeverity: \"error\",\n tags: [\"aria\", \"wcag-4.1.2\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const diagnostics: RuleDiagnostic[] = [];\n for (const element of Array.from(document.querySelectorAll(\"*\"))) {\n for (const attr of element.getAttributeNames()) {\n if (!attr.startsWith(\"aria-\")) continue;\n if (!VALID_ARIA_ATTRIBUTES.has(attr)) {\n diagnostics.push({\n ruleId: \"invalid-aria\",\n message: `Invalid ARIA attribute \"${attr}\".`,\n severity: \"error\",\n category: \"aria\",\n remediation: \"Use only valid WAI-ARIA attributes.\",\n location: locate(element),\n });\n continue;\n }\n\n if (BOOLEAN_ARIA_ATTRIBUTES.has(attr)) {\n const value = element.getAttribute(attr);\n if (value !== \"true\" && value !== \"false\") {\n diagnostics.push({\n ruleId: \"invalid-aria\",\n message: `ARIA boolean attribute \"${attr}\" must be \"true\" or \"false\".`,\n severity: \"error\",\n category: \"aria\",\n remediation: `Set ${attr} to \"true\" or \"false\".`,\n location: locate(element),\n });\n }\n }\n }\n }\n return diagnostics;\n },\n};\n\nexport const interactiveRoleNameRule: RuleDefinition = {\n meta: {\n id: \"interactive-role-name\",\n description: \"Detect custom interactive roles without accessible names.\",\n category: \"aria\",\n defaultSeverity: \"warn\",\n tags: [\"aria\", \"roles\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const interactiveRoles = new Set([\n \"button\",\n \"switch\",\n \"checkbox\",\n \"menuitem\",\n ]);\n return Array.from(document.querySelectorAll(\"[role]\")).flatMap(\n (element) => {\n const role = element.getAttribute(\"role\");\n if (\n !role ||\n !interactiveRoles.has(role) ||\n hasAccessibleName(element)\n ) {\n return [];\n }\n return [\n {\n ruleId: \"interactive-role-name\",\n message: `Element with role=\"${role}\" is missing an accessible name.`,\n severity: \"warn\",\n category: \"aria\",\n remediation:\n \"Provide aria-label, aria-labelledby, or meaningful text content.\",\n location: locate(element),\n },\n ];\n },\n );\n },\n};\n\nexport const positiveTabindexRule: RuleDefinition = {\n meta: {\n id: \"positive-tabindex\",\n description: \"Detect positive tabindex usage.\",\n category: \"keyboard\",\n defaultSeverity: \"warn\",\n tags: [\"keyboard\", \"focus-order\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n return Array.from(document.querySelectorAll(\"[tabindex]\")).flatMap(\n (element) => {\n const value = Number(element.getAttribute(\"tabindex\"));\n if (Number.isNaN(value) || value <= 0) return [];\n return [\n {\n ruleId: \"positive-tabindex\",\n message:\n \"Positive tabindex can create confusing keyboard focus order.\",\n severity: \"warn\",\n category: \"keyboard\",\n remediation:\n 'Use tabindex=\"0\" or rely on natural DOM order when possible.',\n location: locate(element),\n },\n ];\n },\n );\n },\n};\n","import type { RuleDefinition } from \"../../contracts\";\n\nexport const mainLandmarkRule: RuleDefinition = {\n meta: {\n id: \"main-landmark\",\n description: \"Require exactly one main landmark.\",\n category: \"landmarks\",\n defaultSeverity: \"warn\",\n tags: [\"landmarks\", \"wcag-1.3.1\"],\n },\n check({ document, locate }) {\n if (!document) return [];\n const mains = Array.from(document.querySelectorAll(\"main, [role='main']\"));\n if (mains.length === 1) return [];\n if (mains.length === 0) {\n return [\n {\n ruleId: \"main-landmark\",\n message: \"Document is missing a main landmark.\",\n severity: \"warn\",\n category: \"landmarks\",\n remediation:\n 'Add a <main> element (or role=\"main\") around core page content.',\n },\n ];\n }\n\n return mains.map((main) => ({\n ruleId: \"main-landmark\",\n message: \"Document has multiple main landmarks.\",\n severity: \"warn\",\n category: \"landmarks\",\n remediation: \"Keep only one top-level main landmark per page.\",\n location: locate(main),\n }));\n },\n};\n","import type { RuleDefinition } from \"../contracts\";\nimport {\n buttonAccessibleNameRule,\n formControlLabelRule,\n imageAltRule,\n} from \"./core/forms-media-rules\";\nimport {\n duplicateH1Rule,\n duplicateIdRule,\n headingOrderRule,\n htmlLangRule,\n} from \"./core/structure-rules\";\nimport {\n interactiveRoleNameRule,\n invalidAriaRule,\n positiveTabindexRule,\n} from \"./core/aria-keyboard-rules\";\nimport { mainLandmarkRule } from \"./core/landmark-rules\";\n\nexport const coreRules: RuleDefinition[] = [\n duplicateIdRule,\n duplicateH1Rule,\n headingOrderRule,\n htmlLangRule,\n imageAltRule,\n formControlLabelRule,\n buttonAccessibleNameRule,\n invalidAriaRule,\n interactiveRoleNameRule,\n positiveTabindexRule,\n mainLandmarkRule,\n];\n\nexport const defaultRules = coreRules;\n","import type { RuleDefinition } from \"../contracts\";\nimport { coreRules } from \"../rules/default-rules\";\n\nfunction byId(ids: string[]): RuleDefinition[] {\n const idSet = new Set(ids);\n return coreRules.filter((rule) => idSet.has(rule.meta.id));\n}\n\nexport const recommendedPreset: RuleDefinition[] = byId([\n \"duplicate-id\",\n \"heading-order\",\n \"image-alt\",\n \"form-control-label\",\n \"button-accessible-name\",\n \"invalid-aria\",\n \"main-landmark\",\n]);\n\nexport const strictPreset: RuleDefinition[] = [...coreRules];\n\nexport const wcagAaBaselinePreset: RuleDefinition[] = byId([\n \"duplicate-id\",\n \"heading-order\",\n \"html-lang\",\n \"image-alt\",\n \"form-control-label\",\n \"button-accessible-name\",\n \"invalid-aria\",\n \"main-landmark\",\n \"positive-tabindex\",\n]);\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "bettera11y",
3
+ "version": "0.2.1",
4
+ "description": "Core realtime accessibility audit engine for BetterA11y integrations.",
5
+ "author": "Sebastian Inman",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "CHANGELOG.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "typecheck": "tsc --noEmit",
27
+ "lint": "tsc --noEmit",
28
+ "commitlint": "commitlint --from=HEAD~1 --to=HEAD",
29
+ "format": "prettier --write .",
30
+ "format:check": "prettier --check .",
31
+ "test": "vitest run",
32
+ "test:performance": "vitest run tests/performance",
33
+ "validate:release": "npm run format:check && npm run typecheck && npm run test && npm run build"
34
+ },
35
+ "dependencies": {
36
+ "jsdom": "^25.0.1"
37
+ },
38
+ "devDependencies": {
39
+ "@commitlint/cli": "^19.8.0",
40
+ "@commitlint/config-conventional": "^19.8.0",
41
+ "@types/jsdom": "^21.1.7",
42
+ "@types/node": "^22.15.3",
43
+ "prettier": "^3.5.3",
44
+ "tsup": "^8.4.0",
45
+ "typescript": "^5.8.3",
46
+ "vitest": "^2.1.8"
47
+ }
48
+ }