react-native-accessibility-scanner 0.1.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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +501 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +131 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/eslint/index.d.ts +59 -0
  8. package/dist/eslint/index.d.ts.map +1 -0
  9. package/dist/eslint/index.js +181 -0
  10. package/dist/eslint/index.js.map +1 -0
  11. package/dist/index.d.ts +12 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +36 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/reporters/console-reporter.d.ts +5 -0
  16. package/dist/reporters/console-reporter.d.ts.map +1 -0
  17. package/dist/reporters/console-reporter.js +67 -0
  18. package/dist/reporters/console-reporter.js.map +1 -0
  19. package/dist/reporters/json-reporter.d.ts +5 -0
  20. package/dist/reporters/json-reporter.d.ts.map +1 -0
  21. package/dist/reporters/json-reporter.js +13 -0
  22. package/dist/reporters/json-reporter.js.map +1 -0
  23. package/dist/reporters/markdown-reporter.d.ts +5 -0
  24. package/dist/reporters/markdown-reporter.d.ts.map +1 -0
  25. package/dist/reporters/markdown-reporter.js +66 -0
  26. package/dist/reporters/markdown-reporter.js.map +1 -0
  27. package/dist/rules/duplicate-labels.d.ts +25 -0
  28. package/dist/rules/duplicate-labels.d.ts.map +1 -0
  29. package/dist/rules/duplicate-labels.js +107 -0
  30. package/dist/rules/duplicate-labels.js.map +1 -0
  31. package/dist/rules/index.d.ts +13 -0
  32. package/dist/rules/index.d.ts.map +1 -0
  33. package/dist/rules/index.js +38 -0
  34. package/dist/rules/index.js.map +1 -0
  35. package/dist/rules/missing-hint.d.ts +14 -0
  36. package/dist/rules/missing-hint.d.ts.map +1 -0
  37. package/dist/rules/missing-hint.js +94 -0
  38. package/dist/rules/missing-hint.js.map +1 -0
  39. package/dist/rules/missing-label.d.ts +10 -0
  40. package/dist/rules/missing-label.d.ts.map +1 -0
  41. package/dist/rules/missing-label.js +75 -0
  42. package/dist/rules/missing-label.js.map +1 -0
  43. package/dist/rules/missing-role.d.ts +10 -0
  44. package/dist/rules/missing-role.d.ts.map +1 -0
  45. package/dist/rules/missing-role.js +82 -0
  46. package/dist/rules/missing-role.js.map +1 -0
  47. package/dist/rules/small-touch-target.d.ts +10 -0
  48. package/dist/rules/small-touch-target.d.ts.map +1 -0
  49. package/dist/rules/small-touch-target.js +90 -0
  50. package/dist/rules/small-touch-target.js.map +1 -0
  51. package/dist/rules/touchable-without-label.d.ts +17 -0
  52. package/dist/rules/touchable-without-label.d.ts.map +1 -0
  53. package/dist/rules/touchable-without-label.js +81 -0
  54. package/dist/rules/touchable-without-label.js.map +1 -0
  55. package/dist/scanner/config-loader.d.ts +3 -0
  56. package/dist/scanner/config-loader.d.ts.map +1 -0
  57. package/dist/scanner/config-loader.js +46 -0
  58. package/dist/scanner/config-loader.js.map +1 -0
  59. package/dist/scanner/file-scanner.d.ts +6 -0
  60. package/dist/scanner/file-scanner.d.ts.map +1 -0
  61. package/dist/scanner/file-scanner.js +82 -0
  62. package/dist/scanner/file-scanner.js.map +1 -0
  63. package/dist/scanner/index.d.ts +12 -0
  64. package/dist/scanner/index.d.ts.map +1 -0
  65. package/dist/scanner/index.js +64 -0
  66. package/dist/scanner/index.js.map +1 -0
  67. package/dist/scanner/scorer.d.ts +26 -0
  68. package/dist/scanner/scorer.d.ts.map +1 -0
  69. package/dist/scanner/scorer.js +65 -0
  70. package/dist/scanner/scorer.js.map +1 -0
  71. package/dist/types/index.d.ts +88 -0
  72. package/dist/types/index.d.ts.map +1 -0
  73. package/dist/types/index.js +13 -0
  74. package/dist/types/index.js.map +1 -0
  75. package/dist/utils/ast.d.ts +24 -0
  76. package/dist/utils/ast.d.ts.map +1 -0
  77. package/dist/utils/ast.js +170 -0
  78. package/dist/utils/ast.js.map +1 -0
  79. package/package.json +67 -0
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DuplicateLabelsRule = void 0;
37
+ const t = __importStar(require("@babel/types"));
38
+ const ast_1 = require("../utils/ast");
39
+ /**
40
+ * Duplicate Labels Rule
41
+ *
42
+ * Unlike other rules which run per-node, this rule is designed to be run
43
+ * once per file: call it with the Program node (root) after all other nodes
44
+ * are processed, passing the collected label map.
45
+ *
46
+ * The scanner calls this rule's `runOnFile` method after traversal.
47
+ * The `run` method handles per-node label collection (returns empty array
48
+ * since duplicate detection requires the full file scan).
49
+ */
50
+ class DuplicateLabelsRule {
51
+ constructor() {
52
+ this.id = "duplicate-labels";
53
+ this.name = "Duplicate Accessibility Labels";
54
+ this.description = "Multiple interactive elements on the same screen sharing identical accessibilityLabel values may confuse screen reader users.";
55
+ this.severity = "low";
56
+ // Accumulates per-file during traversal
57
+ this.labelMap = new Map();
58
+ }
59
+ reset() {
60
+ this.labelMap = new Map();
61
+ }
62
+ run(node, context) {
63
+ if (!t.isJSXElement(node))
64
+ return [];
65
+ const opening = node.openingElement;
66
+ const name = (0, ast_1.getComponentName)(opening);
67
+ if (!name || !ast_1.INTERACTIVE_COMPONENTS.has(name))
68
+ return [];
69
+ const label = (0, ast_1.getStringAttributeValue)(opening, "accessibilityLabel");
70
+ if (!label)
71
+ return [];
72
+ const loc = opening.loc?.start ?? { line: 1, column: 0 };
73
+ const entry = { line: loc.line, column: loc.column + 1, file: context.filePath };
74
+ if (!this.labelMap.has(label)) {
75
+ this.labelMap.set(label, [entry]);
76
+ }
77
+ else {
78
+ this.labelMap.get(label).push(entry);
79
+ }
80
+ return [];
81
+ }
82
+ /** Call after traversal to collect duplicate label issues */
83
+ collectIssues(context) {
84
+ const issues = [];
85
+ for (const [label, occurrences] of this.labelMap.entries()) {
86
+ if (occurrences.length > 1) {
87
+ // Report all but the first occurrence
88
+ for (const occ of occurrences.slice(1)) {
89
+ issues.push({
90
+ ruleId: this.id,
91
+ ruleName: this.name,
92
+ message: `Duplicate accessibilityLabel "${label}" found ${occurrences.length} times in this file. Screen readers may confuse these elements.`,
93
+ suggestion: `Use unique labels like "${label} (1)", "${label} (2)", or add context: "${label} in header".`,
94
+ severity: this.severity,
95
+ file: occ.file,
96
+ line: occ.line,
97
+ column: occ.column,
98
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, occ.line),
99
+ });
100
+ }
101
+ }
102
+ }
103
+ return issues;
104
+ }
105
+ }
106
+ exports.DuplicateLabelsRule = DuplicateLabelsRule;
107
+ //# sourceMappingURL=duplicate-labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicate-labels.js","sourceRoot":"","sources":["../../src/rules/duplicate-labels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { AccessibilityRule } from "../types";
2
+ import { MissingLabelRule } from "./missing-label";
3
+ import { MissingRoleRule } from "./missing-role";
4
+ import { SmallTouchTargetRule } from "./small-touch-target";
5
+ import { DuplicateLabelsRule } from "./duplicate-labels";
6
+ import { MissingHintRule } from "./missing-hint";
7
+ import { TouchableWithoutLabelRule } from "./touchable-without-label";
8
+ export declare function registerRule(rule: AccessibilityRule): void;
9
+ export declare function getRule(id: string): AccessibilityRule | undefined;
10
+ export declare function getAllRules(): AccessibilityRule[];
11
+ export declare const duplicateLabelsRule: DuplicateLabelsRule;
12
+ export { MissingLabelRule, MissingRoleRule, SmallTouchTargetRule, DuplicateLabelsRule, MissingHintRule, TouchableWithoutLabelRule, };
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAMtE,wBAAgB,YAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAE1D;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAEjE;AAED,wBAAgB,WAAW,IAAI,iBAAiB,EAAE,CAEjD;AAID,eAAO,MAAM,mBAAmB,qBAA4B,CAAC;AAS7D,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,yBAAyB,GAC1B,CAAC"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TouchableWithoutLabelRule = exports.MissingHintRule = exports.DuplicateLabelsRule = exports.SmallTouchTargetRule = exports.MissingRoleRule = exports.MissingLabelRule = exports.duplicateLabelsRule = void 0;
4
+ exports.registerRule = registerRule;
5
+ exports.getRule = getRule;
6
+ exports.getAllRules = getAllRules;
7
+ const missing_label_1 = require("./missing-label");
8
+ Object.defineProperty(exports, "MissingLabelRule", { enumerable: true, get: function () { return missing_label_1.MissingLabelRule; } });
9
+ const missing_role_1 = require("./missing-role");
10
+ Object.defineProperty(exports, "MissingRoleRule", { enumerable: true, get: function () { return missing_role_1.MissingRoleRule; } });
11
+ const small_touch_target_1 = require("./small-touch-target");
12
+ Object.defineProperty(exports, "SmallTouchTargetRule", { enumerable: true, get: function () { return small_touch_target_1.SmallTouchTargetRule; } });
13
+ const duplicate_labels_1 = require("./duplicate-labels");
14
+ Object.defineProperty(exports, "DuplicateLabelsRule", { enumerable: true, get: function () { return duplicate_labels_1.DuplicateLabelsRule; } });
15
+ const missing_hint_1 = require("./missing-hint");
16
+ Object.defineProperty(exports, "MissingHintRule", { enumerable: true, get: function () { return missing_hint_1.MissingHintRule; } });
17
+ const touchable_without_label_1 = require("./touchable-without-label");
18
+ Object.defineProperty(exports, "TouchableWithoutLabelRule", { enumerable: true, get: function () { return touchable_without_label_1.TouchableWithoutLabelRule; } });
19
+ // ─── Registry ─────────────────────────────────────────────────────────────────
20
+ const registry = new Map();
21
+ function registerRule(rule) {
22
+ registry.set(rule.id, rule);
23
+ }
24
+ function getRule(id) {
25
+ return registry.get(id);
26
+ }
27
+ function getAllRules() {
28
+ return Array.from(registry.values());
29
+ }
30
+ // ─── Built-in rules ───────────────────────────────────────────────────────────
31
+ exports.duplicateLabelsRule = new duplicate_labels_1.DuplicateLabelsRule();
32
+ registerRule(new missing_label_1.MissingLabelRule());
33
+ registerRule(new missing_role_1.MissingRoleRule());
34
+ registerRule(new small_touch_target_1.SmallTouchTargetRule());
35
+ registerRule(exports.duplicateLabelsRule);
36
+ registerRule(new missing_hint_1.MissingHintRule());
37
+ registerRule(new touchable_without_label_1.TouchableWithoutLabelRule());
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":";;;AAYA,oCAEC;AAED,0BAEC;AAED,kCAEC;AArBD,mDAAmD;AAmCjD,iGAnCO,gCAAgB,OAmCP;AAlClB,iDAAiD;AAmC/C,gGAnCO,8BAAe,OAmCP;AAlCjB,6DAA4D;AAmC1D,qGAnCO,yCAAoB,OAmCP;AAlCtB,yDAAyD;AAmCvD,oGAnCO,sCAAmB,OAmCP;AAlCrB,iDAAiD;AAmC/C,gGAnCO,8BAAe,OAmCP;AAlCjB,uEAAsE;AAmCpE,0GAnCO,mDAAyB,OAmCP;AAjC3B,iFAAiF;AAEjF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;AAEtD,SAAgB,YAAY,CAAC,IAAuB;IAClD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,SAAgB,OAAO,CAAC,EAAU;IAChC,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,SAAgB,WAAW;IACzB,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,iFAAiF;AAEpE,QAAA,mBAAmB,GAAG,IAAI,sCAAmB,EAAE,CAAC;AAE7D,YAAY,CAAC,IAAI,gCAAgB,EAAE,CAAC,CAAC;AACrC,YAAY,CAAC,IAAI,8BAAe,EAAE,CAAC,CAAC;AACpC,YAAY,CAAC,IAAI,yCAAoB,EAAE,CAAC,CAAC;AACzC,YAAY,CAAC,2BAAmB,CAAC,CAAC;AAClC,YAAY,CAAC,IAAI,8BAAe,EAAE,CAAC,CAAC;AACpC,YAAY,CAAC,IAAI,mDAAyB,EAAE,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import * as t from "@babel/types";
2
+ import type { AccessibilityRule, AccessibilityIssue, RuleContext } from "../types";
3
+ /**
4
+ * Flags critical-action buttons that have an accessibilityLabel mentioning
5
+ * words like "Delete", "Purchase", "Checkout" etc. but no accessibilityHint.
6
+ */
7
+ export declare class MissingHintRule implements AccessibilityRule {
8
+ id: string;
9
+ name: string;
10
+ description: string;
11
+ severity: "low";
12
+ run(node: t.Node, context: RuleContext): AccessibilityIssue[];
13
+ }
14
+ //# sourceMappingURL=missing-hint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-hint.d.ts","sourceRoot":"","sources":["../../src/rules/missing-hint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAUnF;;;GAGG;AACH,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,EAAE,SAAkB;IACpB,IAAI,SAAkD;IACtD,WAAW,SACgH;IAC3H,QAAQ,EAAG,KAAK,CAAU;IAE1B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,kBAAkB,EAAE;CA+C9D"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MissingHintRule = void 0;
37
+ const t = __importStar(require("@babel/types"));
38
+ const ast_1 = require("../utils/ast");
39
+ /**
40
+ * Flags critical-action buttons that have an accessibilityLabel mentioning
41
+ * words like "Delete", "Purchase", "Checkout" etc. but no accessibilityHint.
42
+ */
43
+ class MissingHintRule {
44
+ constructor() {
45
+ this.id = "missing-hint";
46
+ this.name = "Missing accessibilityHint on Critical Action";
47
+ this.description = "Critical actions (delete, purchase, checkout, submit) should provide an accessibilityHint describing what will happen.";
48
+ this.severity = "low";
49
+ }
50
+ run(node, context) {
51
+ if (!t.isJSXElement(node))
52
+ return [];
53
+ const opening = node.openingElement;
54
+ const name = (0, ast_1.getComponentName)(opening);
55
+ if (!name || !ast_1.INTERACTIVE_COMPONENTS.has(name))
56
+ return [];
57
+ if ((0, ast_1.hasNonEmptyAttribute)(opening, "accessibilityHint"))
58
+ return [];
59
+ const label = (0, ast_1.getStringAttributeValue)(opening, "accessibilityLabel") ?? "";
60
+ const isCritical = ast_1.CRITICAL_ACTION_KEYWORDS.some((kw) => label.toLowerCase().includes(kw));
61
+ if (!isCritical)
62
+ return [];
63
+ const loc = opening.loc?.start ?? { line: 1, column: 0 };
64
+ const keyword = ast_1.CRITICAL_ACTION_KEYWORDS.find((kw) => label.toLowerCase().includes(kw));
65
+ const examples = {
66
+ delete: "Permanently removes this item",
67
+ remove: "Removes this item from the list",
68
+ purchase: "Completes your purchase",
69
+ checkout: "Proceeds to payment",
70
+ submit: "Submits the form",
71
+ payment: "Processes your payment",
72
+ pay: "Completes your payment",
73
+ buy: "Purchases the item",
74
+ confirm: "Confirms the action",
75
+ cancel: "Cancels and discards changes",
76
+ };
77
+ const exampleHint = keyword ? examples[keyword] : "Describes what will happen";
78
+ return [
79
+ {
80
+ ruleId: this.id,
81
+ ruleName: this.name,
82
+ message: `<${name}> with label "${label}" is a critical action but has no accessibilityHint.`,
83
+ suggestion: `Add accessibilityHint="${exampleHint}" to help screen reader users understand the consequence.`,
84
+ severity: this.severity,
85
+ file: context.filePath,
86
+ line: loc.line,
87
+ column: loc.column + 1,
88
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, loc.line),
89
+ },
90
+ ];
91
+ }
92
+ }
93
+ exports.MissingHintRule = MissingHintRule;
94
+ //# sourceMappingURL=missing-hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-hint.js","sourceRoot":"","sources":["../../src/rules/missing-hint.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAElC,sCAOsB;AAEtB;;;GAGG;AACH,MAAa,eAAe;IAA5B;QACE,OAAE,GAAG,cAAc,CAAC;QACpB,SAAI,GAAG,8CAA8C,CAAC;QACtD,gBAAW,GACT,wHAAwH,CAAC;QAC3H,aAAQ,GAAG,KAAc,CAAC;IAiD5B,CAAC;IA/CC,GAAG,CAAC,IAAY,EAAE,OAAoB;QACpC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,sBAAgB,EAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,IAAI,CAAC,4BAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAE1D,IAAI,IAAA,0BAAoB,EAAC,OAAO,EAAE,mBAAmB,CAAC;YAAE,OAAO,EAAE,CAAC;QAElE,MAAM,KAAK,GAAG,IAAA,6BAAuB,EAAC,OAAO,EAAE,oBAAoB,CAAC,IAAI,EAAE,CAAC;QAC3E,MAAM,UAAU,GAAG,8BAAwB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CACtD,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CACjC,CAAC;QACF,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,CAAC;QAE3B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACzD,MAAM,OAAO,GAAG,8BAAwB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CACnD,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CACjC,CAAC;QACF,MAAM,QAAQ,GAA2B;YACvC,MAAM,EAAE,+BAA+B;YACvC,MAAM,EAAE,iCAAiC;YACzC,QAAQ,EAAE,yBAAyB;YACnC,QAAQ,EAAE,qBAAqB;YAC/B,MAAM,EAAE,kBAAkB;YAC1B,OAAO,EAAE,wBAAwB;YACjC,GAAG,EAAE,wBAAwB;YAC7B,GAAG,EAAE,oBAAoB;YACzB,OAAO,EAAE,qBAAqB;YAC9B,MAAM,EAAE,8BAA8B;SACvC,CAAC;QACF,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,4BAA4B,CAAC;QAE/E,OAAO;YACL;gBACE,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,OAAO,EAAE,IAAI,IAAI,iBAAiB,KAAK,sDAAsD;gBAC7F,UAAU,EAAE,0BAA0B,WAAW,2DAA2D;gBAC5G,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACtB,WAAW,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;aACtD;SACF,CAAC;IACJ,CAAC;CACF;AAtDD,0CAsDC"}
@@ -0,0 +1,10 @@
1
+ import * as t from "@babel/types";
2
+ import type { AccessibilityRule, AccessibilityIssue, RuleContext } from "../types";
3
+ export declare class MissingLabelRule implements AccessibilityRule {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ severity: "high";
8
+ run(node: t.Node, context: RuleContext): AccessibilityIssue[];
9
+ }
10
+ //# sourceMappingURL=missing-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-label.d.ts","sourceRoot":"","sources":["../../src/rules/missing-label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AASnF,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,EAAE,SAAmB;IACrB,IAAI,SAAgC;IACpC,WAAW,SACmF;IAC9F,QAAQ,EAAG,MAAM,CAAU;IAE3B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,kBAAkB,EAAE;CA4B9D"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MissingLabelRule = void 0;
37
+ const t = __importStar(require("@babel/types"));
38
+ const ast_1 = require("../utils/ast");
39
+ class MissingLabelRule {
40
+ constructor() {
41
+ this.id = "missing-label";
42
+ this.name = "Missing accessibilityLabel";
43
+ this.description = "Interactive elements must have an accessibilityLabel so screen readers can announce them.";
44
+ this.severity = "high";
45
+ }
46
+ run(node, context) {
47
+ if (!t.isJSXElement(node))
48
+ return [];
49
+ const opening = node.openingElement;
50
+ const name = (0, ast_1.getComponentName)(opening);
51
+ if (!name || !ast_1.INTERACTIVE_COMPONENTS.has(name))
52
+ return [];
53
+ // A Text child is considered a valid label substitute for screen readers
54
+ const hasLabel = (0, ast_1.hasNonEmptyAttribute)(opening, "accessibilityLabel");
55
+ const hasText = (0, ast_1.hasTextChild)(node);
56
+ if (hasLabel || hasText)
57
+ return [];
58
+ const loc = opening.loc?.start ?? { line: 1, column: 0 };
59
+ return [
60
+ {
61
+ ruleId: this.id,
62
+ ruleName: this.name,
63
+ message: `<${name}> is interactive but has no accessibilityLabel or Text child. Screen readers cannot describe this element.`,
64
+ suggestion: `Add accessibilityLabel="Describe the action" to the <${name}>.`,
65
+ severity: this.severity,
66
+ file: context.filePath,
67
+ line: loc.line,
68
+ column: loc.column + 1,
69
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, loc.line),
70
+ },
71
+ ];
72
+ }
73
+ }
74
+ exports.MissingLabelRule = MissingLabelRule;
75
+ //# sourceMappingURL=missing-label.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-label.js","sourceRoot":"","sources":["../../src/rules/missing-label.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAElC,sCAMsB;AAEtB,MAAa,gBAAgB;IAA7B;QACE,OAAE,GAAG,eAAe,CAAC;QACrB,SAAI,GAAG,4BAA4B,CAAC;QACpC,gBAAW,GACT,2FAA2F,CAAC;QAC9F,aAAQ,GAAG,MAAe,CAAC;IA8B7B,CAAC;IA5BC,GAAG,CAAC,IAAY,EAAE,OAAoB;QACpC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,sBAAgB,EAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,IAAI,CAAC,4BAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAE1D,yEAAyE;QACzE,MAAM,QAAQ,GAAG,IAAA,0BAAoB,EAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,IAAA,kBAAY,EAAC,IAAI,CAAC,CAAC;QAEnC,IAAI,QAAQ,IAAI,OAAO;YAAE,OAAO,EAAE,CAAC;QAEnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACzD,OAAO;YACL;gBACE,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,OAAO,EAAE,IAAI,IAAI,4GAA4G;gBAC7H,UAAU,EAAE,wDAAwD,IAAI,IAAI;gBAC5E,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACtB,WAAW,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;aACtD;SACF,CAAC;IACJ,CAAC;CACF;AAnCD,4CAmCC"}
@@ -0,0 +1,10 @@
1
+ import * as t from "@babel/types";
2
+ import type { AccessibilityRule, AccessibilityIssue, RuleContext } from "../types";
3
+ export declare class MissingRoleRule implements AccessibilityRule {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ severity: "medium";
8
+ run(node: t.Node, context: RuleContext): AccessibilityIssue[];
9
+ }
10
+ //# sourceMappingURL=missing-role.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-role.d.ts","sourceRoot":"","sources":["../../src/rules/missing-role.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAkBnF,qBAAa,eAAgB,YAAW,iBAAiB;IACvD,EAAE,SAAkB;IACpB,IAAI,SAA+B;IACnC,WAAW,SACyG;IACpH,QAAQ,EAAG,QAAQ,CAAU;IAE7B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,kBAAkB,EAAE;CAyB9D"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.MissingRoleRule = void 0;
37
+ const t = __importStar(require("@babel/types"));
38
+ const ast_1 = require("../utils/ast");
39
+ /** Maps component names to their sensible default role suggestion */
40
+ const SUGGESTED_ROLES = {
41
+ TouchableOpacity: "button",
42
+ TouchableHighlight: "button",
43
+ TouchableNativeFeedback: "button",
44
+ TouchableWithoutFeedback: "button",
45
+ Pressable: "button",
46
+ Button: "button",
47
+ };
48
+ class MissingRoleRule {
49
+ constructor() {
50
+ this.id = "missing-role";
51
+ this.name = "Missing accessibilityRole";
52
+ this.description = "Interactive elements should declare their accessibilityRole to help assistive technologies communicate purpose.";
53
+ this.severity = "medium";
54
+ }
55
+ run(node, context) {
56
+ if (!t.isJSXElement(node))
57
+ return [];
58
+ const opening = node.openingElement;
59
+ const name = (0, ast_1.getComponentName)(opening);
60
+ if (!name || !ast_1.INTERACTIVE_COMPONENTS.has(name))
61
+ return [];
62
+ if ((0, ast_1.hasNonEmptyAttribute)(opening, "accessibilityRole"))
63
+ return [];
64
+ const suggestedRole = SUGGESTED_ROLES[name] ?? "button";
65
+ const loc = opening.loc?.start ?? { line: 1, column: 0 };
66
+ return [
67
+ {
68
+ ruleId: this.id,
69
+ ruleName: this.name,
70
+ message: `<${name}> is missing accessibilityRole. Screen readers may not correctly announce the element type.`,
71
+ suggestion: `Add accessibilityRole="${suggestedRole}" to the <${name}>.`,
72
+ severity: this.severity,
73
+ file: context.filePath,
74
+ line: loc.line,
75
+ column: loc.column + 1,
76
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, loc.line),
77
+ },
78
+ ];
79
+ }
80
+ }
81
+ exports.MissingRoleRule = MissingRoleRule;
82
+ //# sourceMappingURL=missing-role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-role.js","sourceRoot":"","sources":["../../src/rules/missing-role.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAElC,sCAKsB;AAEtB,qEAAqE;AACrE,MAAM,eAAe,GAA2B;IAC9C,gBAAgB,EAAE,QAAQ;IAC1B,kBAAkB,EAAE,QAAQ;IAC5B,uBAAuB,EAAE,QAAQ;IACjC,wBAAwB,EAAE,QAAQ;IAClC,SAAS,EAAE,QAAQ;IACnB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF,MAAa,eAAe;IAA5B;QACE,OAAE,GAAG,cAAc,CAAC;QACpB,SAAI,GAAG,2BAA2B,CAAC;QACnC,gBAAW,GACT,iHAAiH,CAAC;QACpH,aAAQ,GAAG,QAAiB,CAAC;IA2B/B,CAAC;IAzBC,GAAG,CAAC,IAAY,EAAE,OAAoB;QACpC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,sBAAgB,EAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,IAAI,CAAC,4BAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAE1D,IAAI,IAAA,0BAAoB,EAAC,OAAO,EAAE,mBAAmB,CAAC;YAAE,OAAO,EAAE,CAAC;QAElE,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC;QACxD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QACzD,OAAO;YACL;gBACE,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,OAAO,EAAE,IAAI,IAAI,6FAA6F;gBAC9G,UAAU,EAAE,0BAA0B,aAAa,aAAa,IAAI,IAAI;gBACxE,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACtB,WAAW,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;aACtD;SACF,CAAC;IACJ,CAAC;CACF;AAhCD,0CAgCC"}
@@ -0,0 +1,10 @@
1
+ import * as t from "@babel/types";
2
+ import type { AccessibilityRule, AccessibilityIssue, RuleContext } from "../types";
3
+ export declare class SmallTouchTargetRule implements AccessibilityRule {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ severity: "medium";
8
+ run(node: t.Node, context: RuleContext): AccessibilityIssue[];
9
+ }
10
+ //# sourceMappingURL=small-touch-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"small-touch-target.d.ts","sourceRoot":"","sources":["../../src/rules/small-touch-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAQnF,qBAAa,oBAAqB,YAAW,iBAAiB;IAC5D,EAAE,SAAwB;IAC1B,IAAI,SAAwB;IAC5B,WAAW,SACuH;IAClI,QAAQ,EAAG,QAAQ,CAAU;IAE7B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,kBAAkB,EAAE;CA+C9D"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SmallTouchTargetRule = void 0;
37
+ const t = __importStar(require("@babel/types"));
38
+ const ast_1 = require("../utils/ast");
39
+ class SmallTouchTargetRule {
40
+ constructor() {
41
+ this.id = "small-touch-target";
42
+ this.name = "Small Touch Target";
43
+ this.description = "Interactive elements should meet minimum touch target sizes (44×44pt iOS / 48×48dp Android) for users with motor impairments.";
44
+ this.severity = "medium";
45
+ }
46
+ run(node, context) {
47
+ if (!t.isJSXElement(node))
48
+ return [];
49
+ const opening = node.openingElement;
50
+ const name = (0, ast_1.getComponentName)(opening);
51
+ if (!name || !ast_1.TOUCHABLE_COMPONENTS.has(name))
52
+ return [];
53
+ const { minWidth, minHeight } = context.config.touchTarget;
54
+ const width = (0, ast_1.extractStyleDimension)(opening, "width");
55
+ const height = (0, ast_1.extractStyleDimension)(opening, "height");
56
+ // We only report when a dimension is explicitly present AND below threshold.
57
+ // Dynamic styles are skipped — we can't statically evaluate them.
58
+ const issues = [];
59
+ const loc = opening.loc?.start ?? { line: 1, column: 0 };
60
+ if (width !== null && width < minWidth) {
61
+ issues.push({
62
+ ruleId: this.id,
63
+ ruleName: this.name,
64
+ message: `<${name}> has width=${width}, which is below the minimum ${minWidth}pt recommendation.`,
65
+ suggestion: `Increase width to at least ${minWidth} (or use minWidth: ${minWidth} with padding instead).`,
66
+ severity: this.severity,
67
+ file: context.filePath,
68
+ line: loc.line,
69
+ column: loc.column + 1,
70
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, loc.line),
71
+ });
72
+ }
73
+ if (height !== null && height < minHeight) {
74
+ issues.push({
75
+ ruleId: this.id,
76
+ ruleName: this.name,
77
+ message: `<${name}> has height=${height}, which is below the minimum ${minHeight}pt recommendation.`,
78
+ suggestion: `Increase height to at least ${minHeight} (or use minHeight: ${minHeight} with padding instead).`,
79
+ severity: this.severity,
80
+ file: context.filePath,
81
+ line: loc.line,
82
+ column: loc.column + 1,
83
+ codeSnippet: (0, ast_1.getSnippet)(context.sourceCode, loc.line),
84
+ });
85
+ }
86
+ return issues;
87
+ }
88
+ }
89
+ exports.SmallTouchTargetRule = SmallTouchTargetRule;
90
+ //# sourceMappingURL=small-touch-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"small-touch-target.js","sourceRoot":"","sources":["../../src/rules/small-touch-target.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAkC;AAElC,sCAKsB;AAEtB,MAAa,oBAAoB;IAAjC;QACE,OAAE,GAAG,oBAAoB,CAAC;QAC1B,SAAI,GAAG,oBAAoB,CAAC;QAC5B,gBAAW,GACT,+HAA+H,CAAC;QAClI,aAAQ,GAAG,QAAiB,CAAC;IAiD/B,CAAC;IA/CC,GAAG,CAAC,IAAY,EAAE,OAAoB;QACpC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;QACpC,MAAM,IAAI,GAAG,IAAA,sBAAgB,EAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,IAAI,CAAC,0BAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAExD,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC;QAE3D,MAAM,KAAK,GAAG,IAAA,2BAAqB,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAA,2BAAqB,EAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAExD,6EAA6E;QAC7E,kEAAkE;QAClE,MAAM,MAAM,GAAyB,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAEzD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,OAAO,EAAE,IAAI,IAAI,eAAe,KAAK,gCAAgC,QAAQ,oBAAoB;gBACjG,UAAU,EAAE,8BAA8B,QAAQ,sBAAsB,QAAQ,yBAAyB;gBACzG,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACtB,WAAW,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;aACtD,CAAC,CAAC;QACL,CAAC;QAED,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,GAAG,SAAS,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI;gBACnB,OAAO,EAAE,IAAI,IAAI,gBAAgB,MAAM,gCAAgC,SAAS,oBAAoB;gBACpG,UAAU,EAAE,+BAA+B,SAAS,uBAAuB,SAAS,yBAAyB;gBAC7G,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,IAAI,EAAE,OAAO,CAAC,QAAQ;gBACtB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC;gBACtB,WAAW,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC;aACtD,CAAC,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAtDD,oDAsDC"}
@@ -0,0 +1,17 @@
1
+ import * as t from "@babel/types";
2
+ import type { AccessibilityRule, AccessibilityIssue, RuleContext } from "../types";
3
+ /**
4
+ * Detects touchable elements that have neither:
5
+ * - an accessibilityLabel
6
+ * - a direct Text child
7
+ *
8
+ * This means screen readers will have nothing to announce.
9
+ */
10
+ export declare class TouchableWithoutLabelRule implements AccessibilityRule {
11
+ id: string;
12
+ name: string;
13
+ description: string;
14
+ severity: "high";
15
+ run(node: t.Node, context: RuleContext): AccessibilityIssue[];
16
+ }
17
+ //# sourceMappingURL=touchable-without-label.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"touchable-without-label.d.ts","sourceRoot":"","sources":["../../src/rules/touchable-without-label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,cAAc,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AASnF;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;IACjE,EAAE,SAA6B;IAC/B,IAAI,SAAqC;IACzC,WAAW,SACyF;IACpG,QAAQ,EAAG,MAAM,CAAU;IAE3B,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,kBAAkB,EAAE;CA2B9D"}