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 af8b630d79872..5bea6556e2ff4 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
@@ -172,9 +172,8 @@ impl Rule for AltText {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
- let Some(name) = &get_element_type(ctx, jsx_el) else {
- return;
- };
+
+ let name = &get_element_type(ctx, jsx_el);
//
if let Some(custom_tags) = &self.img {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_ambiguous_text.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_ambiguous_text.rs
index 49c3316f0ebb8..1a04fdfdcf8b1 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/anchor_ambiguous_text.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_ambiguous_text.rs
@@ -107,9 +107,7 @@ impl Rule for AnchorAmbiguousText {
return;
};
- let Some(name) = get_element_type(ctx, &jsx_el.opening_element) else {
- return;
- };
+ let name = get_element_type(ctx, &jsx_el.opening_element);
if name != "a" {
return;
@@ -167,15 +165,14 @@ fn get_accessible_text<'a, 'b>(
};
}
- if let Some(name) = get_element_type(ctx, &jsx_el.opening_element) {
- if name == "img" {
- if let Some(alt_text) = has_jsx_prop_ignore_case(&jsx_el.opening_element, "alt") {
- if let Some(text) = get_string_literal_prop_value(alt_text) {
- return Some(Cow::Borrowed(text));
- };
+ let name = get_element_type(ctx, &jsx_el.opening_element);
+ if name == "img" {
+ if let Some(alt_text) = has_jsx_prop_ignore_case(&jsx_el.opening_element, "alt") {
+ if let Some(text) = get_string_literal_prop_value(alt_text) {
+ return Some(Cow::Borrowed(text));
};
- }
- };
+ };
+ }
if is_hidden_from_screen_reader(ctx, &jsx_el.opening_element) {
return None;
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 5a73ece9db63b..18dfa6c782367 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
@@ -64,9 +64,8 @@ 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 Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else {
- return;
- };
+ let name = get_element_type(ctx, &jsx_el.opening_element);
+
if name == "a" {
if is_hidden_from_screen_reader(ctx, &jsx_el.opening_element) {
return;
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 92663980089a7..f53e598040bf0 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
@@ -129,9 +129,8 @@ impl Rule for AnchorIsValid {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXElement(jsx_el) = node.kind() {
- let Some(name) = &get_element_type(ctx, &jsx_el.opening_element) else {
- return;
- };
+ let name = get_element_type(ctx, &jsx_el.opening_element);
+
if name != "a" {
return;
};
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
index 908e84ad5b167..84c276c9e8ef6 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_activedescendant_has_tabindex.rs
@@ -64,9 +64,7 @@ impl Rule for AriaActivedescendantHasTabindex {
return;
};
- let Some(element_type) = get_element_type(ctx, jsx_opening_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_opening_el);
if !HTML_TAG.contains(&element_type) {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs
index 7b0712f7fef8f..91376a4398903 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_role.rs
@@ -143,9 +143,7 @@ impl Rule for AriaRole {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXElement(jsx_el) = node.kind() {
if let Some(aria_role) = has_jsx_prop(&jsx_el.opening_element, "role") {
- let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else {
- return;
- };
+ let element_type = get_element_type(ctx, &jsx_el.opening_element);
if self.ignore_non_dom && !HTML_TAG.contains(&element_type) {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs
index 9a8a9a2d9287b..4a4b933247998 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_unsupported_elements.rs
@@ -49,9 +49,7 @@ fn aria_unsupported_elements_diagnostic(span: Span, x1: &str) -> OxcDiagnostic {
impl Rule for AriaUnsupportedElements {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
- let Some(el_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let el_type = get_element_type(ctx, jsx_el);
if RESERVED_HTML_TAG.contains(&el_type) {
for attr in &jsx_el.attributes {
let attr = match attr {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs
index 5f87714f7bd81..e230ef0fc95a4 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/autocomplete_valid.rs
@@ -181,9 +181,8 @@ impl Rule for AutocompleteValid {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
- let Some(name) = &get_element_type(ctx, jsx_el) else {
- return;
- };
+ let name = &get_element_type(ctx, jsx_el);
+
if !self.input_components.contains(name.as_ref()) {
return;
}
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs b/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs
index af9e24015cb87..fe26185a59743 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/click_events_have_key_events.rs
@@ -59,9 +59,8 @@ impl Rule for ClickEventsHaveKeyEvents {
};
// Check only native DOM elements or custom component via settings
- let Some(element_type) = get_element_type(ctx, jsx_opening_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_opening_el);
+
if !HTML_TAG.contains(&element_type) {
return;
};
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs
index c01549f8ff3f9..cc6954e614f7f 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/heading_has_content.rs
@@ -89,9 +89,7 @@ impl Rule for HeadingHasContent {
// };
// let name = iden.name.as_str();
- let Some(name) = &get_element_type(ctx, jsx_el) else {
- return;
- };
+ let name = &get_element_type(ctx, jsx_el);
if !DEFAULT_COMPONENTS.iter().any(|&comp| comp == name)
&& !self
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs b/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs
index bff4a6dbf5e14..53b22b91058b9 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/html_has_lang.rs
@@ -61,9 +61,7 @@ impl Rule for HtmlHasLang {
return;
};
- let Some(element_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_el);
if element_type != "html" {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs
index 244d40902b2ce..99695e766ac38 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/iframe_has_title.rs
@@ -66,9 +66,7 @@ impl Rule for IframeHasTitle {
return;
};
- let Some(name) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let name = get_element_type(ctx, jsx_el);
if name != "iframe" {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs
index ef502c5a66cc1..cfcf3c36ba41b 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/img_redundant_alt.rs
@@ -125,9 +125,8 @@ impl Rule for ImgRedundantAlt {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
- let Some(element_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+
+ let element_type = get_element_type(ctx, jsx_el);
if !self.types_to_validate.iter().any(|comp| comp == &element_type) {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/label_has_associated_control.rs b/crates/oxc_linter/src/rules/jsx_a11y/label_has_associated_control.rs
index 4167d6d1918bc..79ab8f2601d96 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/label_has_associated_control.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/label_has_associated_control.rs
@@ -206,9 +206,7 @@ impl Rule for LabelHasAssociatedControl {
return;
};
- let Some(element_type) = get_element_type(ctx, &element.opening_element) else {
- return;
- };
+ let element_type = get_element_type(ctx, &element.opening_element);
if self.label_components.binary_search(&element_type.into()).is_err() {
return;
@@ -295,10 +293,9 @@ impl LabelHasAssociatedControl {
match node {
JSXChild::ExpressionContainer(_) => true,
JSXChild::Element(element) => {
- if let Some(element_type) = get_element_type(ctx, &element.opening_element) {
- if self.control_components.is_match(element_type.to_string()) {
- return true;
- }
+ let element_type = get_element_type(ctx, &element.opening_element);
+ if self.control_components.is_match(element_type.to_string()) {
+ return true;
}
for child in &element.children {
@@ -359,12 +356,11 @@ impl LabelHasAssociatedControl {
}
if element.children.is_empty() {
- if let Some(name) = get_element_type(ctx, &element.opening_element) {
- if is_react_component_name(&name)
- && !self.control_components.is_match(name.to_string())
- {
- return true;
- }
+ let name = get_element_type(ctx, &element.opening_element);
+ if is_react_component_name(&name)
+ && !self.control_components.is_match(name.to_string())
+ {
+ return true;
}
}
@@ -1589,6 +1585,13 @@ fn test() {
}])),
None,
),
+ (
+ "",
+ Some(serde_json::json!([{
+ "labelComponents": ["FilesContext.Provider"],
+ }])),
+ None,
+ ),
];
Tester::new(LabelHasAssociatedControl::NAME, LabelHasAssociatedControl::CATEGORY, pass, fail)
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs
index 9ee3e5f3aa78c..306d894fd7b9a 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/lang.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/lang.rs
@@ -63,9 +63,7 @@ impl Rule for Lang {
return;
};
- let Some(element_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_el);
if element_type != "html" {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs b/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs
index 65044d46a5419..03abb3d446c1b 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/media_has_caption.rs
@@ -110,9 +110,7 @@ impl Rule for MediaHasCaption {
return;
};
- let Some(element_name) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let element_name = get_element_type(ctx, jsx_el);
let is_audio_or_video =
self.0.audio.contains(&element_name) || self.0.video.contains(&element_name);
@@ -158,9 +156,8 @@ impl Rule for MediaHasCaption {
} else {
parent.children.iter().any(|child| match child {
JSXChild::Element(child_el) => {
- let Some(child_name) = get_element_type(ctx, &child_el.opening_element) else {
- return false;
- };
+ let child_name = get_element_type(ctx, &child_el.opening_element);
+
self.0.track.contains(&child_name)
&& child_el.opening_element.attributes.iter().any(|attr| {
if let JSXAttributeItem::Attribute(attr) = attr {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs b/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs
index 9faa759c6d43d..7017a22a2cfdc 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/mouse_events_have_key_events.rs
@@ -102,9 +102,7 @@ impl Rule for MouseEventsHaveKeyEvents {
return;
};
- let Some(el_type) = get_element_type(ctx, jsx_opening_el) else {
- return;
- };
+ let el_type = get_element_type(ctx, jsx_opening_el);
if !HTML_TAG.contains(&el_type) {
return;
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs
index 1fb2f6b1b92f3..41fc2159cd604 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/no_aria_hidden_on_focusable.rs
@@ -92,9 +92,7 @@ fn is_aria_hidden_true(attr: &JSXAttributeItem) -> bool {
///
/// `true` if the element is focusable, `false` otherwise.
fn is_focusable<'a>(ctx: &LintContext<'a>, element: &JSXOpeningElement<'a>) -> bool {
- let Some(tag_name) = get_element_type(ctx, element) else {
- return false;
- };
+ let tag_name = get_element_type(ctx, element);
if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_ignore_case(element, "tabIndex") {
if let Some(attr_value) = &attr.value {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs
index 3cc9f0579cb9e..2114081f3f5f4 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs
@@ -100,9 +100,8 @@ impl Rule for NoAutofocus {
let Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") else {
return;
};
- let Some(element_type) = get_element_type(ctx, &jsx_el.opening_element) else {
- return;
- };
+
+ let element_type = get_element_type(ctx, &jsx_el.opening_element);
if self.ignore_non_dom {
if HTML_TAG.contains(&element_type) {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_distracting_elements.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_distracting_elements.rs
index 1d980ed1a9f35..7dcbfafec8d1c 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/no_distracting_elements.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/no_distracting_elements.rs
@@ -58,9 +58,8 @@ impl Rule for NoDistractingElements {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
- let Some(element_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+
+ let element_type = get_element_type(ctx, jsx_el);
if let "marquee" | "blink" = element_type.as_ref() {
ctx.diagnostic(no_distracting_elements_diagnostic(jsx_el.name.span()));
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs
index 6fda1ca69c918..cf4dc6c3d7fd3 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs
@@ -61,9 +61,8 @@ impl Rule for NoRedundantRoles {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
- let Some(component) = get_element_type(ctx, jsx_el) else {
- return;
- };
+
+ let component = get_element_type(ctx, jsx_el);
if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_ignore_case(jsx_el, "role") {
if let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value {
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs b/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs
index 4271ce04acc6d..00a7f897d9d33 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs
@@ -92,10 +92,9 @@ lazy_static! {
impl Rule for PreferTagOverRole {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
- if let Some(name) = get_element_type(ctx, jsx_el) {
- if let Some(role_prop) = has_jsx_prop_ignore_case(jsx_el, "role") {
- Self::check_roles(role_prop, &ROLE_TO_TAG_MAP, &name, ctx);
- }
+ let name = get_element_type(ctx, jsx_el);
+ if let Some(role_prop) = has_jsx_prop_ignore_case(jsx_el, "role") {
+ Self::check_roles(role_prop, &ROLE_TO_TAG_MAP, &name, ctx);
}
}
}
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs
index fec0865ad4cab..884fa9b32f128 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/role_supports_aria_props.rs
@@ -68,9 +68,8 @@ impl Rule for RoleSupportsAriaProps {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
- let Some(el_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+
+ let el_type = get_element_type(ctx, jsx_el);
let role = has_jsx_prop_ignore_case(jsx_el, "role");
let role_value = role.map_or_else(
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/scope.rs b/crates/oxc_linter/src/rules/jsx_a11y/scope.rs
index 070ae8e4f9fa6..8026c1cff3fb8 100644
--- a/crates/oxc_linter/src/rules/jsx_a11y/scope.rs
+++ b/crates/oxc_linter/src/rules/jsx_a11y/scope.rs
@@ -65,9 +65,7 @@ impl Rule for Scope {
}
};
- let Some(element_type) = get_element_type(ctx, jsx_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_el);
if element_type == "th" {
return;
diff --git a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
index 280b8dd8faa48..6247c1143188d 100644
--- a/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
+++ b/crates/oxc_linter/src/rules/react/checked_requires_onchange_or_readonly.rs
@@ -69,9 +69,8 @@ impl Rule for CheckedRequiresOnchangeOrReadonly {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.kind() {
AstKind::JSXOpeningElement(jsx_opening_el) => {
- let Some(element_type) = get_element_type(ctx, jsx_opening_el) else {
- return;
- };
+ let element_type = get_element_type(ctx, jsx_opening_el);
+
if element_type != "input" {
return;
}
diff --git a/crates/oxc_linter/src/snapshots/jsx_a11y_label_has_associated_control.snap b/crates/oxc_linter/src/snapshots/jsx_a11y_label_has_associated_control.snap
index d1d6c78b40ff8..90815e73a3c94 100644
--- a/crates/oxc_linter/src/snapshots/jsx_a11y_label_has_associated_control.snap
+++ b/crates/oxc_linter/src/snapshots/jsx_a11y_label_has_associated_control.snap
@@ -616,3 +616,10 @@ snapshot_kind: text
· ────────────────────────────────────
╰────
help: Either give the label a `htmlFor` attribute with the id of the associated control, or wrap the label around the control.
+
+ ⚠ eslint-plugin-jsx-a11y(label-has-associated-control): A form label must have accessible text.
+ ╭─[label_has_associated_control.tsx:1:1]
+ 1 │
+ · ───────────────────────────────────────────────────────
+ ╰────
+ help: Ensure the label either has text inside it or is accessibly labelled using an attribute such as `aria-label`, or `aria-labelledby`. You can mark more attributes as accessible labels by configuring the `labelAttributes` option.
diff --git a/crates/oxc_linter/src/utils/react.rs b/crates/oxc_linter/src/utils/react.rs
index ff9bf38f79a62..eeee8425a75cd 100644
--- a/crates/oxc_linter/src/utils/react.rs
+++ b/crates/oxc_linter/src/utils/react.rs
@@ -3,7 +3,8 @@ use std::borrow::Cow;
use oxc_ast::{
ast::{
CallExpression, Expression, JSXAttributeItem, JSXAttributeName, JSXAttributeValue,
- JSXChild, JSXElement, JSXExpression, JSXOpeningElement, MemberExpression,
+ JSXChild, JSXElement, JSXElementName, JSXExpression, JSXMemberExpression,
+ JSXMemberExpressionObject, JSXOpeningElement, MemberExpression,
},
match_member_expression, AstKind,
};
@@ -65,14 +66,13 @@ pub fn is_hidden_from_screen_reader<'a>(
ctx: &LintContext<'a>,
node: &JSXOpeningElement<'a>,
) -> bool {
- if let Some(name) = get_element_type(ctx, node) {
- if name.eq_ignore_ascii_case("input") {
- if let Some(item) = has_jsx_prop_ignore_case(node, "type") {
- let hidden = get_string_literal_prop_value(item);
+ let name = get_element_type(ctx, node);
+ if name.eq_ignore_ascii_case("input") {
+ if let Some(item) = has_jsx_prop_ignore_case(node, "type") {
+ let hidden = get_string_literal_prop_value(item);
- if hidden.is_some_and(|val| val.eq_ignore_ascii_case("hidden")) {
- return true;
- }
+ if hidden.is_some_and(|val| val.eq_ignore_ascii_case("hidden")) {
+ return true;
}
}
}
@@ -204,14 +204,34 @@ pub fn get_parent_component<'a, 'b>(
None
}
+fn get_jsx_mem_expr_name<'a>(jsx_mem_expr: &JSXMemberExpression) -> Cow<'a, str> {
+ let prefix = match &jsx_mem_expr.object {
+ JSXMemberExpressionObject::IdentifierReference(id) => Cow::Borrowed(id.name.as_str()),
+ JSXMemberExpressionObject::MemberExpression(mem_expr) => {
+ Cow::Owned(format!("{}.{}", get_jsx_mem_expr_name(mem_expr), mem_expr.property.name))
+ }
+ JSXMemberExpressionObject::ThisExpression(_) => Cow::Borrowed("this"),
+ };
+
+ Cow::Owned(format!("{}.{}", prefix, jsx_mem_expr.property.name))
+}
+
/// Resolve element type(name) using jsx-a11y settings
/// ref:
///
pub fn get_element_type<'c, 'a>(
context: &'c LintContext<'a>,
element: &JSXOpeningElement<'a>,
-) -> Option> {
- let name = element.name.get_identifier_name()?;
+) -> Cow<'c, str> {
+ let name = match &element.name {
+ JSXElementName::Identifier(id) => Cow::Borrowed(id.as_ref().name.as_str()),
+ JSXElementName::IdentifierReference(id) => Cow::Borrowed(id.as_ref().name.as_str()),
+ JSXElementName::NamespacedName(namespaced) => {
+ Cow::Owned(format!("{}:{}", namespaced.namespace.name, namespaced.property.name))
+ }
+ JSXElementName::MemberExpression(jsx_mem_expr) => get_jsx_mem_expr_name(jsx_mem_expr),
+ JSXElementName::ThisExpression(_) => Cow::Borrowed("this"),
+ };
let OxlintSettings { jsx_a11y, .. } = context.settings();
@@ -225,10 +245,10 @@ pub fn get_element_type<'c, 'a>(
.and_then(JSXAttributeValue::as_string_literal)
.map(|s| s.value.as_str());
- let raw_type = polymorphic_prop.unwrap_or_else(|| name.as_str());
- match jsx_a11y.components.get(raw_type) {
- Some(component) => Some(Cow::Borrowed(component)),
- None => Some(Cow::Borrowed(raw_type)),
+ let raw_type = polymorphic_prop.map_or(name, Cow::Borrowed);
+ match jsx_a11y.components.get(raw_type.as_ref()) {
+ Some(component) => Cow::Borrowed(component),
+ None => raw_type,
}
}