@flint.fyi/jsx 0.4.0 → 0.6.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 (96) hide show
  1. package/dist/index.mjs +3235 -0
  2. package/package.json +9 -8
  3. package/lib/index.d.ts +0 -2
  4. package/lib/index.js +0 -2
  5. package/lib/plugin.js +0 -104
  6. package/lib/rules/accessKeys.d.ts +0 -9
  7. package/lib/rules/accessKeys.js +0 -30
  8. package/lib/rules/altTexts.d.ts +0 -9
  9. package/lib/rules/altTexts.js +0 -76
  10. package/lib/rules/anchorAmbiguousText.d.ts +0 -9
  11. package/lib/rules/anchorAmbiguousText.js +0 -55
  12. package/lib/rules/anchorContent.d.ts +0 -9
  13. package/lib/rules/anchorContent.js +0 -55
  14. package/lib/rules/anchorValidity.d.ts +0 -9
  15. package/lib/rules/anchorValidity.js +0 -77
  16. package/lib/rules/ariaActiveDescendantTabIndex.d.ts +0 -9
  17. package/lib/rules/ariaActiveDescendantTabIndex.js +0 -51
  18. package/lib/rules/ariaHiddenFocusables.d.ts +0 -9
  19. package/lib/rules/ariaHiddenFocusables.js +0 -66
  20. package/lib/rules/ariaPropTypes.d.ts +0 -9
  21. package/lib/rules/ariaPropTypes.js +0 -222
  22. package/lib/rules/ariaProps.d.ts +0 -9
  23. package/lib/rules/ariaProps.js +0 -90
  24. package/lib/rules/ariaRoleValidity.d.ts +0 -9
  25. package/lib/rules/ariaRoleValidity.js +0 -111
  26. package/lib/rules/ariaUnsupportedElements.d.ts +0 -9
  27. package/lib/rules/ariaUnsupportedElements.js +0 -86
  28. package/lib/rules/autoFocusProps.d.ts +0 -9
  29. package/lib/rules/autoFocusProps.js +0 -42
  30. package/lib/rules/autocomplete.d.ts +0 -9
  31. package/lib/rules/autocomplete.js +0 -120
  32. package/lib/rules/booleanValues.d.ts +0 -9
  33. package/lib/rules/booleanValues.js +0 -31
  34. package/lib/rules/bracedStatements.d.ts +0 -9
  35. package/lib/rules/bracedStatements.js +0 -31
  36. package/lib/rules/buttonTypes.d.ts +0 -9
  37. package/lib/rules/buttonTypes.js +0 -68
  38. package/lib/rules/childrenProps.d.ts +0 -9
  39. package/lib/rules/childrenProps.js +0 -37
  40. package/lib/rules/clickEventKeyEvents.d.ts +0 -9
  41. package/lib/rules/clickEventKeyEvents.js +0 -54
  42. package/lib/rules/commentTextNodes.d.ts +0 -9
  43. package/lib/rules/commentTextNodes.js +0 -30
  44. package/lib/rules/distractingElements.d.ts +0 -9
  45. package/lib/rules/distractingElements.js +0 -38
  46. package/lib/rules/elementChildrenValidity.d.ts +0 -9
  47. package/lib/rules/elementChildrenValidity.js +0 -52
  48. package/lib/rules/headingContents.d.ts +0 -9
  49. package/lib/rules/headingContents.js +0 -52
  50. package/lib/rules/htmlLangs.d.ts +0 -9
  51. package/lib/rules/htmlLangs.js +0 -30
  52. package/lib/rules/iframeTitles.d.ts +0 -9
  53. package/lib/rules/iframeTitles.js +0 -61
  54. package/lib/rules/interactiveElementRoles.d.ts +0 -9
  55. package/lib/rules/interactiveElementRoles.js +0 -91
  56. package/lib/rules/interactiveElementsFocusable.d.ts +0 -9
  57. package/lib/rules/interactiveElementsFocusable.js +0 -170
  58. package/lib/rules/labelAssociatedControls.d.ts +0 -9
  59. package/lib/rules/labelAssociatedControls.js +0 -68
  60. package/lib/rules/langValidity.d.ts +0 -9
  61. package/lib/rules/langValidity.js +0 -41
  62. package/lib/rules/mediaCaptions.d.ts +0 -9
  63. package/lib/rules/mediaCaptions.js +0 -50
  64. package/lib/rules/mouseEventKeyEvents.d.ts +0 -9
  65. package/lib/rules/mouseEventKeyEvents.js +0 -47
  66. package/lib/rules/nonInteractiveElementInteractions.d.ts +0 -9
  67. package/lib/rules/nonInteractiveElementInteractions.js +0 -93
  68. package/lib/rules/nonInteractiveElementRoles.d.ts +0 -9
  69. package/lib/rules/nonInteractiveElementRoles.js +0 -116
  70. package/lib/rules/nonInteractiveElementTabIndexes.d.ts +0 -9
  71. package/lib/rules/nonInteractiveElementTabIndexes.js +0 -107
  72. package/lib/rules/propDuplicates.d.ts +0 -9
  73. package/lib/rules/propDuplicates.js +0 -37
  74. package/lib/rules/roleRedundancies.d.ts +0 -9
  75. package/lib/rules/roleRedundancies.js +0 -74
  76. package/lib/rules/roleRequiredAriaProps.d.ts +0 -9
  77. package/lib/rules/roleRequiredAriaProps.js +0 -76
  78. package/lib/rules/roleSupportedAriaProps.d.ts +0 -9
  79. package/lib/rules/roleSupportedAriaProps.js +0 -417
  80. package/lib/rules/roleTags.d.ts +0 -9
  81. package/lib/rules/roleTags.js +0 -70
  82. package/lib/rules/ruleCreator.d.ts +0 -2
  83. package/lib/rules/ruleCreator.js +0 -13
  84. package/lib/rules/scopeProps.d.ts +0 -9
  85. package/lib/rules/scopeProps.js +0 -39
  86. package/lib/rules/staticElementInteractions.d.ts +0 -9
  87. package/lib/rules/staticElementInteractions.js +0 -58
  88. package/lib/rules/svgTitles.d.ts +0 -9
  89. package/lib/rules/svgTitles.js +0 -57
  90. package/lib/rules/tabIndexPositiveValues.d.ts +0 -9
  91. package/lib/rules/tabIndexPositiveValues.js +0 -43
  92. package/lib/rules/unescapedEntities.d.ts +0 -9
  93. package/lib/rules/unescapedEntities.js +0 -68
  94. package/lib/rules/unnecessaryFragments.d.ts +0 -9
  95. package/lib/rules/unnecessaryFragments.js +0 -49
  96. /package/{lib/plugin.d.ts → dist/index.d.mts} +0 -0
