@flint.fyi/jsx 0.0.0 → 0.3.2
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.
- package/LICENSE.md +20 -0
- package/lib/index.js +1 -2
- package/lib/plugin.d.ts +309 -1
- package/lib/plugin.js +12 -6
- package/lib/rules/accessKeys.d.ts +8 -1
- package/lib/rules/accessKeys.js +2 -3
- package/lib/rules/altTexts.d.ts +8 -1
- package/lib/rules/altTexts.js +6 -16
- package/lib/rules/anchorAmbiguousText.d.ts +8 -1
- package/lib/rules/anchorAmbiguousText.js +2 -5
- package/lib/rules/anchorContent.d.ts +8 -1
- package/lib/rules/anchorContent.js +2 -3
- package/lib/rules/anchorValidity.d.ts +8 -1
- package/lib/rules/anchorValidity.js +4 -5
- package/lib/rules/ariaActiveDescendantTabIndex.d.ts +8 -1
- package/lib/rules/ariaActiveDescendantTabIndex.js +4 -7
- package/lib/rules/ariaHiddenFocusables.d.ts +8 -1
- package/lib/rules/ariaHiddenFocusables.js +7 -9
- package/lib/rules/ariaPropTypes.d.ts +8 -1
- package/lib/rules/ariaPropTypes.js +6 -9
- package/lib/rules/ariaProps.d.ts +8 -1
- package/lib/rules/ariaProps.js +2 -3
- package/lib/rules/ariaRoleValidity.d.ts +8 -1
- package/lib/rules/ariaRoleValidity.js +3 -4
- package/lib/rules/ariaUnsupportedElements.d.ts +8 -1
- package/lib/rules/ariaUnsupportedElements.js +2 -3
- package/lib/rules/autoFocusProps.d.ts +8 -1
- package/lib/rules/autoFocusProps.js +4 -6
- package/lib/rules/autocomplete.d.ts +8 -1
- package/lib/rules/autocomplete.js +3 -4
- package/lib/rules/booleanValues.d.ts +8 -1
- package/lib/rules/booleanValues.js +2 -3
- package/lib/rules/bracedStatements.d.ts +8 -1
- package/lib/rules/bracedStatements.js +1 -2
- package/lib/rules/buttonTypes.d.ts +8 -1
- package/lib/rules/buttonTypes.js +3 -4
- package/lib/rules/childrenProps.d.ts +8 -1
- package/lib/rules/childrenProps.js +1 -2
- package/lib/rules/clickEventKeyEvents.d.ts +8 -1
- package/lib/rules/clickEventKeyEvents.js +2 -3
- package/lib/rules/commentTextNodes.d.ts +8 -1
- package/lib/rules/commentTextNodes.js +2 -3
- package/lib/rules/distractingElements.d.ts +8 -1
- package/lib/rules/distractingElements.js +2 -3
- package/lib/rules/elementChildrenValidity.d.ts +8 -1
- package/lib/rules/elementChildrenValidity.js +1 -2
- package/lib/rules/headingContents.d.ts +8 -1
- package/lib/rules/headingContents.js +5 -6
- package/lib/rules/htmlLangs.d.ts +8 -1
- package/lib/rules/htmlLangs.js +2 -3
- package/lib/rules/iframeTitles.d.ts +8 -1
- package/lib/rules/iframeTitles.js +3 -4
- package/lib/rules/interactiveElementRoles.d.ts +8 -1
- package/lib/rules/interactiveElementRoles.js +3 -4
- package/lib/rules/interactiveElementsFocusable.d.ts +8 -1
- package/lib/rules/interactiveElementsFocusable.js +7 -8
- package/lib/rules/labelAssociatedControls.d.ts +8 -1
- package/lib/rules/labelAssociatedControls.js +6 -7
- package/lib/rules/langValidity.d.ts +8 -1
- package/lib/rules/langValidity.js +1 -2
- package/lib/rules/mediaCaptions.d.ts +8 -1
- package/lib/rules/mediaCaptions.js +12 -13
- package/lib/rules/mouseEventKeyEvents.d.ts +8 -1
- package/lib/rules/mouseEventKeyEvents.js +2 -3
- package/lib/rules/nonInteractiveElementInteractions.d.ts +8 -1
- package/lib/rules/nonInteractiveElementInteractions.js +3 -4
- package/lib/rules/nonInteractiveElementRoles.d.ts +8 -1
- package/lib/rules/nonInteractiveElementRoles.js +3 -4
- package/lib/rules/nonInteractiveElementTabIndexes.d.ts +8 -1
- package/lib/rules/nonInteractiveElementTabIndexes.js +7 -8
- package/lib/rules/propDuplicates.d.ts +8 -1
- package/lib/rules/propDuplicates.js +2 -3
- package/lib/rules/roleRedundancies.d.ts +8 -1
- package/lib/rules/roleRedundancies.js +3 -4
- package/lib/rules/roleRequiredAriaProps.d.ts +8 -1
- package/lib/rules/roleRequiredAriaProps.js +3 -4
- package/lib/rules/roleSupportedAriaProps.d.ts +8 -1
- package/lib/rules/roleSupportedAriaProps.js +3 -4
- package/lib/rules/roleTags.d.ts +8 -1
- package/lib/rules/roleTags.js +2 -3
- package/lib/rules/ruleCreator.d.ts +1 -1
- package/lib/rules/ruleCreator.js +1 -2
- package/lib/rules/scopeProps.d.ts +8 -1
- package/lib/rules/scopeProps.js +2 -3
- package/lib/rules/staticElementInteractions.d.ts +8 -1
- package/lib/rules/staticElementInteractions.js +2 -3
- package/lib/rules/svgTitles.d.ts +8 -1
- package/lib/rules/svgTitles.js +5 -6
- package/lib/rules/tabIndexPositiveValues.d.ts +8 -1
- package/lib/rules/tabIndexPositiveValues.js +4 -5
- package/lib/rules/unescapedEntities.d.ts +8 -1
- package/lib/rules/unescapedEntities.js +1 -2
- package/lib/rules/unnecessaryFragments.d.ts +8 -1
- package/lib/rules/unnecessaryFragments.js +3 -4
- package/package.json +46 -51
- package/lib/jsx.d.ts +0 -8
- package/lib/jsx.js +0 -100
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/distractingElements.ts
|
|
6
5
|
const distractingElements = new Set(["blink", "marquee"]);
|
|
7
6
|
var distractingElements_default = ruleCreator.createRule(typescriptLanguage, {
|
|
8
7
|
about: {
|
|
9
8
|
description: "Reports distracting elements like <marquee> and <blink>.",
|
|
10
9
|
id: "distractingElements",
|
|
11
|
-
presets: ["logical"]
|
|
10
|
+
presets: ["logical", "logicalStrict"]
|
|
12
11
|
},
|
|
13
12
|
messages: { noDistractingElement: {
|
|
14
13
|
primary: "The <{{ element }}> element is distracting and deprecated.",
|
|
@@ -35,7 +34,7 @@ var distractingElements_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
35
34
|
} };
|
|
36
35
|
}
|
|
37
36
|
});
|
|
38
|
-
|
|
39
37
|
//#endregion
|
|
40
38
|
export { distractingElements_default as default };
|
|
39
|
+
|
|
41
40
|
//# sourceMappingURL=distractingElements.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports void DOM elements that have children, which is invalid HTML.";
|
|
3
|
+
readonly id: "elementChildrenValidity";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "voidElementWithChildren", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=elementChildrenValidity.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/elementChildrenValidity.ts
|
|
6
5
|
const voidElements = new Set([
|
|
7
6
|
"area",
|
|
@@ -49,7 +48,7 @@ var elementChildrenValidity_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
49
48
|
} } };
|
|
50
49
|
}
|
|
51
50
|
});
|
|
52
|
-
|
|
53
51
|
//#endregion
|
|
54
52
|
export { elementChildrenValidity_default as default };
|
|
53
|
+
|
|
55
54
|
//# sourceMappingURL=elementChildrenValidity.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports heading elements without accessible content.";
|
|
3
|
+
readonly id: "headingContents";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "emptyHeading", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=headingContents.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/headingContents.ts
|
|
6
5
|
const headingElements = new Set([
|
|
7
6
|
"h1",
|
|
@@ -15,7 +14,7 @@ var headingContents_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
15
14
|
about: {
|
|
16
15
|
description: "Reports heading elements without accessible content.",
|
|
17
16
|
id: "headingContents",
|
|
18
|
-
presets: ["logical"]
|
|
17
|
+
presets: ["logical", "logicalStrict"]
|
|
19
18
|
},
|
|
20
19
|
messages: { emptyHeading: {
|
|
21
20
|
primary: "This heading element is missing accessible content.",
|
|
@@ -28,14 +27,14 @@ var headingContents_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
28
27
|
} },
|
|
29
28
|
setup(context) {
|
|
30
29
|
function checkHeading(node, { sourceFile }) {
|
|
31
|
-
const tagName = node.kind
|
|
30
|
+
const tagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
|
|
32
31
|
if (tagName.kind !== SyntaxKind.Identifier || !headingElements.has(tagName.text.toLowerCase())) return;
|
|
33
|
-
if ((node.kind
|
|
32
|
+
if ((node.kind === SyntaxKind.JsxElement ? node.openingElement.attributes : node.attributes).properties.some((property) => {
|
|
34
33
|
if (property.kind !== SyntaxKind.JsxAttribute || property.name.kind !== SyntaxKind.Identifier) return false;
|
|
35
34
|
return (property.name.text === "aria-label" || property.name.text === "aria-labelledby") && !!property.initializer;
|
|
36
35
|
})) return;
|
|
37
36
|
if (node.kind === SyntaxKind.JsxElement && node.children.some((child) => {
|
|
38
|
-
if (child.kind === SyntaxKind.JsxText) return child.text.trim().length
|
|
37
|
+
if (child.kind === SyntaxKind.JsxText) return !!child.text.trim().length;
|
|
39
38
|
return child.kind === SyntaxKind.JsxElement || child.kind === SyntaxKind.JsxSelfClosingElement || child.kind === SyntaxKind.JsxExpression;
|
|
40
39
|
})) return;
|
|
41
40
|
context.report({
|
|
@@ -49,7 +48,7 @@ var headingContents_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
49
48
|
} };
|
|
50
49
|
}
|
|
51
50
|
});
|
|
52
|
-
|
|
53
51
|
//#endregion
|
|
54
52
|
export { headingContents_default as default };
|
|
53
|
+
|
|
55
54
|
//# sourceMappingURL=headingContents.js.map
|
package/lib/rules/htmlLangs.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports <html> elements without a lang prop.";
|
|
3
|
+
readonly id: "htmlLangs";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "missingLang", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=htmlLangs.d.ts.map
|
package/lib/rules/htmlLangs.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/htmlLangs.ts
|
|
6
5
|
var htmlLangs_default = ruleCreator.createRule(typescriptLanguage, {
|
|
7
6
|
about: {
|
|
8
7
|
description: "Reports <html> elements without a lang prop.",
|
|
9
8
|
id: "htmlLangs",
|
|
10
|
-
presets: ["logical"]
|
|
9
|
+
presets: ["logical", "logicalStrict"]
|
|
11
10
|
},
|
|
12
11
|
messages: { missingLang: {
|
|
13
12
|
primary: "This <html> element is missing a `lang` prop.",
|
|
@@ -27,7 +26,7 @@ var htmlLangs_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
27
26
|
} } };
|
|
28
27
|
}
|
|
29
28
|
});
|
|
30
|
-
|
|
31
29
|
//#endregion
|
|
32
30
|
export { htmlLangs_default as default };
|
|
31
|
+
|
|
33
32
|
//# sourceMappingURL=htmlLangs.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports <iframe> elements without a title prop.";
|
|
3
|
+
readonly id: "iframeTitles";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "missingTitle", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=iframeTitles.d.ts.map
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/iframeTitles.ts
|
|
6
5
|
var iframeTitles_default = ruleCreator.createRule(typescriptLanguage, {
|
|
7
6
|
about: {
|
|
8
7
|
description: "Reports <iframe> elements without a title prop.",
|
|
9
8
|
id: "iframeTitles",
|
|
10
|
-
presets: ["logical"]
|
|
9
|
+
presets: ["logical", "logicalStrict"]
|
|
11
10
|
},
|
|
12
11
|
messages: { missingTitle: {
|
|
13
12
|
primary: "This <iframe> element is missing a `title` prop.",
|
|
@@ -24,7 +23,7 @@ var iframeTitles_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
24
23
|
const titleAttribute = attributes.properties.find((property) => {
|
|
25
24
|
return property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text.toLowerCase() === "title";
|
|
26
25
|
});
|
|
27
|
-
if (
|
|
26
|
+
if (titleAttribute?.kind !== SyntaxKind.JsxAttribute) {
|
|
28
27
|
context.report({
|
|
29
28
|
message: "missingTitle",
|
|
30
29
|
range: getTSNodeRange(tagName, sourceFile)
|
|
@@ -58,7 +57,7 @@ var iframeTitles_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
58
57
|
} };
|
|
59
58
|
}
|
|
60
59
|
});
|
|
61
|
-
|
|
62
60
|
//#endregion
|
|
63
61
|
export { iframeTitles_default as default };
|
|
62
|
+
|
|
64
63
|
//# sourceMappingURL=iframeTitles.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports interactive elements with non-interactive ARIA roles.";
|
|
3
|
+
readonly id: "interactiveElementRoles";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "invalidRole", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=interactiveElementRoles.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/interactiveElementRoles.ts
|
|
6
5
|
const interactiveElements = new Set([
|
|
7
6
|
"a",
|
|
@@ -54,7 +53,7 @@ var interactiveElementRoles_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
54
53
|
about: {
|
|
55
54
|
description: "Reports interactive elements with non-interactive ARIA roles.",
|
|
56
55
|
id: "interactiveElementRoles",
|
|
57
|
-
presets: ["logical"]
|
|
56
|
+
presets: ["logical", "logicalStrict"]
|
|
58
57
|
},
|
|
59
58
|
messages: { invalidRole: {
|
|
60
59
|
primary: "Interactive element <{{ element }}> should not have the non-interactive role `'{{ role }}'`.",
|
|
@@ -71,7 +70,7 @@ var interactiveElementRoles_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
71
70
|
const elementName = element.tagName.text.toLowerCase();
|
|
72
71
|
if (!interactiveElements.has(elementName)) return;
|
|
73
72
|
const roleProperty = element.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "role");
|
|
74
|
-
if (
|
|
73
|
+
if (roleProperty?.kind !== SyntaxKind.JsxAttribute || roleProperty.initializer?.kind !== SyntaxKind.StringLiteral) return;
|
|
75
74
|
const role = roleProperty.initializer.text;
|
|
76
75
|
if (nonInteractiveRoles.has(role)) context.report({
|
|
77
76
|
data: {
|
|
@@ -88,7 +87,7 @@ var interactiveElementRoles_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
88
87
|
} };
|
|
89
88
|
}
|
|
90
89
|
});
|
|
91
|
-
|
|
92
90
|
//#endregion
|
|
93
91
|
export { interactiveElementRoles_default as default };
|
|
92
|
+
|
|
94
93
|
//# sourceMappingURL=interactiveElementRoles.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports interactive elements that are not focusable via keyboard.";
|
|
3
|
+
readonly id: "interactiveElementsFocusable";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "notFocusable", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=interactiveElementsFocusable.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/interactiveElementsFocusable.ts
|
|
6
5
|
const interactiveHandlers = [
|
|
7
6
|
"onClick",
|
|
@@ -95,7 +94,7 @@ var interactiveElementsFocusable_default = ruleCreator.createRule(typescriptLang
|
|
|
95
94
|
about: {
|
|
96
95
|
description: "Reports interactive elements that are not focusable via keyboard.",
|
|
97
96
|
id: "interactiveElementsFocusable",
|
|
98
|
-
presets: ["logical"]
|
|
97
|
+
presets: ["logical", "logicalStrict"]
|
|
99
98
|
},
|
|
100
99
|
messages: { notFocusable: {
|
|
101
100
|
primary: "The '{{ role }}' role makes this element interactive, so it should also be focusable.",
|
|
@@ -116,17 +115,17 @@ var interactiveElementsFocusable_default = ruleCreator.createRule(typescriptLang
|
|
|
116
115
|
if (!tabIndex?.initializer) return;
|
|
117
116
|
if (tabIndex.initializer.kind === SyntaxKind.JsxExpression) {
|
|
118
117
|
const expression = tabIndex.initializer.expression;
|
|
119
|
-
if (expression
|
|
118
|
+
if (expression?.kind === SyntaxKind.NumericLiteral) return Number(expression.text);
|
|
120
119
|
}
|
|
121
120
|
if (tabIndex.initializer.kind === SyntaxKind.StringLiteral) return Number(tabIndex.initializer.text);
|
|
122
121
|
}
|
|
123
122
|
function getRoleValue(node) {
|
|
124
123
|
const role = node.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "role");
|
|
125
|
-
if (role
|
|
124
|
+
if (role?.kind === SyntaxKind.JsxAttribute && role.initializer?.kind === SyntaxKind.StringLiteral) return role.initializer.text;
|
|
126
125
|
}
|
|
127
126
|
function isAriaHidden(node) {
|
|
128
127
|
const ariaHidden = node.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "aria-hidden");
|
|
129
|
-
if (
|
|
128
|
+
if (ariaHidden?.kind !== SyntaxKind.JsxAttribute || !ariaHidden.initializer) return false;
|
|
130
129
|
if (ariaHidden.initializer.kind === SyntaxKind.JsxExpression) return ariaHidden.initializer.expression?.kind === SyntaxKind.TrueKeyword;
|
|
131
130
|
if (ariaHidden.initializer.kind === SyntaxKind.StringLiteral) return ariaHidden.initializer.text === "true";
|
|
132
131
|
return false;
|
|
@@ -136,7 +135,7 @@ var interactiveElementsFocusable_default = ruleCreator.createRule(typescriptLang
|
|
|
136
135
|
}
|
|
137
136
|
function isDisabled(node) {
|
|
138
137
|
const disabledProperty = node.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "disabled");
|
|
139
|
-
if (
|
|
138
|
+
if (disabledProperty?.kind !== SyntaxKind.JsxAttribute) return false;
|
|
140
139
|
if (!disabledProperty.initializer) return true;
|
|
141
140
|
if (disabledProperty.initializer.kind === SyntaxKind.JsxExpression) return disabledProperty.initializer.expression?.kind === SyntaxKind.TrueKeyword;
|
|
142
141
|
return false;
|
|
@@ -157,7 +156,7 @@ var interactiveElementsFocusable_default = ruleCreator.createRule(typescriptLang
|
|
|
157
156
|
context.report({
|
|
158
157
|
data: { role: displayRole },
|
|
159
158
|
message: "notFocusable",
|
|
160
|
-
range: getTSNodeRange(roleProperty
|
|
159
|
+
range: getTSNodeRange(roleProperty?.kind === SyntaxKind.JsxAttribute ? roleProperty : node.tagName, sourceFile)
|
|
161
160
|
});
|
|
162
161
|
}
|
|
163
162
|
}
|
|
@@ -167,7 +166,7 @@ var interactiveElementsFocusable_default = ruleCreator.createRule(typescriptLang
|
|
|
167
166
|
} };
|
|
168
167
|
}
|
|
169
168
|
});
|
|
170
|
-
|
|
171
169
|
//#endregion
|
|
172
170
|
export { interactiveElementsFocusable_default as default };
|
|
171
|
+
|
|
173
172
|
//# sourceMappingURL=interactiveElementsFocusable.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports <label> elements without an associated control element.";
|
|
3
|
+
readonly id: "labelAssociatedControls";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "missingAssociatedControl", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=labelAssociatedControls.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/labelAssociatedControls.ts
|
|
6
5
|
const controlElements = new Set([
|
|
7
6
|
"input",
|
|
@@ -15,7 +14,7 @@ var labelAssociatedControls_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
15
14
|
about: {
|
|
16
15
|
description: "Reports <label> elements without an associated control element.",
|
|
17
16
|
id: "labelAssociatedControls",
|
|
18
|
-
presets: ["logical"]
|
|
17
|
+
presets: ["logical", "logicalStrict"]
|
|
19
18
|
},
|
|
20
19
|
messages: { missingAssociatedControl: {
|
|
21
20
|
primary: "This <label> element is missing an associated control element.",
|
|
@@ -41,7 +40,7 @@ var labelAssociatedControls_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
41
40
|
}
|
|
42
41
|
function hasNestedControl(children) {
|
|
43
42
|
return children.some((child) => {
|
|
44
|
-
if (child.kind
|
|
43
|
+
if (child.kind === SyntaxKind.JsxElement) {
|
|
45
44
|
const { tagName } = child.openingElement;
|
|
46
45
|
return tagName.kind === SyntaxKind.Identifier && controlElements.has(tagName.text.toLowerCase()) || hasNestedControl(child.children);
|
|
47
46
|
}
|
|
@@ -50,10 +49,10 @@ var labelAssociatedControls_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
50
49
|
});
|
|
51
50
|
}
|
|
52
51
|
function checkLabel(node, { sourceFile }) {
|
|
53
|
-
if (node.kind
|
|
54
|
-
const tagName = node.kind
|
|
52
|
+
if (node.kind === SyntaxKind.JsxElement && hasNestedControl(node.children)) return;
|
|
53
|
+
const tagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
|
|
55
54
|
if (tagName.kind !== SyntaxKind.Identifier || tagName.text.toLowerCase() !== "label") return;
|
|
56
|
-
if (hasHtmlForAttribute(node.kind
|
|
55
|
+
if (hasHtmlForAttribute(node.kind === SyntaxKind.JsxElement ? node.openingElement.attributes : node.attributes)) return;
|
|
57
56
|
context.report({
|
|
58
57
|
message: "missingAssociatedControl",
|
|
59
58
|
range: getTSNodeRange(tagName, sourceFile)
|
|
@@ -65,7 +64,7 @@ var labelAssociatedControls_default = ruleCreator.createRule(typescriptLanguage,
|
|
|
65
64
|
} };
|
|
66
65
|
}
|
|
67
66
|
});
|
|
68
|
-
|
|
69
67
|
//#endregion
|
|
70
68
|
export { labelAssociatedControls_default as default };
|
|
69
|
+
|
|
71
70
|
//# sourceMappingURL=labelAssociatedControls.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports invalid lang attribute values.";
|
|
3
|
+
readonly id: "langValidity";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "invalidLang", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=langValidity.d.ts.map
|
|
@@ -2,7 +2,6 @@ import { ruleCreator } from "./ruleCreator.js";
|
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
4
|
import languageTags from "language-tags";
|
|
5
|
-
|
|
6
5
|
//#region src/rules/langValidity.ts
|
|
7
6
|
var langValidity_default = ruleCreator.createRule(typescriptLanguage, {
|
|
8
7
|
about: {
|
|
@@ -38,7 +37,7 @@ var langValidity_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
38
37
|
} };
|
|
39
38
|
}
|
|
40
39
|
});
|
|
41
|
-
|
|
42
40
|
//#endregion
|
|
43
41
|
export { langValidity_default as default };
|
|
42
|
+
|
|
44
43
|
//# sourceMappingURL=langValidity.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports media elements without captions.";
|
|
3
|
+
readonly id: "mediaCaptions";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "missingCaptions", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=mediaCaptions.d.ts.map
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/mediaCaptions.ts
|
|
6
5
|
var mediaCaptions_default = ruleCreator.createRule(typescriptLanguage, {
|
|
7
6
|
about: {
|
|
8
7
|
description: "Reports media elements without captions.",
|
|
9
8
|
id: "mediaCaptions",
|
|
10
|
-
presets: ["logical"]
|
|
9
|
+
presets: ["logical", "logicalStrict"]
|
|
11
10
|
},
|
|
12
11
|
messages: { missingCaptions: {
|
|
13
12
|
primary: "This media element is missing <track> element captions.",
|
|
@@ -20,12 +19,12 @@ var mediaCaptions_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
20
19
|
} },
|
|
21
20
|
setup(context) {
|
|
22
21
|
function checkMediaElement(node, { sourceFile }) {
|
|
23
|
-
const tagName = node.kind
|
|
24
|
-
if (tagName.kind
|
|
22
|
+
const tagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
|
|
23
|
+
if (tagName.kind !== SyntaxKind.Identifier) return;
|
|
25
24
|
const elementName = tagName.text.toLowerCase();
|
|
26
25
|
if (elementName !== "audio" && elementName !== "video") return;
|
|
27
|
-
if ((node.kind
|
|
28
|
-
if (node.kind
|
|
26
|
+
if ((node.kind === SyntaxKind.JsxElement ? node.openingElement.attributes : node.attributes).properties.some((properties) => properties.kind === SyntaxKind.JsxAttribute && properties.name.kind === SyntaxKind.Identifier && properties.name.text === "muted")) return;
|
|
27
|
+
if (node.kind === SyntaxKind.JsxElement && node.children.some(isCaptionsTrack)) return;
|
|
29
28
|
context.report({
|
|
30
29
|
message: "missingCaptions",
|
|
31
30
|
range: getTSNodeRange(tagName, sourceFile)
|
|
@@ -38,16 +37,16 @@ var mediaCaptions_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
38
37
|
}
|
|
39
38
|
});
|
|
40
39
|
function isCaptionsTrack(node) {
|
|
41
|
-
if (node.kind
|
|
42
|
-
const childTagName = node.kind
|
|
43
|
-
if (childTagName.kind
|
|
44
|
-
return (node.kind
|
|
45
|
-
if (property.kind
|
|
46
|
-
if (property.initializer?.kind
|
|
40
|
+
if (node.kind !== SyntaxKind.JsxElement && node.kind !== SyntaxKind.JsxSelfClosingElement) return false;
|
|
41
|
+
const childTagName = node.kind === SyntaxKind.JsxElement ? node.openingElement.tagName : node.tagName;
|
|
42
|
+
if (childTagName.kind !== SyntaxKind.Identifier || childTagName.text !== "track") return false;
|
|
43
|
+
return (node.kind === SyntaxKind.JsxElement ? node.openingElement.attributes : node.attributes).properties.some((property) => {
|
|
44
|
+
if (property.kind !== SyntaxKind.JsxAttribute || property.name.kind !== SyntaxKind.Identifier || property.name.text !== "kind") return false;
|
|
45
|
+
if (property.initializer?.kind === SyntaxKind.StringLiteral) return property.initializer.text === "captions";
|
|
47
46
|
return false;
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
|
-
|
|
51
49
|
//#endregion
|
|
52
50
|
export { mediaCaptions_default as default };
|
|
51
|
+
|
|
53
52
|
//# sourceMappingURL=mediaCaptions.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports mouse events without corresponding keyboard events.";
|
|
3
|
+
readonly id: "mouseEventKeyEvents";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "missingKeyEvent", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=mouseEventKeyEvents.d.ts.map
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/mouseEventKeyEvents.ts
|
|
6
5
|
const mouseNamesToKeyboardNames = new Map([["onMouseOut", "onBlur"], ["onMouseOver", "onFocus"]]);
|
|
7
6
|
var mouseEventKeyEvents_default = ruleCreator.createRule(typescriptLanguage, {
|
|
8
7
|
about: {
|
|
9
8
|
description: "Reports mouse events without corresponding keyboard events.",
|
|
10
9
|
id: "mouseEventKeyEvents",
|
|
11
|
-
presets: ["logical"]
|
|
10
|
+
presets: ["logical", "logicalStrict"]
|
|
12
11
|
},
|
|
13
12
|
messages: { missingKeyEvent: {
|
|
14
13
|
primary: "`{{ mouseEvent }}` is missing an accompanying `{{ keyEvent }}` for keyboard accessibility.",
|
|
@@ -44,7 +43,7 @@ var mouseEventKeyEvents_default = ruleCreator.createRule(typescriptLanguage, {
|
|
|
44
43
|
} };
|
|
45
44
|
}
|
|
46
45
|
});
|
|
47
|
-
|
|
48
46
|
//#endregion
|
|
49
47
|
export { mouseEventKeyEvents_default as default };
|
|
48
|
+
|
|
50
49
|
//# sourceMappingURL=mouseEventKeyEvents.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports non-interactive elements with interactive event handlers.";
|
|
3
|
+
readonly id: "nonInteractiveElementInteractions";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "invalidHandler", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=nonInteractiveElementInteractions.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/nonInteractiveElementInteractions.ts
|
|
6
5
|
const interactiveHandlers = [
|
|
7
6
|
"onClick",
|
|
@@ -53,7 +52,7 @@ var nonInteractiveElementInteractions_default = ruleCreator.createRule(typescrip
|
|
|
53
52
|
about: {
|
|
54
53
|
description: "Reports non-interactive elements with interactive event handlers.",
|
|
55
54
|
id: "nonInteractiveElementInteractions",
|
|
56
|
-
presets: ["logical"]
|
|
55
|
+
presets: ["logical", "logicalStrict"]
|
|
57
56
|
},
|
|
58
57
|
messages: { invalidHandler: {
|
|
59
58
|
primary: "`<{{ element }}>` elements are non-interactive and so should not have interactive event handlers.",
|
|
@@ -75,7 +74,7 @@ var nonInteractiveElementInteractions_default = ruleCreator.createRule(typescrip
|
|
|
75
74
|
if (elementName !== element.tagName.text || !nonInteractiveElements.has(elementName)) return;
|
|
76
75
|
let hasInteractiveHandler = false;
|
|
77
76
|
for (const property of element.attributes.properties) if (property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier) {
|
|
78
|
-
if (property.name.text === "role" && property.initializer
|
|
77
|
+
if (property.name.text === "role" && property.initializer?.kind === SyntaxKind.StringLiteral && !nonInteractiveRoles.has(property.initializer.text)) return;
|
|
79
78
|
if (interactiveHandlers.includes(property.name.text)) hasInteractiveHandler = true;
|
|
80
79
|
}
|
|
81
80
|
if (hasInteractiveHandler) context.report({
|
|
@@ -90,7 +89,7 @@ var nonInteractiveElementInteractions_default = ruleCreator.createRule(typescrip
|
|
|
90
89
|
} };
|
|
91
90
|
}
|
|
92
91
|
});
|
|
93
|
-
|
|
94
92
|
//#endregion
|
|
95
93
|
export { nonInteractiveElementInteractions_default as default };
|
|
94
|
+
|
|
96
95
|
//# sourceMappingURL=nonInteractiveElementInteractions.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports non-interactive elements with interactive ARIA roles.";
|
|
3
|
+
readonly id: "nonInteractiveElementRoles";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "invalidRole", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=nonInteractiveElementRoles.d.ts.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ruleCreator } from "./ruleCreator.js";
|
|
2
2
|
import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
|
|
3
3
|
import { SyntaxKind } from "typescript";
|
|
4
|
-
|
|
5
4
|
//#region src/rules/nonInteractiveElementRoles.ts
|
|
6
5
|
const nonInteractiveElements = new Set([
|
|
7
6
|
"article",
|
|
@@ -79,7 +78,7 @@ var nonInteractiveElementRoles_default = ruleCreator.createRule(typescriptLangua
|
|
|
79
78
|
about: {
|
|
80
79
|
description: "Reports non-interactive elements with interactive ARIA roles.",
|
|
81
80
|
id: "nonInteractiveElementRoles",
|
|
82
|
-
presets: ["logical"]
|
|
81
|
+
presets: ["logical", "logicalStrict"]
|
|
83
82
|
},
|
|
84
83
|
messages: { invalidRole: {
|
|
85
84
|
primary: "Non-interactive element <{{ element }}> should not have the interactive role `'{{ role }}'`.",
|
|
@@ -96,7 +95,7 @@ var nonInteractiveElementRoles_default = ruleCreator.createRule(typescriptLangua
|
|
|
96
95
|
const elementName = element.tagName.text.toLowerCase();
|
|
97
96
|
if (!nonInteractiveElements.has(elementName)) return;
|
|
98
97
|
const roleProperty = element.attributes.properties.find((property) => property.kind === SyntaxKind.JsxAttribute && property.name.kind === SyntaxKind.Identifier && property.name.text === "role");
|
|
99
|
-
if (
|
|
98
|
+
if (roleProperty?.kind !== SyntaxKind.JsxAttribute || roleProperty.initializer?.kind !== SyntaxKind.StringLiteral) return;
|
|
100
99
|
const role = roleProperty.initializer.text;
|
|
101
100
|
if (interactiveRoles.has(role) && !allowedExceptions[elementName]?.has(role)) context.report({
|
|
102
101
|
data: {
|
|
@@ -113,7 +112,7 @@ var nonInteractiveElementRoles_default = ruleCreator.createRule(typescriptLangua
|
|
|
113
112
|
} };
|
|
114
113
|
}
|
|
115
114
|
});
|
|
116
|
-
|
|
117
115
|
//#endregion
|
|
118
116
|
export { nonInteractiveElementRoles_default as default };
|
|
117
|
+
|
|
119
118
|
//# sourceMappingURL=nonInteractiveElementRoles.js.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
declare const _default: import("@flint.fyi/core").Rule<
|
|
1
|
+
declare const _default: import("@flint.fyi/core").Rule<{
|
|
2
|
+
readonly description: "Reports non-interactive elements with positive or zero tabIndex values.";
|
|
3
|
+
readonly id: "nonInteractiveElementTabIndexes";
|
|
4
|
+
readonly presets: readonly ["logical", "logicalStrict"];
|
|
5
|
+
} & {
|
|
6
|
+
readonly pluginId: string;
|
|
7
|
+
readonly url: string;
|
|
8
|
+
}, "nonInteractiveTabIndex", undefined>;
|
|
2
9
|
export default _default;
|
|
3
10
|
//# sourceMappingURL=nonInteractiveElementTabIndexes.d.ts.map
|