diff --git a/.changeset/tasty-books-fold.md b/.changeset/tasty-books-fold.md new file mode 100644 index 000000000000..ffe96885c57d --- /dev/null +++ b/.changeset/tasty-books-fold.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#7079](https://github.com/biomejs/biome/issues/7079). Now the rule [`useSemanticElements`](https://biomejs.dev/linter/rules/use-semantic-elements/) doesn't trigger components and custom elements. diff --git a/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs b/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs index 26ee2b475d02..5d5ad704d3c0 100644 --- a/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs +++ b/crates/biome_js_analyze/src/lint/a11y/use_semantic_elements.rs @@ -5,8 +5,9 @@ use biome_aria_metadata::AriaRole; use biome_console::markup; use biome_deserialize::TextRange; use biome_diagnostics::Severity; -use biome_js_syntax::{JsxAttribute, JsxOpeningElement, JsxSelfClosingElement}; -use biome_rowan::{AstNode, declare_node_union}; +use biome_js_syntax::JsxAttribute; +use biome_js_syntax::jsx_ext::AnyJsxElement; +use biome_rowan::AstNode; use biome_rule_options::use_semantic_elements::UseSemanticElementsOptions; declare_lint_rule! { @@ -61,24 +62,20 @@ declare_lint_rule! { } } -declare_node_union! { - pub AnyOpeningElement = JsxOpeningElement | JsxSelfClosingElement -} - impl Rule for UseSemanticElements { - type Query = Ast; + type Query = Ast; type State = JsxAttribute; type Signals = Option; type Options = UseSemanticElementsOptions; fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); - let role_attribute = match node { - AnyOpeningElement::JsxOpeningElement(node) => node.find_attribute_by_name("role")?, - AnyOpeningElement::JsxSelfClosingElement(node) => { - node.find_attribute_by_name("role")? - } - }; + + if node.is_custom_component() || node.is_custom_element() { + return None; + } + + let role_attribute = node.find_attribute_by_name("role")?; let role_value = role_attribute.as_static_value()?; let role_value = role_value.as_string_constant()?; diff --git a/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx b/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx index ae610a80656e..7794921f0795 100644 --- a/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx +++ b/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx @@ -31,4 +31,14 @@ export const Component2 = () => (
- \ No newline at end of file + + +<> +
+ + + {children} + + diff --git a/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx.snap b/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx.snap index 9437e7ffd212..9cd867f9dbd6 100644 --- a/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx.snap +++ b/crates/biome_js_analyze/tests/specs/a11y/useSemanticElements/valid.jsx.snap @@ -38,4 +38,15 @@ export const Component2 = () => (
+ +<> +
+ + + {children} + + + ``` diff --git a/crates/biome_js_syntax/src/jsx_ext.rs b/crates/biome_js_syntax/src/jsx_ext.rs index 2746a7ae1d4f..9da18b0db09b 100644 --- a/crates/biome_js_syntax/src/jsx_ext.rs +++ b/crates/biome_js_syntax/src/jsx_ext.rs @@ -6,6 +6,7 @@ use crate::{ JsxSelfClosingElement, JsxString, inner_string_text, static_value::StaticValue, }; use biome_rowan::{AstNode, AstNodeList, SyntaxResult, TokenText, declare_node_union}; +use biome_string_case::StrOnlyExtension; impl JsxString { /// Returns the inner text of a string not including the quotes. @@ -378,6 +379,22 @@ impl AnyJsxElement { self.name().is_ok_and(|it| it.as_jsx_name().is_none()) } + /// Whether the current element is a custom element. + /// + /// A custom element must contain dashes and its name is all lower case. + pub fn is_custom_element(&self) -> bool { + self.name() + .ok() + .and_then(|it| it.as_jsx_name().cloned()) + .and_then(|element| element.value_token().ok()) + .is_some_and(|token| { + token.text_trimmed().contains('-') + && token + .text_trimmed() + .eq(token.text_trimmed().to_lowercase_cow().as_ref()) + }) + } + /// Returns `true` if the current element is an HTML element. /// /// - `` is a component and it would return `false`