@@ -1,417 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { SyntaxKind } from "typescript";
3
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
- //#region src/rules/roleSupportedAriaProps.ts
5
- const globalAriaProperties = /* @__PURE__ */ new Set([
6
- "aria-atomic",
7
- "aria-busy",
8
- "aria-controls",
9
- "aria-current",
10
- "aria-describedby",
11
- "aria-details",
12
- "aria-disabled",
13
- "aria-dropeffect",
14
- "aria-errormessage",
15
- "aria-flowto",
16
- "aria-grabbed",
17
- "aria-haspopup",
18
- "aria-hidden",
19
- "aria-invalid",
20
- "aria-keyshortcuts",
21
- "aria-label",
22
- "aria-labelledby",
23
- "aria-live",
24
- "aria-owns",
25
- "aria-relevant",
26
- "aria-roledescription"
27
- ]);
28
- const roleToSupportedProps = {
29
- alert: /* @__PURE__ */ new Set(["aria-expanded"]),
30
- alertdialog: /* @__PURE__ */ new Set(["aria-expanded", "aria-modal"]),
31
- application: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-expanded"]),
32
- article: /* @__PURE__ */ new Set([
33
- "aria-expanded",
34
- "aria-posinset",
35
- "aria-setsize"
36
- ]),
37
- banner: /* @__PURE__ */ new Set(["aria-expanded"]),
38
- button: /* @__PURE__ */ new Set(["aria-expanded", "aria-pressed"]),
39
- cell: /* @__PURE__ */ new Set([
40
- "aria-colindex",
41
- "aria-colspan",
42
- "aria-expanded",
43
- "aria-rowindex",
44
- "aria-rowspan"
45
- ]),
46
- checkbox: /* @__PURE__ */ new Set([
47
- "aria-checked",
48
- "aria-expanded",
49
- "aria-readonly",
50
- "aria-required"
51
- ]),
52
- columnheader: /* @__PURE__ */ new Set([
53
- "aria-colindex",
54
- "aria-colspan",
55
- "aria-expanded",
56
- "aria-readonly",
57
- "aria-required",
58
- "aria-rowindex",
59
- "aria-rowspan",
60
- "aria-selected",
61
- "aria-sort"
62
- ]),
63
- combobox: /* @__PURE__ */ new Set([
64
- "aria-activedescendant",
65
- "aria-autocomplete",
66
- "aria-expanded",
67
- "aria-readonly",
68
- "aria-required"
69
- ]),
70
- command: /* @__PURE__ */ new Set(["aria-expanded"]),
71
- complementary: /* @__PURE__ */ new Set(["aria-expanded"]),
72
- composite: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-expanded"]),
73
- contentinfo: /* @__PURE__ */ new Set(["aria-expanded"]),
74
- definition: /* @__PURE__ */ new Set(["aria-expanded"]),
75
- dialog: /* @__PURE__ */ new Set(["aria-expanded", "aria-modal"]),
76
- directory: /* @__PURE__ */ new Set(["aria-expanded"]),
77
- document: /* @__PURE__ */ new Set(["aria-expanded"]),
78
- feed: /* @__PURE__ */ new Set(["aria-expanded"]),
79
- figure: /* @__PURE__ */ new Set(["aria-expanded"]),
80
- form: /* @__PURE__ */ new Set(["aria-expanded"]),
81
- grid: /* @__PURE__ */ new Set([
82
- "aria-activedescendant",
83
- "aria-colcount",
84
- "aria-expanded",
85
- "aria-level",
86
- "aria-multiselectable",
87
- "aria-readonly",
88
- "aria-rowcount"
89
- ]),
90
- gridcell: /* @__PURE__ */ new Set([
91
- "aria-colindex",
92
- "aria-colspan",
93
- "aria-expanded",
94
- "aria-readonly",
95
- "aria-required",
96
- "aria-rowindex",
97
- "aria-rowspan",
98
- "aria-selected"
99
- ]),
100
- group: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-expanded"]),
101
- heading: /* @__PURE__ */ new Set(["aria-expanded", "aria-level"]),
102
- img: /* @__PURE__ */ new Set(["aria-expanded"]),
103
- input: /* @__PURE__ */ new Set(["aria-expanded"]),
104
- landmark: /* @__PURE__ */ new Set(["aria-expanded"]),
105
- link: /* @__PURE__ */ new Set(["aria-expanded"]),
106
- list: /* @__PURE__ */ new Set(["aria-expanded"]),
107
- listbox: /* @__PURE__ */ new Set([
108
- "aria-activedescendant",
109
- "aria-expanded",
110
- "aria-multiselectable",
111
- "aria-orientation",
112
- "aria-readonly",
113
- "aria-required"
114
- ]),
115
- listitem: /* @__PURE__ */ new Set([
116
- "aria-expanded",
117
- "aria-level",
118
- "aria-posinset",
119
- "aria-setsize"
120
- ]),
121
- log: /* @__PURE__ */ new Set(["aria-expanded"]),
122
- main: /* @__PURE__ */ new Set(["aria-expanded"]),
123
- marquee: /* @__PURE__ */ new Set(["aria-expanded"]),
124
- math: /* @__PURE__ */ new Set(["aria-expanded"]),
125
- menu: /* @__PURE__ */ new Set([
126
- "aria-activedescendant",
127
- "aria-expanded",
128
- "aria-orientation"
129
- ]),
130
- menubar: /* @__PURE__ */ new Set([
131
- "aria-activedescendant",
132
- "aria-expanded",
133
- "aria-orientation"
134
- ]),
135
- menuitem: /* @__PURE__ */ new Set([
136
- "aria-expanded",
137
- "aria-posinset",
138
- "aria-setsize"
139
- ]),
140
- menuitemcheckbox: /* @__PURE__ */ new Set([
141
- "aria-checked",
142
- "aria-expanded",
143
- "aria-posinset",
144
- "aria-readonly",
145
- "aria-setsize"
146
- ]),
147
- menuitemradio: /* @__PURE__ */ new Set([
148
- "aria-checked",
149
- "aria-expanded",
150
- "aria-posinset",
151
- "aria-readonly",
152
- "aria-setsize"
153
- ]),
154
- navigation: /* @__PURE__ */ new Set(["aria-expanded"]),
155
- none: /* @__PURE__ */ new Set([]),
156
- note: /* @__PURE__ */ new Set(["aria-expanded"]),
157
- option: /* @__PURE__ */ new Set([
158
- "aria-checked",
159
- "aria-expanded",
160
- "aria-posinset",
161
- "aria-selected",
162
- "aria-setsize"
163
- ]),
164
- presentation: /* @__PURE__ */ new Set([]),
165
- progressbar: /* @__PURE__ */ new Set([
166
- "aria-expanded",
167
- "aria-valuemax",
168
- "aria-valuemin",
169
- "aria-valuenow",
170
- "aria-valuetext"
171
- ]),
172
- radio: /* @__PURE__ */ new Set([
173
- "aria-checked",
174
- "aria-expanded",
175
- "aria-posinset",
176
- "aria-readonly",
177
- "aria-setsize"
178
- ]),
179
- radiogroup: /* @__PURE__ */ new Set([
180
- "aria-activedescendant",
181
- "aria-expanded",
182
- "aria-orientation",
183
- "aria-readonly",
184
- "aria-required"
185
- ]),
186
- range: /* @__PURE__ */ new Set([
187
- "aria-expanded",
188
- "aria-valuemax",
189
- "aria-valuemin",
190
- "aria-valuenow",
191
- "aria-valuetext"
192
- ]),
193
- region: /* @__PURE__ */ new Set(["aria-expanded"]),
194
- roletype: /* @__PURE__ */ new Set(["aria-expanded"]),
195
- row: /* @__PURE__ */ new Set([
196
- "aria-activedescendant",
197
- "aria-colindex",
198
- "aria-expanded",
199
- "aria-level",
200
- "aria-rowindex",
201
- "aria-selected"
202
- ]),
203
- rowgroup: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-expanded"]),
204
- rowheader: /* @__PURE__ */ new Set([
205
- "aria-colindex",
206
- "aria-colspan",
207
- "aria-expanded",
208
- "aria-readonly",
209
- "aria-required",
210
- "aria-rowindex",
211
- "aria-rowspan",
212
- "aria-selected",
213
- "aria-sort"
214
- ]),
215
- scrollbar: /* @__PURE__ */ new Set([
216
- "aria-controls",
217
- "aria-expanded",
218
- "aria-orientation",
219
- "aria-valuemax",
220
- "aria-valuemin",
221
- "aria-valuenow",
222
- "aria-valuetext"
223
- ]),
224
- search: /* @__PURE__ */ new Set(["aria-expanded"]),
225
- searchbox: /* @__PURE__ */ new Set([
226
- "aria-activedescendant",
227
- "aria-autocomplete",
228
- "aria-expanded",
229
- "aria-multiline",
230
- "aria-placeholder",
231
- "aria-readonly",
232
- "aria-required"
233
- ]),
234
- section: /* @__PURE__ */ new Set(["aria-expanded"]),
235
- sectionhead: /* @__PURE__ */ new Set(["aria-expanded"]),
236
- select: /* @__PURE__ */ new Set([
237
- "aria-activedescendant",
238
- "aria-expanded",
239
- "aria-orientation"
240
- ]),
241
- separator: /* @__PURE__ */ new Set([
242
- "aria-expanded",
243
- "aria-orientation",
244
- "aria-valuemax",
245
- "aria-valuemin",
246
- "aria-valuenow",
247
- "aria-valuetext"
248
- ]),
249
- slider: /* @__PURE__ */ new Set([
250
- "aria-expanded",
251
- "aria-orientation",
252
- "aria-readonly",
253
- "aria-valuemax",
254
- "aria-valuemin",
255
- "aria-valuenow",
256
- "aria-valuetext"
257
- ]),
258
- spinbutton: /* @__PURE__ */ new Set([
259
- "aria-activedescendant",
260
- "aria-expanded",
261
- "aria-readonly",
262
- "aria-required",
263
- "aria-valuemax",
264
- "aria-valuemin",
265
- "aria-valuenow",
266
- "aria-valuetext"
267
- ]),
268
- status: /* @__PURE__ */ new Set(["aria-expanded"]),
269
- structure: /* @__PURE__ */ new Set(["aria-expanded"]),
270
- switch: /* @__PURE__ */ new Set([
271
- "aria-checked",
272
- "aria-expanded",
273
- "aria-readonly",
274
- "aria-required"
275
- ]),
276
- tab: /* @__PURE__ */ new Set([
277
- "aria-expanded",
278
- "aria-posinset",
279
- "aria-selected",
280
- "aria-setsize"
281
- ]),
282
- table: /* @__PURE__ */ new Set([
283
- "aria-colcount",
284
- "aria-expanded",
285
- "aria-level",
286
- "aria-multiselectable",
287
- "aria-rowcount"
288
- ]),
289
- tablist: /* @__PURE__ */ new Set([
290
- "aria-activedescendant",
291
- "aria-expanded",
292
- "aria-level",
293
- "aria-multiselectable",
294
- "aria-orientation"
295
- ]),
296
- tabpanel: /* @__PURE__ */ new Set(["aria-expanded"]),
297
- term: /* @__PURE__ */ new Set(["aria-expanded"]),
298
- textbox: /* @__PURE__ */ new Set([
299
- "aria-activedescendant",
300
- "aria-autocomplete",
301
- "aria-expanded",
302
- "aria-multiline",
303
- "aria-placeholder",
304
- "aria-readonly",
305
- "aria-required"
306
- ]),
307
- timer: /* @__PURE__ */ new Set(["aria-expanded"]),
308
- toolbar: /* @__PURE__ */ new Set([
309
- "aria-activedescendant",
310
- "aria-expanded",
311
- "aria-orientation"
312
- ]),
313
- tooltip: /* @__PURE__ */ new Set(["aria-expanded"]),
314
- tree: /* @__PURE__ */ new Set([
315
- "aria-activedescendant",
316
- "aria-expanded",
317
- "aria-multiselectable",
318
- "aria-orientation",
319
- "aria-required"
320
- ]),
321
- treegrid: /* @__PURE__ */ new Set([
322
- "aria-activedescendant",
323
- "aria-colcount",
324
- "aria-expanded",
325
- "aria-level",
326
- "aria-multiselectable",
327
- "aria-orientation",
328
- "aria-readonly",
329
- "aria-required",
330
- "aria-rowcount"
331
- ]),
332
- treeitem: /* @__PURE__ */ new Set([
333
- "aria-checked",
334
- "aria-expanded",
335
- "aria-level",
336
- "aria-posinset",
337
- "aria-selected",
338
- "aria-setsize"
339
- ]),
340
- widget: /* @__PURE__ */ new Set(["aria-expanded"]),
341
- window: /* @__PURE__ */ new Set(["aria-expanded", "aria-modal"])
342
- };
343
- const implicitRoles = {
344
- a: "link",
345
- article: "article",
346
- aside: "complementary",
347
- button: "button",
348
- dialog: "dialog",
349
- footer: "contentinfo",
350
- form: "form",
351
- h1: "heading",
352
- h2: "heading",
353
- h3: "heading",
354
- h4: "heading",
355
- h5: "heading",
356
- h6: "heading",
357
- header: "banner",
358
- hr: "separator",
359
- img: "img",
360
- input: "textbox",
361
- li: "listitem",
362
- main: "main",
363
- nav: "navigation",
364
- ol: "list",
365
- section: "region",
366
- select: "listbox",
367
- textarea: "textbox",
368
- ul: "list"
369
- };
370
- function getSupportedPropsForRole(role) {
371
- return /* @__PURE__ */ new Set([...roleToSupportedProps[role] ?? [], ...globalAriaProperties]);
372
- }
373
- var roleSupportedAriaProps_default = ruleCreator.createRule(typescriptLanguage, {
374
- about: {
375
- description: "Reports ARIA properties that are not supported by an element's role.",
376
- id: "roleSupportedAriaProps",
377
- presets: ["logical", "logicalStrict"]
378
- },
379
- messages: { unsupportedProp: {
380
- primary: "The `{{ prop }}` ARIA property is not supported by the `{{ role }}` role.",
381
- secondary: [
382
- "Elements with ARIA roles should only use ARIA properties that are supported by that role.",
383
- "Using unsupported ARIA properties can confuse assistive technologies.",
384
- "This is required for WCAG 4.1.2 compliance."
385
- ],
386
- suggestions: ["Remove the unsupported ARIA property or change the element's role."]
387
- } },
388
- setup(context) {
389
- function checkElement(node, { sourceFile }) {
390
- if (node.tagName.kind !== SyntaxKind.Identifier) return;
391
- const elementName = node.tagName.text.toLowerCase();
392
- const roleProperty = node.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "role");
393
- const role = roleProperty?.kind === SyntaxKind.JsxAttribute && roleProperty.initializer?.kind === SyntaxKind.StringLiteral ? roleProperty.initializer.text.toLowerCase() : implicitRoles[elementName];
394
- if (!role) return;
395
- const supportedProps = getSupportedPropsForRole(role);
396
- for (const property of node.attributes.properties) {
397
- if (property.kind !== SyntaxKind.JsxAttribute || property.name.kind !== SyntaxKind.Identifier) continue;
398
- const propertyName = property.name.text.toLowerCase();
399
- if (!propertyName.startsWith("aria-") || supportedProps.has(propertyName)) continue;
400
- context.report({
401
- data: {
402
- prop: propertyName,
403
- role
404
- },
405
- message: "unsupportedProp",
406
- range: getTSNodeRange(property.name, sourceFile)
407
- });
408
- }
409
- }
410
- return { visitors: {
411
- JsxOpeningElement: checkElement,
412
- JsxSelfClosingElement: checkElement
413
- } };
414
- }
415
- });
416
- //#endregion
417
- export { roleSupportedAriaProps_default as default };
@@ -1,9 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Reports ARIA roles that have semantic HTML element equivalents.";
3
- readonly id: "roleTags";
4
- readonly presets: readonly ["logical", "logicalStrict"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "preferSemanticElement", undefined>;
9
- export default _default;
@@ -1,70 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { SyntaxKind } from "typescript";
3
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
- //#region src/rules/roleTags.ts
5
- const roleToElement = {
6
- article: "article",
7
- banner: "header",
8
- button: "button",
9
- checkbox: "input[type='checkbox']",
10
- complementary: "aside",
11
- contentinfo: "footer",
12
- definition: "dd",
13
- dialog: "dialog",
14
- figure: "figure",
15
- form: "form",
16
- heading: "h1-h6",
17
- img: "img",
18
- link: "a",
19
- list: "ul/ol",
20
- listitem: "li",
21
- main: "main",
22
- navigation: "nav",
23
- radio: "input[type='radio']",
24
- region: "section",
25
- row: "tr",
26
- rowgroup: "tbody/tfoot/thead",
27
- table: "table",
28
- term: "dt",
29
- textbox: "input[type='text']/textarea"
30
- };
31
- var roleTags_default = ruleCreator.createRule(typescriptLanguage, {
32
- about: {
33
- description: "Reports ARIA roles that have semantic HTML element equivalents.",
34
- id: "roleTags",
35
- presets: ["logical", "logicalStrict"]
36
- },
37
- messages: { preferSemanticElement: {
38
- primary: "<{{ currentElement }}> with role='{{ role }}' is a less-accessible equivalent to <{{ semanticElement }}>.",
39
- secondary: [
40
- "Semantic HTML elements have built-in accessibility features.",
41
- "Using native elements is more maintainable than ARIA roles.",
42
- "Browsers provide better default behavior for semantic elements."
43
- ],
44
- suggestions: ["Replace with the semantic HTML element"]
45
- } },
46
- setup(context) {
47
- function checkRole(node, { sourceFile }) {
48
- if (node.tagName.kind !== SyntaxKind.Identifier || node.tagName.text.toLowerCase() !== node.tagName.text) return;
49
- const roleProperty = node.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "role");
50
- if (roleProperty?.kind !== SyntaxKind.JsxAttribute || roleProperty.initializer?.kind !== SyntaxKind.StringLiteral) return;
51
- const role = roleProperty.initializer.text;
52
- const semanticElement = roleToElement[role];
53
- if (semanticElement) context.report({
54
- data: {
55
- currentElement: node.tagName.text,
56
- role,
57
- semanticElement
58
- },
59
- message: "preferSemanticElement",
60
- range: getTSNodeRange(roleProperty, sourceFile)
61
- });
62
- }
63
- return { visitors: {
64
- JsxOpeningElement: checkRole,
65
- JsxSelfClosingElement: checkRole
66
- } };
67
- }
68
- });
69
- //#endregion
70
- export { roleTags_default as default };
@@ -1,2 +0,0 @@
1
- import { RuleCreator } from "@flint.fyi/core";
2
- export declare const ruleCreator: RuleCreator<"logical" | "logicalStrict" | "stylistic", import("@flint.fyi/core").RuleAbout<"logical" | "logicalStrict" | "stylistic">>;
@@ -1,13 +0,0 @@
1
- import { RuleCreator } from "@flint.fyi/core";
2
- //#region src/rules/ruleCreator.ts
3
- const ruleCreator = new RuleCreator({
4
- docs: (ruleId) => `https://flint.fyi/rules/jsx/${ruleId.toLowerCase()}`,
5
- pluginId: "jsx",
6
- presets: [
7
- "logical",
8
- "logicalStrict",
9
- "stylistic"
10
- ]
11
- });
12
- //#endregion
13
- export { ruleCreator };
@@ -1,9 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Reports scope props on non-th elements.";
3
- readonly id: "scopeProps";
4
- readonly presets: readonly ["logical", "logicalStrict"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "invalidScope", undefined>;
9
- export default _default;
@@ -1,39 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { SyntaxKind } from "typescript";
3
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
- //#region src/rules/scopeProps.ts
5
- var scopeProps_default = ruleCreator.createRule(typescriptLanguage, {
6
- about: {
7
- description: "Reports scope props on non-th elements.",
8
- id: "scopeProps",
9
- presets: ["logical", "logicalStrict"]
10
- },
11
- messages: { invalidScope: {
12
- primary: "The `scope` prop only has an effect on <th> elements.",
13
- secondary: [
14
- "The scope attribute defines whether a table header is a column header or row header.",
15
- "Using it on non-<th> elements has no semantic meaning and may confuse assistive technologies.",
16
- "This is required for WCAG 1.3.1 and 4.1.1 compliance."
17
- ],
18
- suggestions: ["Remove the scope prop from this element", "Change this element to a <th> element if it's a table header"]
19
- } },
20
- setup(context) {
21
- function checkElement(node, { sourceFile }) {
22
- if (node.tagName.kind !== SyntaxKind.Identifier || node.tagName.text.toLowerCase() === "th") return;
23
- const scopeProperty = node.attributes.properties.find((property) => {
24
- return property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text.toLowerCase() === "scope";
25
- });
26
- if (!scopeProperty) return;
27
- context.report({
28
- message: "invalidScope",
29
- range: getTSNodeRange(scopeProperty, sourceFile)
30
- });
31
- }
32
- return { visitors: {
33
- JsxOpeningElement: checkElement,
34
- JsxSelfClosingElement: checkElement
35
- } };
36
- }
37
- });
38
- //#endregion
39
- export { scopeProps_default as default };
@@ -1,9 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Reports static elements with event handlers that lack ARIA roles.";
3
- readonly id: "staticElementInteractions";
4
- readonly presets: readonly ["logical", "logicalStrict"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "missingRole", undefined>;
9
- export default _default;
@@ -1,58 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { SyntaxKind } from "typescript";
3
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
- //#region src/rules/staticElementInteractions.ts
5
- const interactiveHandlers = [
6
- "onClick",
7
- "onKeyDown",
8
- "onKeyPress",
9
- "onKeyUp",
10
- "onMouseDown",
11
- "onMouseUp"
12
- ];
13
- const interactiveElements = /* @__PURE__ */ new Set([
14
- "a",
15
- "button",
16
- "input",
17
- "select",
18
- "textarea"
19
- ]);
20
- var staticElementInteractions_default = ruleCreator.createRule(typescriptLanguage, {
21
- about: {
22
- description: "Reports static elements with event handlers that lack ARIA roles.",
23
- id: "staticElementInteractions",
24
- presets: ["logical", "logicalStrict"]
25
- },
26
- messages: { missingRole: {
27
- primary: "This static element that handles events is missing a role attribute.",
28
- secondary: [
29
- "Static HTML elements like <div> and <span> have no semantic meaning.",
30
- "Interactive handlers require a role to convey purpose to assistive technology and search engine crawlers.",
31
- "Required for WCAG 4.1.2 compliance."
32
- ],
33
- suggestions: ["Add a role attribute (e.g. `role='button'`)", "Use a semantic HTML element instead (e.g. `<button>`)"]
34
- } },
35
- setup(context) {
36
- function checkElement(node, { sourceFile }) {
37
- if (node.tagName.kind !== SyntaxKind.Identifier || node.tagName.text.toLowerCase() !== node.tagName.text) return;
38
- const elementName = node.tagName.text.toLowerCase();
39
- if (interactiveElements.has(elementName)) return;
40
- let hadInteractiveHandler = false;
41
- for (const property of node.attributes.properties) if (property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier) {
42
- if (property.name.text === "role") return;
43
- if (interactiveHandlers.includes(property.name.text)) hadInteractiveHandler = true;
44
- }
45
- if (!hadInteractiveHandler) return;
46
- context.report({
47
- message: "missingRole",
48
- range: getTSNodeRange(node.tagName, sourceFile)
49
- });
50
- }
51
- return { visitors: {
52
- JsxOpeningElement: checkElement,
53
- JsxSelfClosingElement: checkElement
54
- } };
55
- }
56
- });
57
- //#endregion
58
- export { staticElementInteractions_default as default };
@@ -1,9 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Reports <svg> elements without a <title> child element.";
3
- readonly id: "svgTitles";
4
- readonly presets: readonly ["logical", "logicalStrict"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "missingTitle", undefined>;
9
- export default _default;