-
-
Notifications
You must be signed in to change notification settings - Fork 484
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): eslint-plugin-jsx-a11y no-redundant-roles rule (#1981)
Part of: #1141 Based on: - doc: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-redundant-roles.md - code: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/rules/no-redundant-roles.js - test: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/__tests__/src/rules/no-redundant-roles-test.js
- Loading branch information
Yuto Yoshino
authored
Jan 14, 2024
1 parent
b4d76f0
commit 530d1be
Showing
3 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
crates/oxc_linter/src/rules/jsx_a11y/no_redundant_roles.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use crate::{ | ||
context::LintContext, | ||
rule::Rule, | ||
utils::{get_element_type, has_jsx_prop_lowercase}, | ||
AstNode, | ||
}; | ||
use oxc_ast::{ | ||
ast::{JSXAttributeItem, JSXAttributeValue}, | ||
AstKind, | ||
}; | ||
use oxc_diagnostics::{ | ||
miette::{self, Diagnostic}, | ||
thiserror::{self, Error}, | ||
}; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
use phf::phf_map; | ||
|
||
#[derive(Debug, Error, Diagnostic)] | ||
#[error( | ||
"eslint-plugin-jsx-a11y(no-redundant-roles): The element `{element}` has an implicit role of `{role}`. Defining this explicitly is redundant and should be avoided." | ||
)] | ||
#[diagnostic( | ||
severity(warning), | ||
help("Remove the redundant role `{role}` from the element `{element}`.") | ||
)] | ||
struct NoRedundantRolesDiagnostic { | ||
#[label] | ||
pub span: Span, | ||
pub element: String, | ||
pub role: String, | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct NoRedundantRoles; | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// Enforces that the explicit role property is not the same as implicit/default role property on element. | ||
/// | ||
/// ### Why is this bad? | ||
/// Redundant roles can lead to confusion and verbosity in the codebase. | ||
/// | ||
/// ### Example | ||
/// ```javascript | ||
/// // Bad | ||
/// <nav role="navigation" /> | ||
/// | ||
/// // Good | ||
/// <nav /> | ||
/// ``` | ||
NoRedundantRoles, | ||
correctness | ||
); | ||
|
||
static DEFAULT_ROLE_EXCEPTIONS: phf::Map<&'static str, &'static str> = phf_map! { | ||
"nav" =>"navigation", | ||
"button" => "button", | ||
"body" => "document", | ||
}; | ||
|
||
impl Rule for NoRedundantRoles { | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
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") | ||
{ | ||
if let Some(JSXAttributeValue::StringLiteral(role_values)) = &attr.value { | ||
let roles: Vec<String> = role_values | ||
.value | ||
.split_whitespace() | ||
.map(std::string::ToString::to_string) | ||
.collect(); | ||
for role in &roles { | ||
let exceptions = DEFAULT_ROLE_EXCEPTIONS.get(&component); | ||
if exceptions.map_or(false, |set| set.contains(role)) { | ||
ctx.diagnostic(NoRedundantRolesDiagnostic { | ||
span: attr.span, | ||
element: component.clone(), | ||
role: role.to_string(), | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::rules::NoRedundantRoles; | ||
use crate::tester::Tester; | ||
|
||
fn settings() -> serde_json::Value { | ||
serde_json::json!({ | ||
"jsx-a11y": { | ||
"components": { | ||
"Button": "button", | ||
} | ||
} | ||
}) | ||
} | ||
|
||
let pass = vec![ | ||
("<div />", None, None, None), | ||
("<button role='main' />", None, None, None), | ||
("<MyComponent role='button' />", None, None, None), | ||
("<button role={`${foo}button`} />", None, None, None), | ||
("<Button role={`${foo}button`} />", None, Some(settings()), None), | ||
]; | ||
|
||
let fail = vec![ | ||
("<button role='button' />", None, None, None), | ||
("<body role='document' />", None, None, None), | ||
("<Button role='button' />", None, Some(settings()), None), | ||
]; | ||
|
||
Tester::new(NoRedundantRoles::NAME, pass, fail).test_and_snapshot(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
--- | ||
source: crates/oxc_linter/src/tester.rs | ||
expression: no_redundant_roles | ||
--- | ||
⚠ eslint-plugin-jsx-a11y(no-redundant-roles): The element `button` has an implicit role of `button`. Defining this explicitly is redundant and should be avoided. | ||
╭─[no_redundant_roles.tsx:1:1] | ||
1 │ <button role='button' /> | ||
· ───────────── | ||
╰──── | ||
help: Remove the redundant role `button` from the element `button`. | ||
|
||
⚠ eslint-plugin-jsx-a11y(no-redundant-roles): The element `body` has an implicit role of `document`. Defining this explicitly is redundant and should be avoided. | ||
╭─[no_redundant_roles.tsx:1:1] | ||
1 │ <body role='document' /> | ||
· ─────────────── | ||
╰──── | ||
help: Remove the redundant role `document` from the element `body`. | ||
|
||
⚠ eslint-plugin-jsx-a11y(no-redundant-roles): The element `button` has an implicit role of `button`. Defining this explicitly is redundant and should be avoided. | ||
╭─[no_redundant_roles.tsx:1:1] | ||
1 │ <Button role='button' /> | ||
· ───────────── | ||
╰──── | ||
help: Remove the redundant role `button` from the element `button`. | ||
|
||
|