feat(lint): useIframeTite for HTML and JSX
ADD
internal/compiler/lint/rules/a11y/useIframeTitle.test.rjson
+[
+ {
+ filename: "file.tsx"
+ invalid: [
+ "<iframe />"
+ "<iframe {...props} />"
+ "<iframe title=\"\" />"
+ "<iframe title={\"\"} />"
+ "<iframe title={``} />"
+ "<iframe title={undefined} />"
+ "<iframe title={false} />"
+ "<iframe title={true} />"
+ "<iframe title={42} />"
+ ]
+ valid: [
+ "<iframe title='title' />"
+ "<Iframe title='title' />"
+ ]
+}
+ {
+ filename: "file.html"
+ invalid: [
+ "<iframe></iframe>"
+ "<iframe title=\"\"></iframe>"
+ ]
+ valid: [
+ "<iframe title=\"Tour\"></iframe>"
+ "<iframe title=\"Camping\"></iframe>"
+ ]
+}
+]

RENAME
internal/compiler/lint/rules/jsx-a11y/useIframeTitle.ts
internal/compiler/lint/rules/a11y/useIframeTitle.ts
@@ -8,6 +8,8 @@ import {
import {JSXElement} from "@internal/ast";
import {createVisitor, signals} from "@internal/compiler";
import {isJSXDOMElement} from "@internal/js-ast-utils/isJSXDOMElement";
+import getHTMLAttribute from "@internal/js-ast-utils/getHTMLAttribute";
+import isHTMLElement from "@internal/js-ast-utils/isHTMLElement";
function validTitle(node: JSXElement) {
if (isJSXDOMElement(node) && hasJSXAttribute(node, "title")) {
@@ -32,7 +34,7 @@ function validTitle(node: JSXElement) {
}
export default createVisitor({
- name: "jsx-a11y/useIframeTitle",
+ name: "a11y/useIframeTitle",
enter(path) {
const {node} = path;
@@ -40,7 +42,15 @@ export default createVisitor({
if (!(hasJSXAttribute(node, "title") && validTitle(node))) {
path.context.addNodeDiagnostic(
node,
- descriptions.LINT.JSX_A11Y_IFRAME_USE_TITLE,
+ descriptions.LINT.A11Y_IFRAME_USE_TITLE,
+ );
+ }
+ } else if (isHTMLElement(node) && node.name.name === "iframe") {
+ const titleAttribute = getHTMLAttribute(node, "title");
+ if (!titleAttribute || titleAttribute.value?.value === "") {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.A11Y_IFRAME_USE_TITLE,
);
}
}

MODIFY
internal/compiler/lint/rules/index.ts
internal/compiler/lint/rules/index.ts
-/* GENERATED:START(hash:4c54e7af704a3b972adddcc5e51ba95a8b481b03,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
+/* GENERATED:START(hash:faf4e375ca7405d0bb228c8feccf6b11a88eff8f,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
import noAriaUnsupportedElements from "./a11y/noAriaUnsupportedElements";
import noDistractingElements from "./a11y/noDistractingElements";
import noNoninteractiveElementToInteractiveRole from "./a11y/noNoninteractiveElementToInteractiveRole";
@@ -7,6 +7,7 @@ import noSvgWithoutTitle from "./a11y/noSvgWithoutTitle";
import useAltText from "./a11y/useAltText";
import useAriaProptypes from "./a11y/useAriaProptypes";
import useHtmlLang from "./a11y/useHtmlLang";
+import useIframeTitle from "./a11y/useIframeTitle";
import useMediaCaption from "./a11y/useMediaCaption";
import useValidLang from "./a11y/useValidLang";
import useClosingNonVoid from "./html/useClosingNonVoid";
@@ -70,7 +71,6 @@ import useAnchorContent from "./jsx-a11y/useAnchorContent";
import useAriaProps from "./jsx-a11y/useAriaProps";
import useAriaPropsForRole from "./jsx-a11y/useAriaPropsForRole";
import useHeadingContent from "./jsx-a11y/useHeadingContent";
-import useIframeTitle from "./jsx-a11y/useIframeTitle";
import useKeyWithClickEvents from "./jsx-a11y/useKeyWithClickEvents";
import useKeyWithMouseEvents from "./jsx-a11y/useKeyWithMouseEvents";
import useValidAnchor from "./jsx-a11y/useValidAnchor";
@@ -128,6 +128,7 @@ lintTransforms.set("a11y/noSvgWithoutTitle", noSvgWithoutTitle);
lintTransforms.set("a11y/useAltText", useAltText);
lintTransforms.set("a11y/useAriaProptypes", useAriaProptypes);
lintTransforms.set("a11y/useHtmlLang", useHtmlLang);
+lintTransforms.set("a11y/useIframeTitle", useIframeTitle);
lintTransforms.set("a11y/useMediaCaption", useMediaCaption);
lintTransforms.set("a11y/useValidLang", useValidLang);
lintTransforms.set("html/useClosingNonVoid", useClosingNonVoid);
@@ -197,7 +198,6 @@ lintTransforms.set("jsx-a11y/useAnchorContent", useAnchorContent);
lintTransforms.set("jsx-a11y/useAriaProps", useAriaProps);
lintTransforms.set("jsx-a11y/useAriaPropsForRole", useAriaPropsForRole);
lintTransforms.set("jsx-a11y/useHeadingContent", useHeadingContent);
-lintTransforms.set("jsx-a11y/useIframeTitle", useIframeTitle);
lintTransforms.set("jsx-a11y/useKeyWithClickEvents", useKeyWithClickEvents);
lintTransforms.set("jsx-a11y/useKeyWithMouseEvents", useKeyWithMouseEvents);
lintTransforms.set("jsx-a11y/useValidAnchor", useValidAnchor);
@@ -272,6 +272,7 @@ export const lintRuleNames: LintRuleName[] = [
"a11y/useAltText",
"a11y/useAriaProptypes",
"a11y/useHtmlLang",
+ "a11y/useIframeTitle",
"a11y/useMediaCaption",
"a11y/useValidLang",
"html/useClosingNonVoid",
@@ -335,7 +336,6 @@ export const lintRuleNames: LintRuleName[] = [
"jsx-a11y/useAriaProps",
"jsx-a11y/useAriaPropsForRole",
"jsx-a11y/useHeadingContent",
- "jsx-a11y/useIframeTitle",
"jsx-a11y/useKeyWithClickEvents",
"jsx-a11y/useKeyWithMouseEvents",
"jsx-a11y/useValidAnchor",
@@ -390,6 +390,7 @@ export type LintRuleName =
| "a11y/useAltText"
| "a11y/useAriaProptypes"
| "a11y/useHtmlLang"
+ | "a11y/useIframeTitle"
| "a11y/useMediaCaption"
| "a11y/useValidLang"
| "html/useClosingNonVoid"
@@ -453,7 +454,6 @@ export type LintRuleName =
| "jsx-a11y/useAriaProps"
| "jsx-a11y/useAriaPropsForRole"
| "jsx-a11y/useHeadingContent"
- | "jsx-a11y/useIframeTitle"
| "jsx-a11y/useKeyWithClickEvents"
| "jsx-a11y/useKeyWithMouseEvents"
| "jsx-a11y/useValidAnchor"

DELETE
internal/compiler/lint/rules/jsx-a11y/useIframeTitle.test.rjson
-filename: "file.tsx"
-invalid: [
- "<iframe />"
- "<iframe {...props} />"
- "<iframe title=\"\" />"
- "<iframe title={\"\"} />"
- "<iframe title={``} />"
- "<iframe title={undefined} />"
- "<iframe title={false} />"
- "<iframe title={true} />"
- "<iframe title={42} />"
-]
-valid: [
- "<iframe title='title' />"
- "<Iframe title='title' />"
-]

MODIFY
internal/compiler/lint/rules/tests.ts
internal/compiler/lint/rules/tests.ts
@@ -12,7 +12,7 @@ type Tests = Dict<{
cases: Test | (Test[]) | (Test[][]);
}>;
-/* GENERATED:START(hash:dc6d907fbe31d034b0e660cd5366c15b40b3c20f,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
+/* GENERATED:START(hash:7c338129a77fa5641ec09fc8d7de4672265d412c,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
// @ts-ignore
import noAriaUnsupportedElements from "./a11y/noAriaUnsupportedElements.test.rjson";
// @ts-ignore
@@ -30,6 +30,8 @@ import useAriaProptypes from "./a11y/useAriaProptypes.test.rjson";
// @ts-ignore
import useHtmlLang from "./a11y/useHtmlLang.test.rjson";
// @ts-ignore
+import useIframeTitle from "./a11y/useIframeTitle.test.rjson";
+// @ts-ignore
import useMediaCaption from "./a11y/useMediaCaption.test.rjson";
// @ts-ignore
import useValidLang from "./a11y/useValidLang.test.rjson";
@@ -154,8 +156,6 @@ import useAriaPropsForRole from "./jsx-a11y/useAriaPropsForRole.test.rjson";
// @ts-ignore
import useHeadingContent from "./jsx-a11y/useHeadingContent.test.rjson";
// @ts-ignore
-import useIframeTitle from "./jsx-a11y/useIframeTitle.test.rjson";
-// @ts-ignore
import useKeyWithClickEvents from "./jsx-a11y/useKeyWithClickEvents.test.rjson";
// @ts-ignore
import useKeyWithMouseEvents from "./jsx-a11y/useKeyWithMouseEvents.test.rjson";
@@ -267,6 +267,10 @@ export const tests: Tests = {
category: ["lint", "a11y", "useHtmlLang"],
cases: useHtmlLang,
},
+ "a11y/useIframeTitle": {
+ category: ["lint", "a11y", "useIframeTitle"],
+ cases: useIframeTitle,
+ },
"a11y/useMediaCaption": {
category: ["lint", "a11y", "useMediaCaption"],
cases: useMediaCaption,
@@ -515,10 +519,6 @@ export const tests: Tests = {
category: ["lint", "jsx-a11y", "useHeadingContent"],
cases: useHeadingContent,
},
- "jsx-a11y/useIframeTitle": {
- category: ["lint", "jsx-a11y", "useIframeTitle"],
- cases: useIframeTitle,
- },
"jsx-a11y/useKeyWithClickEvents": {
category: ["lint", "jsx-a11y", "useKeyWithClickEvents"],
cases: useKeyWithClickEvents,

MODIFY
internal/diagnostics/categories.ts
internal/diagnostics/categories.ts
@@ -100,7 +100,7 @@ type DiagnosticCategoryString = StringConverter<DiagnosticCategory>;
type DiagnosticLintCategoryString = StringConverter<DiagnosticLintCategory>;
-/* GENERATED:START(hash:73d4d80f2f19e5e0af1a581d3749fd5ac177d9ac,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
+/* GENERATED:START(hash:3b94ccb81510bc041899cfb140e04143c7fafe99,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. */
export type DiagnosticLintCategory =
| ["lint", "a11y", "noAriaUnsupportedElements"]
| ["lint", "a11y", "noDistractingElements"]
@@ -110,6 +110,7 @@ export type DiagnosticLintCategory =
| ["lint", "a11y", "useAltText"]
| ["lint", "a11y", "useAriaProptypes"]
| ["lint", "a11y", "useHtmlLang"]
+ | ["lint", "a11y", "useIframeTitle"]
| ["lint", "a11y", "useMediaCaption"]
| ["lint", "a11y", "useValidLang"]
| ["lint", "html", "useClosingNonVoid"]
@@ -173,7 +174,6 @@ export type DiagnosticLintCategory =
| ["lint", "jsx-a11y", "useAriaProps"]
| ["lint", "jsx-a11y", "useAriaPropsForRole"]
| ["lint", "jsx-a11y", "useHeadingContent"]
- | ["lint", "jsx-a11y", "useIframeTitle"]
| ["lint", "jsx-a11y", "useKeyWithClickEvents"]
| ["lint", "jsx-a11y", "useKeyWithMouseEvents"]
| ["lint", "jsx-a11y", "useValidAnchor"]
@@ -240,6 +240,7 @@ const lintCategoryNameMap: {
"lint/a11y/useAltText": ["lint", "a11y", "useAltText"],
"lint/a11y/useAriaProptypes": ["lint", "a11y", "useAriaProptypes"],
"lint/a11y/useHtmlLang": ["lint", "a11y", "useHtmlLang"],
+ "lint/a11y/useIframeTitle": ["lint", "a11y", "useIframeTitle"],
"lint/a11y/useMediaCaption": ["lint", "a11y", "useMediaCaption"],
"lint/a11y/useValidLang": ["lint", "a11y", "useValidLang"],
"lint/html/useClosingNonVoid": ["lint", "html", "useClosingNonVoid"],
@@ -315,7 +316,6 @@ const lintCategoryNameMap: {
"useAriaPropsForRole",
],
"lint/jsx-a11y/useHeadingContent": ["lint", "jsx-a11y", "useHeadingContent"],
- "lint/jsx-a11y/useIframeTitle": ["lint", "jsx-a11y", "useIframeTitle"],
"lint/jsx-a11y/useKeyWithClickEvents": [
"lint",
"jsx-a11y",

MODIFY
internal/diagnostics/descriptions/lint.ts
internal/diagnostics/descriptions/lint.ts
@@ -435,8 +435,8 @@ export const lint = createDiagnosticsCategory({
},
],
},
- JSX_A11Y_IFRAME_USE_TITLE: {
- category: DIAGNOSTIC_CATEGORIES["lint/jsx-a11y/useIframeTitle"],
+ A11Y_IFRAME_USE_TITLE: {
+ category: DIAGNOSTIC_CATEGORIES["lint/a11y/useIframeTitle"],
message: markup`Provide a <emphasis>title</emphasis> attribute when using <emphasis>iframe</emphasis> elements.`,
advice: [
{

MODIFY
website/src/docs/lint/rules/index.md
website/src/docs/lint/rules/index.md
@@ -11,7 +11,7 @@ eleventyNavigation:
# Rules
-<!-- GENERATED:START(hash:a84121d08a825f38c8f1ccad3433756a29808fd7,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. -->
+<!-- GENERATED:START(hash:8afb883d7e5f4ac1d4377e4b2d08f0475a1bf947,id:main) Everything below is automatically generated. DO NOT MODIFY. Run `./rome run scripts/generated-files/lint-rules` to update. -->
<section>
<h2>JavaScript</h2>
<p>Rule semantics and descriptions taken from <a href="https://eslint.org/">ESLint</a>. See individual rule docs for direct references.</p>
@@ -444,6 +444,13 @@ enforce ARIA state and property values are valid
the `lang` attribute is mandatory
</div>
<div class="rule">
+<h3 data-toc-exclude id="useIframeTitle">
+ <a href="/docs/lint/rules/a11y/useIframeTitle">useIframeTitle</a>
+ <a class="header-anchor" href="#useIframeTitle"></a>
+</h3>
+enforce `iframe` elements have a title attribute
+</div>
+<div class="rule">
<h3 data-toc-exclude id="useMediaCaption">
<a href="/docs/lint/rules/a11y/useMediaCaption">useMediaCaption</a>
<a class="header-anchor" href="#useMediaCaption"></a>
@@ -546,13 +553,6 @@ enforce that elements with ARIA roles must have all required attributes for that
enforce heading (`h1`, `h2`, etc) elements contain accessible content
</div>
<div class="rule">
-<h3 data-toc-exclude id="useIframeTitle">
- <a href="/docs/lint/rules/jsx-a11y/useIframeTitle">useIframeTitle</a>
- <a class="header-anchor" href="#useIframeTitle"></a>
-</h3>
-enforce `iframe` elements have a title attribute
-</div>
-<div class="rule">
<h3 data-toc-exclude id="useKeyWithClickEvents">
<a href="/docs/lint/rules/jsx-a11y/useKeyWithClickEvents">useKeyWithClickEvents</a>
<a class="header-anchor" href="#useKeyWithClickEvents"></a>

feat(lint): useIframeTite for HTML and JSX (#1382)