feat: add jsx-a11y/iframe-has-title lint rule
MODIFY
packages/@romejs/diagnostics/categories.ts
packages/@romejs/diagnostics/categories.ts
@@ -102,6 +102,7 @@ type LintDiagnosticCategory =
| 'lint/getterReturn'
| 'lint/importDefaultBasename'
| 'lint/inconsiderateLanguage'
+ | 'lint/jsxA11yIframeHasTitle'
| 'lint/jsxKey'
| 'lint/jsxNoCommentText'
| 'lint/negationElse'

MODIFY
packages/@romejs/diagnostics/descriptions.ts
packages/@romejs/diagnostics/descriptions.ts
@@ -415,6 +415,10 @@ export const descriptions = createMessages({
category: 'lint/jsxNoCommentText',
message: 'Comments inside children should be placed in braces',
},
+ REACT_JSX_A11Y_IFRAME_HAS_TITLE: {
+ category: 'lint/jsxA11yIframeHasTitle',
+ message: markup`<emphasis>iframe</emphasis> elements should have a <emphasis>title prop</emphasis>.`,
+ },
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
@@ -16,6 +16,7 @@ import emptyMatches from './regular/emptyMatches';
import getterReturn from './regular/getterReturn';
import importDefaultBasename from './regular/importDefaultBasename';
import inconsiderateLanguage from './regular/inconsiderateLanguage';
+import jsxA11YIframeHasTitle from './react/jsxA11yIframeHasTitle';
import jsxKey from './react/jsxKey';
import jsxNoCommentText from './react/jsxNoCommentText';
import negationElse from './regular/negationElse';
@@ -75,6 +76,7 @@ export const lintTransforms = [
getterReturn,
importDefaultBasename,
inconsiderateLanguage,
+ jsxA11YIframeHasTitle,
jsxKey,
jsxNoCommentText,
negationElse,

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.test.ts
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import {test} from 'rome';
+
+import {testLintMultiple} from '../testHelpers';
+
+test(
+ 'require a title attribute on <iframe> JSX elements',
+ async (t) => {
+ await testLintMultiple(
+ t,
+ [
+ // 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} >',
+ ],
+ {category: 'lint/jsxA11yIframeHasTitle'},
+ );
+ },
+);

ADD
packages/@romejs/js-compiler/lint/rules/react/jsxA11yIframeHasTitle.ts
+/**
+* Copyright (c) Facebook, Inc. and its affiliates.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+import {descriptions} from '@romejs/diagnostics';
+import {AnyNode} from '@romejs/js-ast';
+import {Path} from '@romejs/js-compiler';
+
+function jsxIframeMissingTitle(node: AnyNode) {
+ return (
+ node.type === 'JSXElement' &&
+ node.name.type === 'JSXIdentifier' &&
+ node.name.name === 'iframe' &&
+ !node.attributes.some((attribute) =>
+ attribute.type === 'JSXAttribute' &&
+ attribute.name.name === 'title' &&
+ attribute.value &&
+ ((attribute.value.type === 'StringLiteral' &&
+ attribute.value.value.length > 0) ||
+ (attribute.value.type === 'JSXExpressionContainer' &&
+ ((attribute.value.expression.type === 'ReferenceIdentifier' &&
+ attribute.value.expression.name !== 'undefined') ||
+ (attribute.value.expression.type === 'StringLiteral' &&
+ attribute.value.expression.value.length > 0))))
+ )
+ );
+}
+
+export default {
+ name: 'jsxA11yIframeHasTitle',
+
+ enter(path: Path): AnyNode {
+ const {node} = path;
+
+ if (jsxIframeMissingTitle(node)) {
+ path.context.addNodeDiagnostic(
+ node,
+ descriptions.LINT.REACT_JSX_A11Y_IFRAME_HAS_TITLE,
+ );
+ }
+
+ return node;
+ },
+};

feat: add jsx-a11y/iframe-has-title lint rule