@flint.fyi/jsx 0.3.3 → 0.5.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/{lib/plugin.d.ts → dist/index.d.mts} +45 -48
  2. package/dist/index.mjs +3235 -0
  3. package/package.json +10 -10
  4. package/lib/index.d.ts +0 -2
  5. package/lib/index.js +0 -2
  6. package/lib/plugin.js +0 -106
  7. package/lib/rules/accessKeys.d.ts +0 -10
  8. package/lib/rules/accessKeys.js +0 -32
  9. package/lib/rules/altTexts.d.ts +0 -10
  10. package/lib/rules/altTexts.js +0 -78
  11. package/lib/rules/anchorAmbiguousText.d.ts +0 -10
  12. package/lib/rules/anchorAmbiguousText.js +0 -57
  13. package/lib/rules/anchorContent.d.ts +0 -10
  14. package/lib/rules/anchorContent.js +0 -57
  15. package/lib/rules/anchorValidity.d.ts +0 -10
  16. package/lib/rules/anchorValidity.js +0 -79
  17. package/lib/rules/ariaActiveDescendantTabIndex.d.ts +0 -10
  18. package/lib/rules/ariaActiveDescendantTabIndex.js +0 -53
  19. package/lib/rules/ariaHiddenFocusables.d.ts +0 -10
  20. package/lib/rules/ariaHiddenFocusables.js +0 -68
  21. package/lib/rules/ariaPropTypes.d.ts +0 -10
  22. package/lib/rules/ariaPropTypes.js +0 -224
  23. package/lib/rules/ariaProps.d.ts +0 -10
  24. package/lib/rules/ariaProps.js +0 -92
  25. package/lib/rules/ariaRoleValidity.d.ts +0 -10
  26. package/lib/rules/ariaRoleValidity.js +0 -113
  27. package/lib/rules/ariaUnsupportedElements.d.ts +0 -10
  28. package/lib/rules/ariaUnsupportedElements.js +0 -88
  29. package/lib/rules/autoFocusProps.d.ts +0 -10
  30. package/lib/rules/autoFocusProps.js +0 -44
  31. package/lib/rules/autocomplete.d.ts +0 -10
  32. package/lib/rules/autocomplete.js +0 -126
  33. package/lib/rules/booleanValues.d.ts +0 -10
  34. package/lib/rules/booleanValues.js +0 -33
  35. package/lib/rules/bracedStatements.d.ts +0 -10
  36. package/lib/rules/bracedStatements.js +0 -33
  37. package/lib/rules/buttonTypes.d.ts +0 -10
  38. package/lib/rules/buttonTypes.js +0 -76
  39. package/lib/rules/childrenProps.d.ts +0 -10
  40. package/lib/rules/childrenProps.js +0 -39
  41. package/lib/rules/clickEventKeyEvents.d.ts +0 -10
  42. package/lib/rules/clickEventKeyEvents.js +0 -56
  43. package/lib/rules/commentTextNodes.d.ts +0 -10
  44. package/lib/rules/commentTextNodes.js +0 -32
  45. package/lib/rules/distractingElements.d.ts +0 -10
  46. package/lib/rules/distractingElements.js +0 -40
  47. package/lib/rules/elementChildrenValidity.d.ts +0 -10
  48. package/lib/rules/elementChildrenValidity.js +0 -54
  49. package/lib/rules/headingContents.d.ts +0 -10
  50. package/lib/rules/headingContents.js +0 -54
  51. package/lib/rules/htmlLangs.d.ts +0 -10
  52. package/lib/rules/htmlLangs.js +0 -32
  53. package/lib/rules/iframeTitles.d.ts +0 -10
  54. package/lib/rules/iframeTitles.js +0 -63
  55. package/lib/rules/interactiveElementRoles.d.ts +0 -10
  56. package/lib/rules/interactiveElementRoles.js +0 -93
  57. package/lib/rules/interactiveElementsFocusable.d.ts +0 -10
  58. package/lib/rules/interactiveElementsFocusable.js +0 -172
  59. package/lib/rules/labelAssociatedControls.d.ts +0 -10
  60. package/lib/rules/labelAssociatedControls.js +0 -70
  61. package/lib/rules/langValidity.d.ts +0 -10
  62. package/lib/rules/langValidity.js +0 -43
  63. package/lib/rules/mediaCaptions.d.ts +0 -10
  64. package/lib/rules/mediaCaptions.js +0 -52
  65. package/lib/rules/mouseEventKeyEvents.d.ts +0 -10
  66. package/lib/rules/mouseEventKeyEvents.js +0 -49
  67. package/lib/rules/nonInteractiveElementInteractions.d.ts +0 -10
  68. package/lib/rules/nonInteractiveElementInteractions.js +0 -95
  69. package/lib/rules/nonInteractiveElementRoles.d.ts +0 -10
  70. package/lib/rules/nonInteractiveElementRoles.js +0 -118
  71. package/lib/rules/nonInteractiveElementTabIndexes.d.ts +0 -10
  72. package/lib/rules/nonInteractiveElementTabIndexes.js +0 -109
  73. package/lib/rules/propDuplicates.d.ts +0 -10
  74. package/lib/rules/propDuplicates.js +0 -39
  75. package/lib/rules/roleRedundancies.d.ts +0 -10
  76. package/lib/rules/roleRedundancies.js +0 -76
  77. package/lib/rules/roleRequiredAriaProps.d.ts +0 -10
  78. package/lib/rules/roleRequiredAriaProps.js +0 -78
  79. package/lib/rules/roleSupportedAriaProps.d.ts +0 -10
  80. package/lib/rules/roleSupportedAriaProps.js +0 -419
  81. package/lib/rules/roleTags.d.ts +0 -10
  82. package/lib/rules/roleTags.js +0 -72
  83. package/lib/rules/ruleCreator.d.ts +0 -3
  84. package/lib/rules/ruleCreator.js +0 -15
  85. package/lib/rules/scopeProps.d.ts +0 -10
  86. package/lib/rules/scopeProps.js +0 -41
  87. package/lib/rules/staticElementInteractions.d.ts +0 -10
  88. package/lib/rules/staticElementInteractions.js +0 -60
  89. package/lib/rules/svgTitles.d.ts +0 -10
  90. package/lib/rules/svgTitles.js +0 -59
  91. package/lib/rules/tabIndexPositiveValues.d.ts +0 -10
  92. package/lib/rules/tabIndexPositiveValues.js +0 -45
  93. package/lib/rules/unescapedEntities.d.ts +0 -10
  94. package/lib/rules/unescapedEntities.js +0 -70
  95. package/lib/rules/unnecessaryFragments.d.ts +0 -10
  96. package/lib/rules/unnecessaryFragments.js +0 -51
