feat: add jsx-a11y/no-access-key, jsx-a11y-no-autofocus, jsx-a11y-no-scope
MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -107,8 +107,11 @@ type LintDiagnosticCategory =
| "lint/jsxA11yHTMLHasLang"
| "lint/jsxA11yIframeHasTitle"
| "lint/jsxA11yImgRedundantAlt"
+ | "lint/jsxA11yNoAccessKey"
+ | "lint/jsxA11yNoAutofocus"
| "lint/jsxA11yNoDistractingElements"
| "lint/jsxA11yNoTargetBlank"
+ | "lint/jsxA11yScope"
| "lint/jsxKey"
| "lint/jsxNoCommentText"
| "lint/negationElse"

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -455,6 +455,18 @@ export const descriptions = createMessages({
category: "lint/jsxA11yIframeHasTitle",
message: `<emphasis>iframe</emphasis> elements should have a <emphasis>title prop</emphasis>.`,
},
+ REACT_JSX_NO_ACCESS_KEY: {
+ category: "lint/jsxA11yNoAccessKey",
+ message: "The <emphasis>accessKey</emphasis> prop is not allowed. Inconsistencies between keyboard shortcuts and keyboard comments used by screenreader and keyboard only users create a11y complications.",
+ },
+ REACT_JSX_NO_AUTOFOCUS: {
+ category: "lint/jsxA11yNoAutofocus",
+ message: "The <emphasis>autoFocus</emphasis> prop should not be used, as it can reduce usability and accessibility for users.",
+ },
+ REACT_JSX_NO_SCOPE: {
+ category: "lint/jsxA11yScope",
+ message: "The <emphasis>scope</emphasis> prop can only be used on <emphasis>th</emphasis> elements.",
+ },
REACT_JSX_KEY: (origin: string) => ({
category: "lint/jsxKey",
message: markup`Missing the "key" prop for element in ${origin}`,

MODIFY
packages/@romejs/js-compiler/lint/rules/index.ts
packages/@romejs/js-compiler/lint/rules/index.ts
*/
// EVERYTHING BELOW IS AUTOGENERATED. SEE SCRIPTS FOLDER FOR UPDATE SCRIPTS
+
import camelCase from "./regular/camelCase";
import caseSingleStatement from "./regular/caseSingleStatement";
import confusingLanguage from "./regular/confusingLanguage";
@@ -16,13 +17,16 @@ import emptyBlocks from "./regular/emptyBlocks";
import emptyMatches from "./regular/emptyMatches";
import getterReturn from "./regular/getterReturn";
import importDefaultBasename from "./regular/importDefaultBasename";
-import jsxA11YAltText from "./react/jsxA11yAltText";
-import jsxA11YHeadingHasContent from "./react/jsxA11yHeadingHasContent";
-import jsxA11YHTMLHasLang from "./react/jsxA11yHTMLHasLang";
-import jsxA11YIframeHasTitle from "./react/jsxA11yIframeHasTitle";
-import jsxA11YImgRedundantAlt from "./react/jsxA11yImgRedundantAlt";
-import jsxA11YNoDistractingElements from "./react/jsxA11yNoDistractingElements";
-import jsxA11YNoTargetBlank from "./react/jsxA11yNoTargetBlank";
+import jsxA11yAltText from "./react/jsxA11yAltText";
+import jsxA11yHeadingHasContent from "./react/jsxA11yHeadingHasContent";
+import jsxA11yHTMLHasLang from "./react/jsxA11yHTMLHasLang";
+import jsxA11yIframeHasTitle from "./react/jsxA11yIframeHasTitle";
+import jsxA11yImgRedundantAlt from "./react/jsxA11yImgRedundantAlt";
+import jsxA11yNoAccessKey from "./react/jsxA11yNoAccessKey";
+import jsxA11yNoAutofocus from "./react/jsxA11yNoAutofocus";
+import jsxA11yNoDistractingElements from "./react/jsxA11yNoDistractingElements";
+import jsxA11yNoTargetBlank from "./react/jsxA11yNoTargetBlank";
+import jsxA11yScope from "./react/jsxA11yScope";
import jsxKey from "./react/jsxKey";
import jsxNoCommentText from "./react/jsxNoCommentText";
import negationElse from "./regular/negationElse";
@@ -85,13 +89,16 @@ export const lintTransforms = [
emptyMatches,
getterReturn,
importDefaultBasename,
- jsxA11YAltText,
- jsxA11YHeadingHasContent,
- jsxA11YHTMLHasLang,
- jsxA11YIframeHasTitle,
- jsxA11YImgRedundantAlt,
- jsxA11YNoDistractingElements,
- jsxA11YNoTargetBlank,
+ jsxA11yAltText,
+ jsxA11yHeadingHasContent,
+ jsxA11yHTMLHasLang,
+ jsxA11yIframeHasTitle,
+ jsxA11yImgRedundantAlt,
+ jsxA11yNoAccessKey,
+ jsxA11yNoAutofocus,
+ jsxA11yNoDistractingElements,
+ jsxA11yNoTargetBlank,
+ jsxA11yScope,
jsxKey,
jsxNoCommentText,
negationElse,

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yNoAccessKey.test.ts
+import {test} from "rome";
+import {testLintMultiple} from "../testHelpers";
+
+test(
+ "disallow the accessKey prop",
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ '<input accessKey="key" />',
+ "<input accessKey={key} />",
+ // VALID
+ "<input />",
+ "<input accessKey={undefined} />",
+ ],
+ {category: "lint/jsxA11yNoAccessKey"},
+ );
+ },
+);

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yNoAccessKey.ts
+import {descriptions} from "@romejs/diagnostics";
+import {AnyNode} from "@romejs/js-ast";
+import {Path} from "@romejs/js-compiler";
+import {hasJSXAttribute, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "jsxA11yNoAccessKey",
+
+ enter(path: Path): AnyNode {
+ const {node} = path;
+
+ if (isJSXElement(node) && hasJSXAttribute(node, "accessKey")) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.REACT_JSX_NO_ACCESS_KEY,
+ );
+ }
+
+ return node;
+ },
+};

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yNoAutofocus.test.ts
+import {test} from "rome";
+import {testLintMultiple} from "../testHelpers";
+
+test(
+ "disallow the autoFocus prop",
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ "<input autoFocus />",
+ '<input autoFocus="true" />',
+ '<input autoFocus="false" />',
+ // VALID
+ "<input />",
+ "<input autoFocus={undefined} />",
+ ],
+ {category: "lint/jsxA11yNoAutofocus"},
+ );
+ },
+);

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yNoAutofocus.ts
+import {descriptions} from "@romejs/diagnostics";
+import {AnyNode} from "@romejs/js-ast";
+import {Path} from "@romejs/js-compiler";
+import {hasJSXAttribute, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "jsxA11yNoAutofocus",
+
+ enter(path: Path): AnyNode {
+ const {node} = path;
+
+ if (isJSXElement(node) && hasJSXAttribute(node, "autoFocus")) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.REACT_JSX_NO_AUTOFOCUS,
+ );
+ }
+
+ return node;
+ },
+};

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yScope.test.ts
+import {test} from "rome";
+import {testLintMultiple} from "../testHelpers";
+
+test(
+ "disallow the scope prop on elements other than th",
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // INVALID
+ "<div scope={scope} />",
+ '<div scope="col" />',
+ // VALID
+ "<th scope={scope}></th>",
+ '<th scope="col"></th>',
+ ],
+ {category: "lint/jsxA11yScope"},
+ );
+ },
+);

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yScope.ts
+import {descriptions} from "@romejs/diagnostics";
+import {AnyNode} from "@romejs/js-ast";
+import {Path} from "@romejs/js-compiler";
+import {hasJSXAttribute, isJSXElement} from "@romejs/js-ast-utils";
+
+export default {
+ name: "jsxA11yScope",
+
+ enter(path: Path): AnyNode {
+ const {node} = path;
+
+ if (
+ isJSXElement(node) &&
+ hasJSXAttribute(node, "scope") &&
+ !isJSXElement(node, "th")
+ ) {
+ path.context.addNodeDiagnostic(node, descriptions.LINT.REACT_JSX_NO_SCOPE);
+ }
+
+ return node;
+ },
+};

