perf(linter): remove allocations for string comparisons (#4570)

Refactors a lot of case-insensitive comparisons from
```rust
a.to_lowercase() == b.to_lowercase()
```

with
```rust
a.eq_ignore_ascii_case(b)
```

These mostly happened when checking JSX props, so I'm expecting the most benefit from JSX-related rules.
This commit is contained in:
DonIsaac 2024-07-31 00:24:12 +00:00
parent 0914e47660
commit 7585e16beb
27 changed files with 92 additions and 95 deletions

View file

@ -1267,8 +1267,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for RegExpLiteral<'a> {
let last = p.peek_nth(0);
// Avoid forming a single-line comment or "</script" sequence
if Some('/') == last
|| (Some('<') == last
&& self.regex.pattern.as_str().to_lowercase().starts_with("script"))
|| (Some('<') == last && self.regex.pattern.to_lowercase().starts_with("script"))
{
p.print_hard_space();
}

View file

@ -10,7 +10,7 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
get_element_type, get_prop_value, get_string_literal_prop_value, has_jsx_prop_lowercase,
get_element_type, get_prop_value, get_string_literal_prop_value, has_jsx_prop_ignore_case,
object_has_accessible_child,
},
AstNode,
@ -205,8 +205,8 @@ impl Rule for AltText {
// <input type="image">
if let Some(custom_tags) = &self.input_type_image {
let has_input_with_type_image = name.to_lowercase() == "input"
&& has_jsx_prop_lowercase(jsx_el, "type").map_or(false, |v| {
let has_input_with_type_image = name.eq_ignore_ascii_case("input")
&& has_jsx_prop_ignore_case(jsx_el, "type").map_or(false, |v| {
get_string_literal_prop_value(v).map_or(false, |v| v == "image")
});
if has_input_with_type_image || custom_tags.iter().any(|i| i == name) {
@ -247,26 +247,26 @@ fn aria_label_has_value<'a>(item: &'a JSXAttributeItem<'a>) -> bool {
}
fn img_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) {
if let Some(alt_prop) = has_jsx_prop_lowercase(node, "alt") {
if let Some(alt_prop) = has_jsx_prop_ignore_case(node, "alt") {
if !is_valid_alt_prop(alt_prop) {
ctx.diagnostic(missing_alt_value(node.span));
}
return;
}
if has_jsx_prop_lowercase(node, "role").map_or(false, is_presentation_role) {
if has_jsx_prop_ignore_case(node, "role").map_or(false, is_presentation_role) {
ctx.diagnostic(prefer_alt(node.span));
return;
}
if let Some(aria_label_prop) = has_jsx_prop_lowercase(node, "aria-label") {
if let Some(aria_label_prop) = has_jsx_prop_ignore_case(node, "aria-label") {
if !aria_label_has_value(aria_label_prop) {
ctx.diagnostic(aria_label_value(node.span));
}
return;
}
if let Some(aria_labelledby_prop) = has_jsx_prop_lowercase(node, "aria-labelledby") {
if let Some(aria_labelledby_prop) = has_jsx_prop_ignore_case(node, "aria-labelledby") {
if !aria_label_has_value(aria_labelledby_prop) {
ctx.diagnostic(aria_labelled_by_value(node.span));
}
@ -282,11 +282,11 @@ fn object_rule<'a>(
ctx: &LintContext<'a>,
) {
let has_aria_label =
has_jsx_prop_lowercase(node, "aria-label").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-label").map_or(false, aria_label_has_value);
let has_aria_labelledby =
has_jsx_prop_lowercase(node, "aria-labelledby").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-labelledby").map_or(false, aria_label_has_value);
let has_label = has_aria_label || has_aria_labelledby;
let has_title_attr = has_jsx_prop_lowercase(node, "title")
let has_title_attr = has_jsx_prop_ignore_case(node, "title")
.and_then(get_string_literal_prop_value)
.map_or(false, |v| !v.is_empty());
@ -298,14 +298,14 @@ fn object_rule<'a>(
fn area_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) {
let has_aria_label =
has_jsx_prop_lowercase(node, "aria-label").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-label").map_or(false, aria_label_has_value);
let has_aria_labelledby =
has_jsx_prop_lowercase(node, "aria-labelledby").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-labelledby").map_or(false, aria_label_has_value);
let has_label = has_aria_label || has_aria_labelledby;
if has_label {
return;
}
has_jsx_prop_lowercase(node, "alt").map_or_else(
has_jsx_prop_ignore_case(node, "alt").map_or_else(
|| {
ctx.diagnostic(area(node.span));
},
@ -319,14 +319,14 @@ fn area_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) {
fn input_type_image_rule<'a>(node: &'a JSXOpeningElement<'a>, ctx: &LintContext<'a>) {
let has_aria_label =
has_jsx_prop_lowercase(node, "aria-label").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-label").map_or(false, aria_label_has_value);
let has_aria_labelledby =
has_jsx_prop_lowercase(node, "aria-labelledby").map_or(false, aria_label_has_value);
has_jsx_prop_ignore_case(node, "aria-labelledby").map_or(false, aria_label_has_value);
let has_label = has_aria_label || has_aria_labelledby;
if has_label {
return;
}
has_jsx_prop_lowercase(node, "alt").map_or_else(
has_jsx_prop_ignore_case(node, "alt").map_or_else(
|| {
ctx.diagnostic(input_type_image(node.span));
},

View file

@ -7,7 +7,7 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
get_element_type, has_jsx_prop_lowercase, is_hidden_from_screen_reader,
get_element_type, has_jsx_prop_ignore_case, is_hidden_from_screen_reader,
object_has_accessible_child,
},
AstNode,
@ -79,7 +79,7 @@ impl Rule for AnchorHasContent {
}
for attr in ["title", "aria-label"] {
if has_jsx_prop_lowercase(&jsx_el.opening_element, attr).is_some() {
if has_jsx_prop_ignore_case(&jsx_el.opening_element, attr).is_some() {
return;
};
}

View file

@ -9,7 +9,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase},
utils::{get_element_type, has_jsx_prop_ignore_case},
AstNode,
};
@ -134,7 +134,7 @@ impl Rule for AnchorIsValid {
};
if name == "a" {
if let Option::Some(herf_attr) =
has_jsx_prop_lowercase(&jsx_el.opening_element, "href")
has_jsx_prop_ignore_case(&jsx_el.opening_element, "href")
{
// Check if the 'a' element has a correct href attribute
match herf_attr {
@ -142,7 +142,7 @@ impl Rule for AnchorIsValid {
Some(value) => {
let is_empty = check_value_is_empty(value, &self.0.valid_hrefs);
if is_empty {
if has_jsx_prop_lowercase(&jsx_el.opening_element, "onclick")
if has_jsx_prop_ignore_case(&jsx_el.opening_element, "onclick")
.is_some()
{
ctx.diagnostic(cant_be_anchor(ident.span));

View file

@ -10,7 +10,7 @@ use crate::{
context::LintContext,
globals::HTML_TAG,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase, is_interactive_element, parse_jsx_value},
utils::{get_element_type, has_jsx_prop_ignore_case, is_interactive_element, parse_jsx_value},
AstNode,
};
@ -62,7 +62,7 @@ impl Rule for AriaActivedescendantHasTabindex {
return;
};
if has_jsx_prop_lowercase(jsx_opening_el, "aria-activedescendant").is_none() {
if has_jsx_prop_ignore_case(jsx_opening_el, "aria-activedescendant").is_none() {
return;
};
@ -75,7 +75,7 @@ impl Rule for AriaActivedescendantHasTabindex {
};
if let Some(JSXAttributeItem::Attribute(tab_index_attr)) =
has_jsx_prop_lowercase(jsx_opening_el, "tabIndex")
has_jsx_prop_ignore_case(jsx_opening_el, "tabIndex")
{
if !is_valid_tab_index_attr(tab_index_attr) {
return;

View file

@ -10,7 +10,7 @@ use phf::{phf_map, phf_set};
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase},
utils::{get_element_type, has_jsx_prop_ignore_case},
AstNode,
};
@ -183,7 +183,7 @@ impl Rule for AutocompleteValid {
return;
}
let Some(autocomplete_prop) = has_jsx_prop_lowercase(jsx_el, "autocomplete") else {
let Some(autocomplete_prop) = has_jsx_prop_ignore_case(jsx_el, "autocomplete") else {
return;
};
let attr = match autocomplete_prop {

View file

@ -9,7 +9,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, get_prop_value, has_jsx_prop_lowercase},
utils::{get_element_type, get_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -70,7 +70,7 @@ impl Rule for HtmlHasLang {
return;
};
has_jsx_prop_lowercase(jsx_el, "lang").map_or_else(
has_jsx_prop_ignore_case(jsx_el, "lang").map_or_else(
|| ctx.diagnostic(missing_lang_prop(identifier.span)),
|lang_prop| {
if !is_valid_lang_prop(lang_prop) {

View file

@ -9,7 +9,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, get_prop_value, has_jsx_prop_lowercase},
utils::{get_element_type, get_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -75,7 +75,7 @@ impl Rule for IframeHasTitle {
return;
}
let Some(alt_prop) = has_jsx_prop_lowercase(jsx_el, "title") else {
let Some(alt_prop) = has_jsx_prop_ignore_case(jsx_el, "title") else {
ctx.diagnostic(iframe_has_title_diagnostic(iden.span));
return;
};

View file

@ -11,7 +11,7 @@ use crate::{
context::LintContext,
rule::Rule,
utils::{
get_element_type, get_prop_value, has_jsx_prop_lowercase, is_hidden_from_screen_reader,
get_element_type, get_prop_value, has_jsx_prop_ignore_case, is_hidden_from_screen_reader,
},
AstNode,
};
@ -120,7 +120,7 @@ impl Rule for ImgRedundantAlt {
return;
}
let Some(alt_prop) = has_jsx_prop_lowercase(jsx_el, "alt") else {
let Some(alt_prop) = has_jsx_prop_ignore_case(jsx_el, "alt") else {
return;
};

View file

@ -10,7 +10,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, get_prop_value, has_jsx_prop_lowercase},
utils::{get_element_type, get_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -75,7 +75,7 @@ impl Rule for Lang {
return;
};
has_jsx_prop_lowercase(jsx_el, "lang").map_or_else(
has_jsx_prop_ignore_case(jsx_el, "lang").map_or_else(
|| ctx.diagnostic(lang_diagnostic(identifier.span)),
|lang_prop| {
if !is_valid_lang_prop(lang_prop) {

View file

@ -149,7 +149,7 @@ impl Rule for MediaHasCaption {
if let JSXAttributeName::Identifier(iden) = &attr.name {
if let Some(JSXAttributeValue::StringLiteral(s)) = &attr.value {
return iden.name == "kind"
&& s.value.to_lowercase() == "captions";
&& s.value.eq_ignore_ascii_case("captions");
}
}
}

View file

@ -6,7 +6,7 @@ use oxc_diagnostics::OxcDiagnostic;
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::has_jsx_prop_ignore_case, AstNode};
fn no_access_key_diagnostic(span0: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("No access key attribute allowed.")
@ -42,7 +42,8 @@ impl Rule for NoAccessKey {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_lowercase(jsx_el, "accessKey")
if let Some(JSXAttributeItem::Attribute(attr)) =
has_jsx_prop_ignore_case(jsx_el, "accessKey")
{
match attr.value.as_ref() {
Some(JSXAttributeValue::StringLiteral(_)) => {

View file

@ -9,7 +9,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase, parse_jsx_value},
utils::{get_element_type, has_jsx_prop_ignore_case, parse_jsx_value},
AstNode,
};
@ -46,7 +46,7 @@ impl Rule for NoAriaHiddenOnFocusable {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
if let Some(aria_hidden_prop) = has_jsx_prop_lowercase(jsx_el, "aria-hidden") {
if let Some(aria_hidden_prop) = has_jsx_prop_ignore_case(jsx_el, "aria-hidden") {
if is_aria_hidden_true(aria_hidden_prop) && is_focusable(ctx, jsx_el) {
if let JSXAttributeItem::Attribute(boxed_attr) = aria_hidden_prop {
ctx.diagnostic(no_aria_hidden_on_focusable_diagnostic(boxed_attr.span));
@ -89,16 +89,16 @@ fn is_focusable(ctx: &LintContext, element: &JSXOpeningElement) -> bool {
return false;
};
if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_lowercase(element, "tabIndex") {
if let Some(JSXAttributeItem::Attribute(attr)) = has_jsx_prop_ignore_case(element, "tabIndex") {
if let Some(attr_value) = &attr.value {
return parse_jsx_value(attr_value).map_or(false, |num| num >= 0.0);
}
}
match tag_name.as_str() {
"a" | "area" => has_jsx_prop_lowercase(element, "href").is_some(),
"a" | "area" => has_jsx_prop_ignore_case(element, "href").is_some(),
"button" | "input" | "select" | "textarea" => {
has_jsx_prop_lowercase(element, "disabled").is_none()
has_jsx_prop_ignore_case(element, "disabled").is_none()
}
_ => false,
}

View file

@ -10,7 +10,7 @@ use phf::phf_map;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase},
utils::{get_element_type, has_jsx_prop_ignore_case},
AstNode,
};
@ -55,7 +55,7 @@ impl Rule for NoRedundantRoles {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
if let Some(component) = get_element_type(ctx, jsx_el) {
if let Some(JSXAttributeItem::Attribute(attr)) =
has_jsx_prop_lowercase(jsx_el, "role")
has_jsx_prop_ignore_case(jsx_el, "role")
{
if let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value {
let roles: Vec<String> = role_values

View file

@ -11,7 +11,7 @@ use phf::phf_map;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase},
utils::{get_element_type, has_jsx_prop_ignore_case},
AstNode,
};
@ -90,7 +90,7 @@ 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_lowercase(jsx_el, "role") {
if let Some(role_prop) = has_jsx_prop_ignore_case(jsx_el, "role") {
Self::check_roles(role_prop, &ROLE_TO_TAG_MAP, &name, ctx);
}
}

View file

@ -7,7 +7,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use phf::{phf_map, phf_set};
use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop_lowercase, AstNode};
use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop_ignore_case, AstNode};
fn role_has_required_aria_props_diagnostic(span: Span, role: &str, props: &str) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("`{role}` role is missing required aria props `{props}`."))
@ -50,7 +50,7 @@ static ROLE_TO_REQUIRED_ARIA_PROPS: phf::Map<&'static str, phf::Set<&'static str
impl Rule for RoleHasRequiredAriaProps {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
let Some(role_prop) = has_jsx_prop_lowercase(jsx_el, "role") else {
let Some(role_prop) = has_jsx_prop_ignore_case(jsx_el, "role") else {
return;
};
let JSXAttributeItem::Attribute(attr) = role_prop else {
@ -63,7 +63,7 @@ impl Rule for RoleHasRequiredAriaProps {
for role in roles {
if let Some(props) = ROLE_TO_REQUIRED_ARIA_PROPS.get(role) {
for prop in props {
if has_jsx_prop_lowercase(jsx_el, prop).is_none() {
if has_jsx_prop_ignore_case(jsx_el, prop).is_none() {
ctx.diagnostic(role_has_required_aria_props_diagnostic(
attr.span, role, prop,
));

View file

@ -13,7 +13,7 @@ use crate::{
rule::Rule,
utils::{
get_element_type, get_jsx_attribute_name, get_string_literal_prop_value,
has_jsx_prop_lowercase,
has_jsx_prop_ignore_case,
},
AstNode,
};
@ -63,7 +63,7 @@ impl Rule for RoleSupportsAriaProps {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_el) = node.kind() {
if let Some(el_type) = get_element_type(ctx, jsx_el) {
let role = has_jsx_prop_lowercase(jsx_el, "role");
let role = has_jsx_prop_ignore_case(jsx_el, "role");
let role_value = role.map_or_else(
|| get_implicit_role(jsx_el, el_type.as_str()),
|i| get_string_literal_prop_value(i),
@ -98,7 +98,7 @@ fn get_implicit_role<'a>(
element_type: &str,
) -> Option<&'static str> {
let implicit_role = match element_type {
"a" | "area" | "link" => match has_jsx_prop_lowercase(node, "href") {
"a" | "area" | "link" => match has_jsx_prop_ignore_case(node, "href") {
Some(_) => "link",
None => "",
},
@ -112,11 +112,11 @@ fn get_implicit_role<'a>(
"form" => "form",
"h1" | "h2" | "h3" | "h4" | "h5" | "h6" => "heading",
"hr" => "separator",
"img" => has_jsx_prop_lowercase(node, "alt").map_or("img", |i| {
"img" => has_jsx_prop_ignore_case(node, "alt").map_or("img", |i| {
get_string_literal_prop_value(i)
.map_or("img", |v| if v.is_empty() { "" } else { "img" })
}),
"input" => has_jsx_prop_lowercase(node, "type").map_or("textbox", |input_type| {
"input" => has_jsx_prop_ignore_case(node, "type").map_or("textbox", |input_type| {
match get_string_literal_prop_value(input_type) {
Some("button" | "image" | "reset" | "submit") => "button",
Some("checkbox") => "checkbox",
@ -126,21 +126,18 @@ fn get_implicit_role<'a>(
}
}),
"li" => "listitem",
"menu" => has_jsx_prop_lowercase(node, "type").map_or("", |v| {
"menu" => has_jsx_prop_ignore_case(node, "type").map_or("", |v| {
get_string_literal_prop_value(v)
.map_or("", |v| if v == "toolbar" { "toolbar" } else { "" })
}),
"menuitem" => {
has_jsx_prop_lowercase(node, "type").map_or(
"",
|v| match get_string_literal_prop_value(v) {
Some("checkbox") => "menuitemcheckbox",
Some("command") => "menuitem",
Some("radio") => "menuitemradio",
_ => "",
},
)
}
"menuitem" => has_jsx_prop_ignore_case(node, "type").map_or("", |v| {
match get_string_literal_prop_value(v) {
Some("checkbox") => "menuitemcheckbox",
Some("command") => "menuitem",
Some("radio") => "menuitemradio",
_ => "",
}
}),
"meter" | "progress" => "progressbar",
"nav" => "navigation",
"ol" | "ul" => "list",

View file

@ -7,7 +7,7 @@ use crate::{
context::LintContext,
globals::HTML_TAG,
rule::Rule,
utils::{get_element_type, has_jsx_prop_lowercase},
utils::{get_element_type, has_jsx_prop_ignore_case},
AstNode,
};
@ -49,7 +49,7 @@ impl Rule for Scope {
return;
};
let scope_attribute = match has_jsx_prop_lowercase(jsx_el, "scope") {
let scope_attribute = match has_jsx_prop_ignore_case(jsx_el, "scope") {
Some(v) => match v {
JSXAttributeItem::Attribute(attr) => attr,
JSXAttributeItem::SpreadAttribute(_) => {

View file

@ -6,7 +6,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{has_jsx_prop_lowercase, parse_jsx_value},
utils::{has_jsx_prop_ignore_case, parse_jsx_value},
AstNode,
};
@ -44,7 +44,7 @@ impl Rule for TabindexNoPositive {
let AstKind::JSXOpeningElement(jsx_el) = node.kind() else {
return;
};
if let Some(tab_index_prop) = has_jsx_prop_lowercase(jsx_el, "tabIndex") {
if let Some(tab_index_prop) = has_jsx_prop_ignore_case(jsx_el, "tabIndex") {
check_and_diagnose(tab_index_prop, ctx);
}
}

View file

@ -6,7 +6,7 @@ use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
rule::Rule,
utils::{get_string_literal_prop_value, has_jsx_prop_lowercase},
utils::{get_string_literal_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -55,7 +55,7 @@ impl Rule for GoogleFontDisplay {
return;
}
let Some(href_prop) = has_jsx_prop_lowercase(jsx_opening_element, "href") else {
let Some(href_prop) = has_jsx_prop_ignore_case(jsx_opening_element, "href") else {
return;
};

View file

@ -6,7 +6,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_string_literal_prop_value, has_jsx_prop_lowercase},
utils::{get_string_literal_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -47,7 +47,7 @@ impl Rule for GoogleFontPreconnect {
return;
}
let Some(href_prop) = has_jsx_prop_lowercase(jsx_opening_element, "href") else {
let Some(href_prop) = has_jsx_prop_ignore_case(jsx_opening_element, "href") else {
return;
};
let Some(href_prop_value) = get_string_literal_prop_value(href_prop) else {
@ -55,7 +55,7 @@ impl Rule for GoogleFontPreconnect {
};
let preconnect_missing =
has_jsx_prop_lowercase(jsx_opening_element, "rel").map_or(true, |rel_prop| {
has_jsx_prop_ignore_case(jsx_opening_element, "rel").map_or(true, |rel_prop| {
let rel_prop_value = get_string_literal_prop_value(rel_prop);
rel_prop_value != Some("preconnect")
});

View file

@ -12,7 +12,7 @@ use oxc_span::Span;
use crate::{
context::LintContext,
rule::Rule,
utils::{get_string_literal_prop_value, has_jsx_prop_lowercase},
utils::{get_string_literal_prop_value, has_jsx_prop_ignore_case},
AstNode,
};
@ -58,7 +58,7 @@ impl Rule for NextScriptForGa {
// Check if the Alternative async tag is being used to add GA.
// https://developers.google.com/analytics/devguides/collection/analyticsjs#alternative_async_tag
// https://developers.google.com/analytics/devguides/collection/gtagjs
if let Some(src_prop) = has_jsx_prop_lowercase(jsx_opening_element, "src") {
if let Some(src_prop) = has_jsx_prop_ignore_case(jsx_opening_element, "src") {
if let Some(src_prop_value) = get_string_literal_prop_value(src_prop) {
if SUPPORTED_SRCS.iter().any(|s| src_prop_value.contains(s)) {
ctx.diagnostic(next_script_for_ga_diagnostic(jsx_opening_element_name.span));
@ -92,7 +92,7 @@ fn get_dangerously_set_inner_html_prop_value<'a>(
jsx_opening_element: &'a JSXOpeningElement<'a>,
) -> Option<&'a ObjectProperty<'a>> {
let Some(JSXAttributeItem::Attribute(dangerously_set_inner_html_prop)) =
has_jsx_prop_lowercase(jsx_opening_element, "dangerouslysetinnerhtml")
has_jsx_prop_ignore_case(jsx_opening_element, "dangerouslysetinnerhtml")
else {
return None;
};

View file

@ -12,7 +12,7 @@ use oxc_span::{GetSpan, Span};
use crate::{
context::LintContext,
rule::Rule,
utils::{get_prop_value, has_jsx_prop_lowercase, is_create_element_call},
utils::{get_prop_value, has_jsx_prop_ignore_case, is_create_element_call},
AstNode,
};
@ -77,7 +77,7 @@ impl Rule for ButtonHasType {
return;
}
has_jsx_prop_lowercase(jsx_el, "type").map_or_else(
has_jsx_prop_ignore_case(jsx_el, "type").map_or_else(
|| {
ctx.diagnostic(missing_type_prop(identifier.span));
},

View file

@ -317,7 +317,7 @@ fn check_rel_val(str: &StringLiteral, allow_referrer: bool) -> bool {
false
});
}
splits.any(|str| str.to_lowercase() == "noreferrer")
splits.any(|str| str.eq_ignore_ascii_case("noreferrer"))
}
fn match_rel_expression<'a>(
@ -367,7 +367,7 @@ fn match_target_expression<'a>(expr: &'a Expression<'a>) -> (bool, &'a str, bool
let default = (false, "", false, false);
match expr {
Expression::StringLiteral(str) => {
(str.value.as_str().to_lowercase() == "_blank", "", false, false)
(str.value.eq_ignore_ascii_case("_blank"), "", false, false)
}
Expression::ConditionalExpression(expr) => {
let consequent = match_target_expression(&expr.consequent);
@ -390,7 +390,7 @@ fn check_target<'a>(attribute_value: &'a JSXAttributeValue<'a>) -> (bool, &'a st
let default = (false, "", false, false);
match attribute_value {
JSXAttributeValue::StringLiteral(str) => {
(str.value.as_str().to_lowercase() == "_blank", "", false, false)
(str.value.eq_ignore_ascii_case("_blank"), "", false, false)
}
JSXAttributeValue::ExpressionContainer(expr) => {
if let Some(expr) = expr.expression.as_expression() {

View file

@ -436,7 +436,7 @@ fn is_valid_data_attr(name: &str) -> bool {
fn normalize_attribute_case(name: &str) -> &str {
DOM_PROPERTIES_IGNORE_CASE
.iter()
.find(|camel_name| camel_name.to_lowercase() == name.to_lowercase())
.find(|camel_name| camel_name.eq_ignore_ascii_case(name))
.unwrap_or(&name)
}
fn has_uppercase(name: &str) -> bool {

View file

@ -103,7 +103,7 @@ fn is_jsx_meta_elem_with_charset_attr(id: AstNodeId, ctx: &LintContext) -> bool
let JSXAttributeName::Identifier(ident) = &jsx_attr.name else {
return false;
};
if ident.name.to_lowercase() != "charset" {
if !ident.name.eq_ignore_ascii_case("charset") {
return false;
}
@ -116,7 +116,7 @@ fn is_jsx_meta_elem_with_charset_attr(id: AstNodeId, ctx: &LintContext) -> bool
return false;
};
if name.name.to_lowercase() != "meta" {
if !name.name.eq_ignore_ascii_case("meta") {
return false;
}

View file

@ -37,7 +37,7 @@ pub fn has_jsx_prop<'a, 'b>(
})
}
pub fn has_jsx_prop_lowercase<'a, 'b>(
pub fn has_jsx_prop_ignore_case<'a, 'b>(
node: &'b JSXOpeningElement<'a>,
target_prop: &'b str,
) -> Option<&'b JSXAttributeItem<'a>> {
@ -48,7 +48,7 @@ pub fn has_jsx_prop_lowercase<'a, 'b>(
return false;
};
name.name.as_str().to_lowercase() == target_prop.to_lowercase()
name.name.as_str().eq_ignore_ascii_case(target_prop)
}
})
}
@ -84,7 +84,7 @@ pub fn get_string_literal_prop_value<'a>(item: &'a JSXAttributeItem<'_>) -> Opti
pub fn is_hidden_from_screen_reader(ctx: &LintContext, node: &JSXOpeningElement) -> bool {
if let Some(name) = get_element_type(ctx, node) {
if name.as_str().to_uppercase() == "INPUT" {
if let Some(item) = has_jsx_prop_lowercase(node, "type") {
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.to_uppercase() == "HIDDEN") {
@ -94,7 +94,7 @@ pub fn is_hidden_from_screen_reader(ctx: &LintContext, node: &JSXOpeningElement)
}
}
has_jsx_prop_lowercase(node, "aria-hidden").map_or(false, |v| match get_prop_value(v) {
has_jsx_prop_ignore_case(node, "aria-hidden").map_or(false, |v| match get_prop_value(v) {
None => true,
Some(JSXAttributeValue::StringLiteral(s)) if s.value == "true" => true,
Some(JSXAttributeValue::ExpressionContainer(container)) => {
@ -118,8 +118,8 @@ pub fn object_has_accessible_child(ctx: &LintContext, node: &JSXElement<'_>) ->
&& !container.expression.is_undefined()
}
_ => false,
}) || has_jsx_prop_lowercase(&node.opening_element, "dangerouslySetInnerHTML").is_some()
|| has_jsx_prop_lowercase(&node.opening_element, "children").is_some()
}) || has_jsx_prop_ignore_case(&node.opening_element, "dangerouslySetInnerHTML").is_some()
|| has_jsx_prop_ignore_case(&node.opening_element, "children").is_some()
}
pub fn is_presentation_role(jsx_opening_el: &JSXOpeningElement) -> bool {
@ -247,7 +247,7 @@ pub fn get_element_type(context: &LintContext, element: &JSXOpeningElement) -> O
.polymorphic_prop_name
.as_ref()
.and_then(|polymorphic_prop_name_value| {
has_jsx_prop_lowercase(element, polymorphic_prop_name_value)
has_jsx_prop_ignore_case(element, polymorphic_prop_name_value)
})
.and_then(get_prop_value)
.and_then(|prop_value| match prop_value {