diff --git a/crates/oxc_linter/src/context/mod.rs b/crates/oxc_linter/src/context/mod.rs index 588f7381e0189..548182b175403 100644 --- a/crates/oxc_linter/src/context/mod.rs +++ b/crates/oxc_linter/src/context/mod.rs @@ -485,23 +485,21 @@ impl<'a> LintContext<'a> { /// ``` #[inline] fn plugin_name_to_prefix(plugin_name: &'static str) -> &'static str { - PLUGIN_PREFIXES.get(plugin_name).copied().unwrap_or(plugin_name) + match plugin_name { + "import" => "eslint-plugin-import", + "jest" => "eslint-plugin-jest", + "jsdoc" => "eslint-plugin-jsdoc", + "jsx_a11y" => "eslint-plugin-jsx-a11y", + "nextjs" => "eslint-plugin-next", + "promise" => "eslint-plugin-promise", + "react_perf" => "eslint-plugin-react-perf", + "react" => "eslint-plugin-react", + "typescript" => "typescript-eslint", + "unicorn" => "eslint-plugin-unicorn", + "vitest" => "eslint-plugin-vitest", + "node" => "eslint-plugin-node", + "vue" => "eslint-plugin-vue", + "regexp" => "eslint-plugin-regexp", + _ => plugin_name, + } } - -/// Map of plugin names to their prefixed versions. -const PLUGIN_PREFIXES: phf::Map<&'static str, &'static str> = phf::phf_map! { - "import" => "eslint-plugin-import", - "jest" => "eslint-plugin-jest", - "jsdoc" => "eslint-plugin-jsdoc", - "jsx_a11y" => "eslint-plugin-jsx-a11y", - "nextjs" => "eslint-plugin-next", - "promise" => "eslint-plugin-promise", - "react_perf" => "eslint-plugin-react-perf", - "react" => "eslint-plugin-react", - "typescript" => "typescript-eslint", - "unicorn" => "eslint-plugin-unicorn", - "vitest" => "eslint-plugin-vitest", - "node" => "eslint-plugin-node", - "vue" => "eslint-plugin-vue", - "regexp" => "eslint-plugin-regexp", -}; diff --git a/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs b/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs index 671e9720cd30f..fefeb441125ec 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs @@ -8,7 +8,6 @@ use oxc_ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use phf::{Map, phf_map, phf_ordered_set}; use crate::{AstNode, ast_util::get_symbol_id_of_variable, context::LintContext, rule::Rule}; @@ -20,11 +19,14 @@ fn prefer_numeric_literals_diagnostic(span: Span, prefix_name: &str) -> OxcDiagn #[derive(Debug, Default, Clone)] pub struct PreferNumericLiterals; -const RADIX_MAP: Map<&'static str, phf::OrderedSet<&'static str>> = phf_map! { - "2" => phf_ordered_set!{"binary", "0b"}, - "8" => phf_ordered_set!{"octal", "0o"}, - "16" => phf_ordered_set!{"hexadecimal", "0x"}, -}; +fn radix_map(base: &str) -> Option<(&'static str, &'static str)> { + match base { + "2" => Some(("binary", "0b")), + "8" => Some(("octal", "0o")), + "16" => Some(("hexadecimal", "0x")), + _ => None, + } +} declare_oxc_lint!( /// ### What it does @@ -133,10 +135,7 @@ fn check_arguments<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) { }; let raw = numeric_lit.raw.as_ref().unwrap().as_str(); - if let Some(name_prefix_set) = RADIX_MAP.get(raw) { - let name = name_prefix_set.index(0).unwrap(); - let prefix = name_prefix_set.index(1).unwrap(); - + if let Some((name, prefix)) = radix_map(raw) { match is_fixable(call_expr, raw) { Ok(argument) => { ctx.diagnostic_with_fix( diff --git a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs index 9261397ae8d1a..ed8afb6abd8fc 100644 --- a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs +++ b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs @@ -4,7 +4,6 @@ use oxc_ast::ast::Expression; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; -use phf::{Map, phf_map}; use crate::{context::LintContext, rule::Rule}; @@ -87,14 +86,16 @@ declare_oxc_lint!( fix ); -const DEPRECATED_FUNCTIONS_MAP: Map<&'static str, (usize, &'static str)> = phf_map! { - "jest.resetModuleRegistry" => (15, "jest.resetModules"), - "jest.addMatchers" => (17, "expect.extend"), - "require.requireMock" => (21, "jest.requireMock"), - "require.requireActual" => (21, "jest.requireMock"), - "jest.runTimersToTime" => (22, "jest.advanceTimersByTime"), - "jest.genMockFromModule" => (26, "jest.createMockFromModule"), -}; +fn deprecated_functions_map(deprecated_fn: &str) -> Option<(usize, &'static str)> { + match deprecated_fn { + "jest.resetModuleRegistry" => Some((15, "jest.resetModules")), + "jest.addMatchers" => Some((17, "expect.extend")), + "require.requireMock" | "require.requireActual" => Some((21, "jest.requireMock")), + "jest.runTimersToTime" => Some((22, "jest.advanceTimersByTime")), + "jest.genMockFromModule" => Some((26, "jest.createMockFromModule")), + _ => None, + } +} impl Rule for NoDeprecatedFunctions { fn from_configuration(value: serde_json::Value) -> Self { @@ -133,12 +134,12 @@ impl Rule for NoDeprecatedFunctions { // Todo: read from configuration let jest_version_num: usize = self.jest.version.parse().unwrap_or(29); - if let Some((base_version, replacement)) = DEPRECATED_FUNCTIONS_MAP.get(&node_name) - && jest_version_num >= *base_version + if let Some((base_version, replacement)) = deprecated_functions_map(&node_name) + && jest_version_num >= base_version { ctx.diagnostic_with_fix( deprecated_function(&node_name, replacement, mem_expr.span()), - |fixer| fixer.replace(mem_expr.span(), *replacement), + |fixer| fixer.replace(mem_expr.span(), replacement), ); } } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs index ae3affd3b9c34..bedae416bd851 100644 --- a/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs +++ b/crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs @@ -60,7 +60,7 @@ impl Rule for AriaProps { let name = get_jsx_attribute_name(&attr.name); let name = name.cow_to_ascii_lowercase(); if name.starts_with("aria-") && !is_valid_aria_property(&name) { - let suggestion = COMMON_TYPOS.get(&name).copied(); + let suggestion = get_common_aria_prop_typo(&name); let diagnostic = aria_props_diagnostic(attr.span, &name, suggestion); if let Some(suggestion) = suggestion { @@ -75,13 +75,16 @@ impl Rule for AriaProps { } } -const COMMON_TYPOS: phf::Map<&'static str, &'static str> = phf::phf_map! { - "aria-labeledby" => "aria-labelledby", - "aria-role" => "role", - "aria-sorted" => "aria-sort", - "aria-lable" => "aria-label", - "aria-value" => "aria-valuenow", -}; +fn get_common_aria_prop_typo(prop_name: &str) -> Option<&'static str> { + match prop_name { + "aria-labeledby" => Some("aria-labelledby"), + "aria-role" => Some("role"), + "aria-sorted" => Some("aria-sort"), + "aria-lable" => Some("aria-label"), + "aria-value" => Some("aria-valuenow"), + _ => None, + } +} #[test] fn test() { 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 dbcd5a126325f..55f7a63f4fdd5 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 @@ -5,7 +5,6 @@ use oxc_ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use phf::phf_map; use crate::{ AstNode, @@ -52,11 +51,14 @@ declare_oxc_lint!( fix ); -static DEFAULT_ROLE_EXCEPTIONS: phf::Map<&'static str, &'static str> = phf_map! { - "nav" => "navigation", - "button" => "button", - "body" => "document", -}; +fn get_default_role_exception(tag: &str) -> Option<&'static str> { + match tag { + "nav" => Some("navigation"), + "button" => Some("button"), + "body" => Some("document"), + _ => None, + } +} impl Rule for NoRedundantRoles { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { @@ -71,7 +73,7 @@ impl Rule for NoRedundantRoles { { let roles = role_values.value.split_whitespace().collect::>(); for role in &roles { - let exceptions = DEFAULT_ROLE_EXCEPTIONS.get(&component); + let exceptions = get_default_role_exception(&component); if exceptions.is_some_and(|set| set.contains(role)) { ctx.diagnostic_with_fix( no_redundant_roles_diagnostic(attr.span, &component, role), 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 9231120443012..ab6eb56e57be0 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 @@ -1,5 +1,3 @@ -use phf::phf_map; - use oxc_ast::{ AstKind, ast::{JSXAttributeItem, JSXAttributeValue}, @@ -50,52 +48,44 @@ declare_oxc_lint!( ); impl PreferTagOverRole { - fn check_roles<'a>( - role_prop: &JSXAttributeItem<'a>, - role_to_tag: &phf::Map<&str, &str>, - jsx_name: &str, - ctx: &LintContext<'a>, - ) { + fn check_roles<'a>(role_prop: &JSXAttributeItem<'a>, jsx_name: &str, ctx: &LintContext<'a>) { if let JSXAttributeItem::Attribute(attr) = role_prop && let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value { let roles = role_values.value.split_whitespace(); for role in roles { - Self::check_role(role, role_to_tag, jsx_name, attr.span, ctx); + Self::check_role(role, jsx_name, attr.span, ctx); } } } - fn check_role( - role: &str, - role_to_tag: &phf::Map<&str, &str>, - jsx_name: &str, - span: Span, - ctx: &LintContext, - ) { - if let Some(tag) = role_to_tag.get(role) - && jsx_name != *tag + fn check_role(role: &str, jsx_name: &str, span: Span, ctx: &LintContext) { + if let Some(tag) = get_tags_from_role(role) + && jsx_name != tag { ctx.diagnostic(prefer_tag_over_role_diagnostic(span, tag, role)); } } } -const ROLE_TO_TAG_MAP: phf::Map<&'static str, &'static str> = phf_map! { - "checkbox" => "input", - "button" => "button", - "heading" => "h1,h2,h3,h4,h5,h6", - "link" => "a,area", - "rowgroup" => "tbody,tfoot,thead", - "banner" => "header", -}; +fn get_tags_from_role(role: &str) -> Option<&'static str> { + match role { + "checkbox" => Some("input"), + "button" => Some("button"), + "heading" => Some("h1,h2,h3,h4,h5,h6"), + "link" => Some("a,area"), + "rowgroup" => Some("tbody,tfoot,thead"), + "banner" => Some("header"), + _ => None, + } +} impl Rule for PreferTagOverRole { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { if let AstKind::JSXOpeningElement(jsx_el) = node.kind() { 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); + Self::check_roles(role_prop, &name, ctx); } } } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs b/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs index 02446df10d833..5e238ab64f06e 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs @@ -5,7 +5,6 @@ use oxc_ast::{ use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use phf::phf_map; use crate::{AstNode, ast_util::is_method_call, context::LintContext, rule::Rule}; @@ -21,17 +20,23 @@ fn prefer_modern_dom_apis_diagnostic( #[derive(Debug, Default, Clone)] pub struct PreferModernDomApis; -const DISALLOWED_METHODS: phf::Map<&'static str, &'static str> = phf_map!( - "replaceChild" => "replaceWith", - "insertBefore" => "before", -); +fn get_replacement_for_disallowed_method(method: &str) -> Option<&'static str> { + match method { + "replaceChild" => Some("replaceWith"), + "insertBefore" => Some("before"), + _ => None, + } +} -const POSITION_REPLACERS: phf::Map<&'static str, &'static str> = phf_map!( - "beforebegin" => "before", - "afterbegin" => "prepend", - "beforeend" => "append", - "afterend" => "after", -); +fn get_replacement_for_position(position: &str) -> Option<&'static str> { + match position { + "beforebegin" => Some("before"), + "afterbegin" => Some("prepend"), + "beforeend" => Some("append"), + "afterend" => Some("after"), + _ => None, + } +} declare_oxc_lint!( /// ### What it does @@ -89,7 +94,7 @@ impl Rule for PreferModernDomApis { .all(|argument| matches!(argument.as_expression(), Some(expr) if !expr.is_undefined())) && matches!(member_expr.object, Expression::Identifier(_)) && !call_expr.optional - && let Some(preferred_method) = DISALLOWED_METHODS.get(method) + && let Some(preferred_method) = get_replacement_for_disallowed_method(method) { ctx.diagnostic(prefer_modern_dom_apis_diagnostic( preferred_method, @@ -107,16 +112,13 @@ impl Rule for PreferModernDomApis { Some(2), Some(2), ) && let Argument::StringLiteral(lit) = &call_expr.arguments[0] + && let Some(replacer) = get_replacement_for_position(lit.value.as_str()) { - for (position, replacer) in &POSITION_REPLACERS { - if lit.value == position { - ctx.diagnostic(prefer_modern_dom_apis_diagnostic( - replacer, - method, - member_expr.property.span, - )); - } - } + ctx.diagnostic(prefer_modern_dom_apis_diagnostic( + replacer, + method, + member_expr.property.span, + )); } } } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs b/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs index a6aaac52819ad..d7885fc922325 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_query_selector.rs @@ -2,7 +2,6 @@ use oxc_ast::{AstKind, ast::Expression}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; -use phf::phf_map; use crate::{AstNode, context::LintContext, rule::Rule, utils::is_node_value_not_dom_node}; @@ -19,11 +18,13 @@ fn prefer_query_selector_diagnostic( #[derive(Debug, Default, Clone)] pub struct PreferQuerySelector; -const DISALLOWED_IDENTIFIER_NAMES: phf::Map<&'static str, &'static str> = phf_map!( - "getElementById" => "querySelector", - "getElementsByClassName" => "querySelectorAll", - "getElementsByTagName" => "querySelectorAll" -); +fn get_preferred_identifier_name(ident_name: &str) -> Option<&'static str> { + match ident_name { + "getElementById" => Some("querySelector"), + "getElementsByClassName" | "getElementsByTagName" => Some("querySelectorAll"), + _ => None, + } +} declare_oxc_lint!( /// ### What it does @@ -89,20 +90,13 @@ impl Rule for PreferQuerySelector { return; }; - for (cur_property_name, preferred_selector) in &DISALLOWED_IDENTIFIER_NAMES { - if cur_property_name != &property_name { - continue; - } - - let diagnostic = prefer_query_selector_diagnostic( - preferred_selector, - cur_property_name, - property_span, - ); + if let Some(preferred_selector) = get_preferred_identifier_name(property_name) { + let diagnostic = + prefer_query_selector_diagnostic(preferred_selector, property_name, property_span); if argument_expr.is_null() { return ctx.diagnostic_with_fix(diagnostic, |fixer| { - fixer.replace(property_span, *preferred_selector) + fixer.replace(property_span, preferred_selector) }); } @@ -121,12 +115,12 @@ impl Rule for PreferQuerySelector { if let Some(literal_value) = literal_value { return ctx.diagnostic_with_fix(diagnostic, |fixer| { if literal_value.is_empty() { - return fixer.replace(property_span, *preferred_selector); + return fixer.replace(property_span, preferred_selector); } let source_text = fixer.source_range(argument_expr.span()); let quotes_symbol = source_text.chars().next().unwrap(); - let argument = match *cur_property_name { + let argument = match property_name { "getElementById" => format!("#{literal_value}"), "getElementsByClassName" => { format!(