@@ -1,59 +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/svgTitles.ts
5
- var svgTitles_default = ruleCreator.createRule(typescriptLanguage, {
6
- about: {
7
- description: "Reports <svg> elements without a <title> child element.",
8
- id: "svgTitles",
9
- presets: ["logical", "logicalStrict"]
10
- },
11
- messages: { missingTitle: {
12
- primary: "This <svg> element is missing a <title> child element.",
13
- secondary: [
14
- "SVG elements without a title are not accessible to screen readers.",
15
- "The <title> element provides a text description of the SVG's content.",
16
- "This is required for WCAG 1.1.1 compliance."
17
- ],
18
- suggestions: ["Add a <title> child element with descriptive text", "Use aria-label or aria-labelledby as an alternative"]
19
- } },
20
- setup(context) {
21
- function hasValidAriaLabel(attributes) {
22
- return attributes.properties.some((property) => {
23
- if (property.kind !== SyntaxKind.JsxAttribute || property.name.kind !== SyntaxKind.Identifier || property.name.text !== "aria-label" && property.name.text !== "aria-labelledby") return false;
24
- if (!property.initializer) return false;
25
- if (property.initializer.kind === SyntaxKind.JsxExpression) {
26
- const { expression } = property.initializer;
27
- if (!expression) return false;
28
- if (expression.kind === SyntaxKind.StringLiteral || expression.kind === SyntaxKind.NoSubstitutionTemplateLiteral) return expression.text !== "";
29
- if (expression.kind === SyntaxKind.Identifier) return expression.text !== "undefined";
30
- }
31
- if (property.initializer.kind === SyntaxKind.StringLiteral) return property.initializer.text !== "";
32
- return false;
33
- });
34
- }
35
- function checkElement(node, { sourceFile }) {
36
- const tagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
37
- if (tagName.kind !== SyntaxKind.Identifier || tagName.text.toLowerCase() !== "svg") return;
38
- if (hasValidAriaLabel(node.kind === SyntaxKind.JsxElement ? node.openingElement.attributes : node.attributes)) return;
39
- if (node.kind === SyntaxKind.JsxElement && node.children.some(isTitleElement)) return;
40
- context.report({
41
- message: "missingTitle",
42
- range: getTSNodeRange(tagName, sourceFile)
43
- });
44
- }
45
- return { visitors: {
46
- JsxElement: checkElement,
47
- JsxSelfClosingElement: checkElement
48
- } };
49
- }
50
- });
51
- function isTitleElement(node) {
52
- if (node.kind !== SyntaxKind.JsxElement && node.kind !== SyntaxKind.JsxSelfClosingElement) return false;
53
- const childTagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
54
- return childTagName.kind === SyntaxKind.Identifier && childTagName.text === "title";
55
- }
56
- //#endregion
57
- export { svgTitles_default as default };
58
-
59
- //# sourceMappingURL=svgTitles.js.map
@@ -1,10 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Reports positive tabIndex values.";
3
- readonly id: "tabIndexPositiveValues";
4
- readonly presets: readonly ["logical", "logicalStrict"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "noPositiveTabIndex", undefined>;
9
- export default _default;
10
- //# sourceMappingURL=tabIndexPositiveValues.d.ts.map
@@ -1,45 +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/tabIndexPositiveValues.ts
5
- var tabIndexPositiveValues_default = ruleCreator.createRule(typescriptLanguage, {
6
- about: {
7
- description: "Reports positive tabIndex values.",
8
- id: "tabIndexPositiveValues",
9
- presets: ["logical", "logicalStrict"]
10
- },
11
- messages: { noPositiveTabIndex: {
12
- primary: "Positive `tabIndex` values disrupt tab order and make keyboard navigation unpredictable.",
13
- secondary: [
14
- "Injecting elements on top of the default tab order with positive `tabIndex` values changes from the intuitive order for users.",
15
- "Use tabIndex=\"0\" to include elements in the natural tab order, or tabIndex=\"-1\" to make them programmatically focusable.",
16
- "This is required for WCAG 2.4.3 compliance."
17
- ],
18
- suggestions: [
19
- "Use tabIndex=\"0\" for elements that should be in the tab order",
20
- "Use tabIndex=\"-1\" for elements that should be programmatically focusable",
21
- "Remove tabIndex to use the default tab order"
22
- ]
23
- } },
24
- setup(context) {
25
- return { visitors: { JsxAttribute(node, { sourceFile }) {
26
- if (node.name.kind !== SyntaxKind.Identifier || node.name.text.toLowerCase() !== "tabindex" || !node.initializer) return;
27
- const value = getInitializerValue(node.initializer);
28
- if (value !== void 0 && value > 0) context.report({
29
- message: "noPositiveTabIndex",
30
- range: getTSNodeRange(node, sourceFile)
31
- });
32
- } } };
33
- }
34
- });
35
- function getInitializerValue(initializer) {
36
- if (initializer.kind === SyntaxKind.StringLiteral) {
37
- const parsed = Number(initializer.text);
38
- return Number.isNaN(parsed) ? void 0 : parsed;
39
- }
40
- if (initializer.kind === SyntaxKind.JsxExpression) return initializer.expression?.kind === SyntaxKind.NumericLiteral ? Number(initializer.expression.text) : void 0;
41
- }
42
- //#endregion
43
- export { tabIndexPositiveValues_default as default };
44
-
45
- //# sourceMappingURL=tabIndexPositiveValues.js.map
@@ -1,10 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Disallow unescaped HTML entities in JSX text that may cause rendering issues.";
3
- readonly id: "unescapedEntities";
4
- readonly presets: readonly ["stylistic"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "unescapedEntity", undefined>;
9
- export default _default;
10
- //# sourceMappingURL=unescapedEntities.d.ts.map
@@ -1,70 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { typescriptLanguage } from "@flint.fyi/typescript-language";
3
- //#region src/rules/unescapedEntities.ts
4
- const problematicEntities = [
5
- {
6
- entity: "\"",
7
- toBrace: "{\"\\\"\"}",
8
- toHTML: "&quot;"
9
- },
10
- {
11
- entity: "'",
12
- toBrace: "{\"'\"}",
13
- toHTML: "&#39;"
14
- },
15
- {
16
- entity: ">",
17
- toBrace: "{'>'}",
18
- toHTML: "&gt;"
19
- },
20
- {
21
- entity: "}",
22
- toBrace: "{'}'}",
23
- toHTML: "&#125;"
24
- }
25
- ];
26
- var unescapedEntities_default = ruleCreator.createRule(typescriptLanguage, {
27
- about: {
28
- description: "Disallow unescaped HTML entities in JSX text that may cause rendering issues.",
29
- id: "unescapedEntities",
30
- presets: ["stylistic"]
31
- },
32
- messages: { unescapedEntity: {
33
- primary: "This unescaped entity `{{ entity }}` may not render properly.",
34
- secondary: ["Certain characters like >, <, \", ', {, and } can cause issues in JSX when not properly escaped.", "React may not render these characters correctly or they might break JSX syntax."],
35
- suggestions: ["Use HTML entities: {{ html }}", "Wrap in JSX expression: {{ brace }}"]
36
- } },
37
- setup(context) {
38
- return { visitors: { JsxText(node, { sourceFile }) {
39
- const nodeStart = node.getStart(sourceFile);
40
- const reports = [];
41
- for (const { entity, toBrace, toHTML } of problematicEntities) {
42
- let index = 0;
43
- while ((index = node.text.indexOf(entity, index)) !== -1) {
44
- reports.push({
45
- begin: nodeStart + index,
46
- data: {
47
- brace: toBrace,
48
- entity,
49
- html: toHTML
50
- },
51
- end: nodeStart + index + entity.length
52
- });
53
- index += entity.length;
54
- }
55
- }
56
- for (const report of reports.toSorted((a, b) => a.begin - b.begin)) context.report({
57
- data: report.data,
58
- message: "unescapedEntity",
59
- range: {
60
- begin: report.begin,
61
- end: report.end
62
- }
63
- });
64
- } } };
65
- }
66
- });
67
- //#endregion
68
- export { unescapedEntities_default as default };
69
-
70
- //# sourceMappingURL=unescapedEntities.js.map
@@ -1,10 +0,0 @@
1
- declare const _default: import("@flint.fyi/core").Rule<{
2
- readonly description: "Disallow unnecessary JSX fragments that wrap a single child or have no children.";
3
- readonly id: "unnecessaryFragments";
4
- readonly presets: readonly ["stylistic"];
5
- } & {
6
- readonly pluginId: string;
7
- readonly url: string;
8
- }, "unnecessaryFragment", undefined>;
9
- export default _default;
10
- //# sourceMappingURL=unnecessaryFragments.d.ts.map
@@ -1,51 +0,0 @@
1
- import { ruleCreator } from "./ruleCreator.js";
2
- import { SyntaxKind } from "typescript";
3
- import { typescriptLanguage } from "@flint.fyi/typescript-language";
4
- //#region src/rules/unnecessaryFragments.ts
5
- var unnecessaryFragments_default = ruleCreator.createRule(typescriptLanguage, {
6
- about: {
7
- description: "Disallow unnecessary JSX fragments that wrap a single child or have no children.",
8
- id: "unnecessaryFragments",
9
- presets: ["stylistic"]
10
- },
11
- messages: { unnecessaryFragment: {
12
- primary: "Unnecessary fragment wrapping {{ childType }}.",
13
- secondary: [
14
- "Fragments are only needed when rendering multiple adjacent elements.",
15
- "A fragment wrapping a single child or no children adds unnecessary complexity.",
16
- "Remove the fragment and use the child directly, or remove empty fragments entirely."
17
- ],
18
- suggestions: ["Remove the fragment wrapper and use the child element directly."]
19
- } },
20
- setup(context) {
21
- function checkNodeChildren(node, range) {
22
- const children = node.children.filter((child) => child.kind !== SyntaxKind.JsxText || !!child.text.trim().length);
23
- let childType;
24
- if (!children.length) childType = "no children";
25
- else if (children.length === 1) childType = "a single child";
26
- if (childType) context.report({
27
- data: { childType },
28
- message: "unnecessaryFragment",
29
- range
30
- });
31
- }
32
- return { visitors: {
33
- JsxElement(node, { sourceFile }) {
34
- if (node.openingElement.tagName.kind === SyntaxKind.Identifier && !node.openingElement.attributes.properties.length && node.openingElement.tagName.text === "Fragment") checkNodeChildren(node, {
35
- begin: node.openingElement.getStart(sourceFile),
36
- end: node.closingElement.getEnd()
37
- });
38
- },
39
- JsxFragment(node, { sourceFile }) {
40
- checkNodeChildren(node, {
41
- begin: node.openingFragment.getStart(sourceFile),
42
- end: node.closingFragment.getEnd()
43
- });
44
- }
45
- } };
46
- }
47
- });
48
- //#endregion
49
- export { unnecessaryFragments_default as default };
50
-
51
- //# sourceMappingURL=unnecessaryFragments.js.map