Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 17 additions & 19 deletions crates/oxc_linter/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
};
19 changes: 9 additions & 10 deletions crates/oxc_linter/src/rules/eslint/prefer_numeric_literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down
25 changes: 13 additions & 12 deletions crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
);
}
}
Expand Down
19 changes: 11 additions & 8 deletions crates/oxc_linter/src/rules/jsx_a11y/aria_props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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() {
Expand Down
16 changes: 9 additions & 7 deletions crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>) {
Expand All @@ -71,7 +73,7 @@ impl Rule for NoRedundantRoles {
{
let roles = role_values.value.split_whitespace().collect::<Vec<_>>();
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),
Expand Down
44 changes: 17 additions & 27 deletions crates/oxc_linter/src/rules/jsx_a11y/prefer_tag_over_role.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use phf::phf_map;

use oxc_ast::{
AstKind,
ast::{JSXAttributeItem, JSXAttributeValue},
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
44 changes: 23 additions & 21 deletions crates/oxc_linter/src/rules/unicorn/prefer_modern_dom_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
));
}
}
}
Expand Down
Loading
Loading