MODIFY
packages/@romejs/string-markup/parse.ts
packages/@romejs/string-markup/parse.ts
@@ -51,6 +51,7 @@ tags.set("highlight", ["i"]);
tags.set("table", []);
tags.set("tr", []);
tags.set("td", ["align"]);
+tags.set("th", []);
tags.set("hr", []);
tags.set("pad", ["width", "align"]);
tags.set("nobr", []);
@@ -61,7 +62,7 @@ tags.set("ol", ["reversed", "start"]);
// Tags that only support certain other tags as their children
const tagsToOnlyChildren: Map<MarkupTagName, Array<MarkupTagName>> = new Map();
tagsToOnlyChildren.set("table", ["tr"]);
-tagsToOnlyChildren.set("tr", ["td"]);
+tagsToOnlyChildren.set("tr", ["th", "td"]);
tagsToOnlyChildren.set("ol", ["li"]);
tagsToOnlyChildren.set("ul", ["li"]);
@@ -69,6 +70,7 @@ tagsToOnlyChildren.set("ul", ["li"]);
const tagsToOnlyParent: Map<MarkupTagName, Array<MarkupTagName>> = new Map();
tagsToOnlyParent.set("tr", ["table"]);
tagsToOnlyParent.set("td", ["tr"]);
+tagsToOnlyParent.set("th", ["tr"]);
tagsToOnlyParent.set("li", ["ol", "ul"]);
//

MODIFY
packages/@romejs/string-markup/types.ts
packages/@romejs/string-markup/types.ts
@@ -63,6 +63,7 @@ export type MarkupTagName =
| "table"
| "tr"
| "td"
+ | "th"
| "nobr"
| "ol"
| "ul"

feat: add jsx-a11y/no-access-key, jsx-a11y-no-autofocus, jsx-a11y-no-scope