From 2521b52011e2495d324eec6d92bdb88bc5e3bb0f Mon Sep 17 00:00:00 2001 From: Yuji Sugiura <6259812+leaysgur@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:55:50 +0900 Subject: [PATCH] fix(linter/jsx_a11y): Ensure plugin settings are used (#2359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, some of the rules did not use `settings`, so make sure they do. - [x] Align implementation of `getElementType()` - (This is the only util that depends on `settings`) Original rules which use `getElementType()` are: - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ accessible-emoji - [x] โœ… alt-text - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ anchor-ambiguous-text - [x] โœ… anchor-has-content - [x] anchor-is-valid - TODO: `from_configuration()` not implemented => #2361 - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ aria-activedescendant-has-tabindex - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ aria-role - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ aria-unsupported-elements - [x] autocomplete-valid - TODO: `from_configuration()` not implemented and needed for this => #2362 - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ click-events-have-key-events - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ control-has-associated-label - [x] heading-has-content - TODO: 1 test should be failed but passes ๐Ÿค” => #2360 - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ html-has-lang - [x] โœ… iframe-has-title - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ img-redundant-alt - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ interactive-supports-focus - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ label-has-associated-control - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ label-has-for - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ lang - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ media-has-caption - [x] โœ… no-aria-hidden-on-focusable - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ no-autofocus - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ no-distracting-elements - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-interactive-element-to-noninteractive-role - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-noninteractive-element-interactions - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-noninteractive-element-to-interactive-role - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-noninteractive-tabindex - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-onchange - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ no-redundant-roles - [x] ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ no-static-element-interactions - [x] โœ… prefer-tag-over-role - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ role-has-required-aria-props - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ role-supports-aria-props - [x] ๐Ÿ™†๐Ÿปโ€โ™€๏ธ scope ๐Ÿ™…๐Ÿปโ€โ™‚๏ธ = Not implemented yet by oxlint / โœ… = Fixed by this PR / ๐Ÿ™†๐Ÿปโ€โ™€๏ธ = Already used --- .../oxc_linter/src/rules/jsx_a11y/alt_text.rs | 345 +++++++++--------- .../src/rules/jsx_a11y/anchor_has_content.rs | 53 +-- .../src/rules/jsx_a11y/anchor_is_valid.rs | 107 +++--- .../src/rules/jsx_a11y/heading_has_content.rs | 92 +++-- .../src/rules/jsx_a11y/iframe_has_title.rs | 56 ++- .../jsx_a11y/no_aria_hidden_on_focusable.rs | 15 +- .../rules/jsx_a11y/prefer_tag_over_role.rs | 38 +- crates/oxc_linter/src/snapshots/alt_text.snap | 7 + .../src/snapshots/anchor_has_content.snap | 7 + .../src/snapshots/anchor_is_valid.snap | 7 + .../src/snapshots/heading_has_content.snap | 7 + .../src/snapshots/iframe_has_title.snap | 7 + crates/oxc_linter/src/utils/react.rs | 26 +- 13 files changed, 433 insertions(+), 334 deletions(-) diff --git a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs index 627f0f37f..de1e88090 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs @@ -1,6 +1,6 @@ use oxc_ast::{ ast::{ - JSXAttributeItem, JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression, + JSXAttributeItem, JSXAttributeValue, JSXChild, JSXElement, JSXExpression, JSXExpressionContainer, JSXOpeningElement, }, AstKind, @@ -12,7 +12,9 @@ use oxc_diagnostics::{ use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use crate::utils::{get_prop_value, get_string_literal_prop_value, has_jsx_prop_lowercase}; +use crate::utils::{ + get_element_type, get_prop_value, get_string_literal_prop_value, has_jsx_prop_lowercase, +}; use crate::{context::LintContext, rule::Rule, AstNode}; #[derive(Debug, Error, Diagnostic)] @@ -163,8 +165,7 @@ impl Rule for AltText { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::JSXOpeningElement(jsx_el) = node.kind() else { return }; - let JSXElementName::Identifier(iden) = &jsx_el.name else { return }; - let name = iden.name.as_str(); + let Some(name) = &get_element_type(ctx, jsx_el) else { return }; // if let Some(custom_tags) = &self.img { @@ -356,7 +357,7 @@ fn input_type_image_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext< fn test() { use crate::tester::Tester; - fn array() -> serde_json::Value { + fn config() -> serde_json::Value { serde_json::json!([{ "img": ["Thumbnail", "Image"], "object": ["Object"], @@ -366,177 +367,179 @@ fn test() { } let pass = vec![ - (r#"foo;"#, None), - (r#"{"foo"};"#, None), - (r"{alt};", None), - (r#"foo;"#, None), - (r"{`This;", None), - (r#"foo;"#, None), - (r#"foo;"#, None), - (r#"foo"#, None), - (r"", None), - (r"
", None), - (r"{function(e)", None), - (r"
", None), - (r"{() void 0} />", None), - (r"", None), - (r"test", None), - (r#"{alt"#, None), - (r"{photo.caption};", None), - (r"{bar()};", None), - (r#"{foo.bar"#, None), - (r#"{bar()"#, None), - (r#"{foo.bar()"#, None), - (r#""#, None), - (r"{`${undefined}`}", None), - (r#" "#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#"this is lit..."#, None), - (r#"{error"#, None), - (r#"{undefined"#, None), - (r#"{plugin.name"#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r"Foo", None), - (r"

This is descriptive!

", None), - (r"", None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#"This is descriptive!"#, None), - (r"{altText}", None), - (r"", None), - (r"", None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r"", None), - (r#""#, None), - // TODO: When polymorphic components are supported - // (r#""#, None), - (r#";"#, Some(array())), - (r#";"#, Some(array())), - (r";", Some(array())), - (r#";"#, Some(array())), - (r";", Some(array())), - (r#";"#, Some(array())), - (r#";"#, Some(array())), - (r#""#, Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"
", Some(array())), - (r" void 0} />", Some(array())), - (r"", Some(array())), - (r#""#, Some(array())), - (r#"foo;"#, Some(array())), - (r#"{"foo"};"#, Some(array())), - (r"{alt};", Some(array())), - (r#"foo;"#, Some(array())), - (r"{`This;", Some(array())), - (r#"foo;"#, Some(array())), - (r#"foo;"#, Some(array())), - (r#"foo"#, Some(array())), - (r"", Some(array())), - (r"{function(e)", Some(array())), - (r"
", Some(array())), - (r"{() void 0} />", Some(array())), - (r"", Some(array())), - (r#"{alt"#, Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r"Foo", Some(array())), - (r"

This is descriptive!

", Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r#"This is descriptive!"#, Some(array())), - (r"{altText}", Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r#""#, Some(array())), - (r"", Some(array())), + (r#"foo;"#, None, None), + (r#"{"foo"};"#, None, None), + (r"{alt};", None, None), + (r#"foo;"#, None, None), + (r"{`This;", None, None), + (r#"foo;"#, None, None), + (r#"foo;"#, None, None), + (r#"foo"#, None, None), + (r"", None, None), + (r"
", None, None), + (r"{function(e)", None, None), + (r"
", None, None), + (r"{() void 0} />", None, None), + (r"", None, None), + (r"test", None, None), + (r#"{alt"#, None, None), + (r"{photo.caption};", None, None), + (r"{bar()};", None, None), + (r#"{foo.bar"#, None, None), + (r#"{bar()"#, None, None), + (r#"{foo.bar()"#, None, None), + (r#""#, None, None), + (r"{`${undefined}`}", None, None), + (r#" "#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#"this is lit..."#, None, None), + (r#"{error"#, None, None), + (r#"{undefined"#, None, None), + (r#"{plugin.name"#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"Foo", None, None), + (r"

This is descriptive!

", None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#"This is descriptive!"#, None, None), + (r"{altText}", None, None), + (r"", None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#";"#, Some(config()), None), + (r#";"#, Some(config()), None), + (r";", Some(config()), None), + (r#";"#, Some(config()), None), + (r";", Some(config()), None), + (r#";"#, Some(config()), None), + (r#";"#, Some(config()), None), + (r#""#, Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"
", Some(config()), None), + (r" void 0} />", Some(config()), None), + (r"", Some(config()), None), + (r#""#, Some(config()), None), + (r#"foo;"#, Some(config()), None), + (r#"{"foo"};"#, Some(config()), None), + (r"{alt};", Some(config()), None), + (r#"foo;"#, Some(config()), None), + (r"{`This;", Some(config()), None), + (r#"foo;"#, Some(config()), None), + (r#"foo;"#, Some(config()), None), + (r#"foo"#, Some(config()), None), + (r"", Some(config()), None), + (r"{function(e)", Some(config()), None), + (r"
", Some(config()), None), + (r"{() void 0} />", Some(config()), None), + (r"", Some(config()), None), + (r#"{alt"#, Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r"Foo", Some(config()), None), + (r"

This is descriptive!

", Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r#"This is descriptive!"#, Some(config()), None), + (r"{altText}", Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r#""#, Some(config()), None), + (r"", Some(config()), None), ]; let fail = vec![ - (r";", None), - (r";", None), - (r"{undefined};", None), - (r#""#, None), - (r"", None), - (r"", None), + (r";", None, None), + (r";", None, None), + (r"{undefined};", None, None), + (r#""#, None, None), + (r"", None, None), + (r"", None, None), // TODO: Could support if get_prop_value could evaluate // some logical expressions - // (r#"{false"#, None), - (r#"{undefined};"#, None), - (r#";"#, None), - (r#";"#, None), - (r#";"#, None), - (r"", None), - (r"", None), - (r#""#, None), - (r#""#, None), - // TODO: When polymorphic components are supported - // (r#""#, None), - (r"", None), - (r"
", None), - (r"", None), - (r#""#, None), - (r#""#, None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"{undefined}", None), - (r#""#, None), - (r"", None), - (r#""#, None), - (r"", None), - (r#""#, None), - (r"", None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#"Foo"#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r#""#, None), - (r";", Some(array())), - (r";", Some(array())), - (r";", Some(array())), - (r#""#, Some(array())), - (r"", Some(array())), - (r";", Some(array())), - (r";", Some(array())), - (r"{undefined};", Some(array())), - (r#""#, Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"
", Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"{undefined}", Some(array())), - (r#""#, Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"", Some(array())), - (r"Foo", Some(array())), - (r"", Some(array())), - (r#""#, None), + // (r#"{false"#, None, None), + (r#"{undefined};"#, None, None), + (r#";"#, None, None), + (r#";"#, None, None), + (r#";"#, None, None), + (r"", None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + ( + r#""#, + None, + Some(serde_json::json!({ "jsx-a11y": { "polymorphicPropName": "as" } })), + ), + (r"", None, None), + (r"
", None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"{undefined}", None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r"", None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#"Foo"#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r#""#, None, None), + (r";", Some(config()), None), + (r";", Some(config()), None), + (r";", Some(config()), None), + (r#""#, Some(config()), None), + (r"", Some(config()), None), + (r";", Some(config()), None), + (r";", Some(config()), None), + (r"{undefined};", Some(config()), None), + (r#""#, Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"
", Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"{undefined}", Some(config()), None), + (r#""#, Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"", Some(config()), None), + (r"Foo", Some(config()), None), + (r"", Some(config()), None), + (r#""#, None, None), ]; Tester::new(AltText::NAME, pass, fail).with_jsx_a11y_plugin(true).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs index b3435ee47..65c9a5b1a 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_has_content.rs @@ -17,7 +17,7 @@ use oxc_allocator::Vec; use crate::{ context::LintContext, rule::Rule, - utils::{get_prop_value, has_jsx_prop_lowercase}, + utils::{get_element_type, get_prop_value, has_jsx_prop_lowercase}, AstNode, }; @@ -75,8 +75,7 @@ declare_oxc_lint!( impl Rule for AnchorHasContent { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXElement(jsx_el) = node.kind() { - let JSXElementName::Identifier(iden) = &jsx_el.opening_element.name else { return }; - let name = iden.name.as_str(); + let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { return }; if name == "a" { // check self attr if has_jsx_prop_lowercase(&jsx_el.opening_element, "aria-hidden").is_some() { @@ -163,30 +162,36 @@ fn test() { // https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-jsx-a11y/main/__tests__/src/rules/anchor-has-content-test.js let pass = vec![ - (r"
;", None), - (r"Foo", None), - (r"", None), - (r"{foo}", None), - (r"{foo.bar}", None), - (r#""#, None), - (r"", None), - // TODO: - // { code: 'foo', settings: { 'jsx-a11y': { components: { Link: 'a' } } }, }, - (r"", None), - (r"", None), - (r"", None), - (r"Foo", None), + (r"
;", None, None), + (r"Foo", None, None), + (r"", None, None), + (r"{foo}", None, None), + (r"{foo.bar}", None, None), + (r#""#, None, None), + (r"", None, None), + ( + r"foo", + None, + Some(serde_json::json!({ "jsx-a11y": { "components": { "Link": "a" } } })), + ), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"Foo", None, None), ]; let fail = vec![ - (r"", None), - (r"", None), - (r"{undefined}", None), - // TODO: - // { code: '', errors: [expectedError], settings: { 'jsx-a11y': { components: { Link: 'a' } } }, }, - (r"", None), - (r"{null}", None), - (r"", None), + (r"", None, None), + (r"", None, None), + (r"{undefined}", None, None), + ( + r"", + None, + Some(serde_json::json!({ "jsx-a11y": { "components": { "Link": "a" } } })), + ), + (r"", None, None), + (r"{null}", None, None), + (r"", None, None), ]; Tester::new(AnchorHasContent::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs index 9e572b31c..2699a52b4 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs @@ -9,7 +9,12 @@ use oxc_diagnostics::{ use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop_lowercase, AstNode}; +use crate::{ + context::LintContext, + rule::Rule, + utils::{get_element_type, has_jsx_prop_lowercase}, + AstNode, +}; #[derive(Debug, Error, Diagnostic)] enum AnchorIsValidDiagnostic { @@ -108,10 +113,12 @@ declare_oxc_lint!( ); impl Rule for AnchorIsValid { + // TODO: Implement from_configuration() and test it + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXElement(jsx_el) = node.kind() { let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else { return }; - let name = ident.name.as_str(); + let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else { return }; if name == "a" { if let Option::Some(herf_attr) = has_jsx_prop_lowercase(&jsx_el.opening_element, "href") @@ -212,24 +219,24 @@ fn test() { // https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-jsx-a11y/main/__tests__/src/rules/anchor-is-valid-test.js let pass = vec![ - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"
", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"test", None), - (r"", None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"
", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"test", None, None), + (r"", None, None), // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), @@ -248,7 +255,13 @@ fn test() { // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), - (r"", None), + ( + r"", + None, + Some( + serde_json::json!({ "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } }), + ), + ), // (r#""#, Some(serde_json::json!(specialLink))), // (r#""#, Some(serde_json::json!(specialLink))), // (r#""#, Some(serde_json::json!(specialLink))), @@ -284,16 +297,16 @@ fn test() { // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), // (r#"test"#, Some(serde_json::json!(componentsAndSpecialLink))), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r"
void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r"
void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), // (r#" void 0} />"#, Some(serde_json::json!(components))), // (r#" void 0} />"#, Some(serde_json::json!(components))), // (r#" void 0} />"#, Some(serde_json::json!(components))), @@ -460,18 +473,18 @@ fn test() { ]; let fail = vec![ - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r"", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), - (r" void 0} />", None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r"", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), + (r" void 0} />", None, None), // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), // (r#""#, Some(serde_json::json!(components))), @@ -505,7 +518,13 @@ fn test() { // r#" void 0} />"#, // Some(serde_json::json!(components)), // ), - // (r#" void 0} />"#, None), + ( + r" void 0} />", + None, + Some( + serde_json::json!({ "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } }), + ), + ), // (r#""#, Some(serde_json::json!(specialLink))), // (r#""#, Some(serde_json::json!(specialLink))), // (r#"", None), - (r"