From d88f4f49832c5fa81226c553d3f8c4bf5b851867 Mon Sep 17 00:00:00 2001 From: msdlisper <1170167213@qq.com> Date: Fri, 8 Dec 2023 16:43:14 +0800 Subject: [PATCH] feat(linter): eslint-plugin-jsx-a11y no-autofocus (#1641) impl linter for #1141 --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/jsx_a11y/no_autofocus.rs | 299 ++++++++++++++++++ .../src/snapshots/no_autofocus.snap | 69 ++++ 3 files changed, 370 insertions(+) create mode 100644 crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs create mode 100644 crates/oxc_linter/src/snapshots/no_autofocus.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 7a42c143f3a1c..5ae022569a24c 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -227,6 +227,7 @@ mod jsx_a11y { pub mod html_has_lang; pub mod iframe_has_title; pub mod img_redundant_alt; + pub mod no_autofocus; pub mod scope; } @@ -435,5 +436,6 @@ oxc_macros::declare_all_lint_rules! { jsx_a11y::iframe_has_title, jsx_a11y::img_redundant_alt, jsx_a11y::scope, + jsx_a11y::no_autofocus, oxc::no_accumulating_spread } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs new file mode 100644 index 0000000000000..fd685932e6802 --- /dev/null +++ b/crates/oxc_linter/src/rules/jsx_a11y/no_autofocus.rs @@ -0,0 +1,299 @@ +use oxc_ast::{ast::JSXElementName, AstKind}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop, AstNode}; + +use phf::phf_set; + +const HTML_TAG: phf::Set<&'static str> = phf_set! { + "a", + "abbr", + "acronym", + "address", + "applet", + "area", + "article", + "aside", + "audio", + "b", + "base", + "basefont", + "bdi", + "bdo", + "bgsound", + "big", + "blink", + "blockquote", + "body", + "br", + "button", + "canvas", + "caption", + "center", + "cite", + "code", + "col", + "colgroup", + "command", + "content", + "data", + "datalist", + "dd", + "del", + "details", + "dfn", + "dialog", + "dir", + "div", + "dl", + "dt", + "element", + "em", + "embed", + "fieldset", + "figcaption", + "figure", + "font", + "footer", + "form", + "frame", + "frameset", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "head", + "header", + "hgroup", + "hr", + "html", + "i", + "iframe", + "image", + "img", + "input", + "ins", + "isindex", + "kbd", + "keygen", + "label", + "legend", + "li", + "link", + "listing", + "main", + "map", + "mark", + "marquee", + "math", + "menu", + "menuitem", + "meta", + "meter", + "multicol", + "nav", + "nextid", + "nobr", + "noembed", + "noframes", + "noscript", + "object", + "ol", + "optgroup", + "option", + "output", + "p", + "param", + "picture", + "plaintext", + "pre", + "progress", + "q", + "rb", + "rbc", + "rp", + "rt", + "rtc", + "ruby", + "s", + "samp", + "script", + "search", + "section", + "select", + "shadow", + "slot", + "small", + "source", + "spacer", + "span", + "strike", + "strong", + "style", + "sub", + "summary", + "sup", + "svg", + "table", + "tbody", + "td", + "template", + "textarea", + "tfoot", + "th", + "thead", + "time", + "title", + "tr", + "track", + "tt", + "u", + "ul", + "var", + "video", + "wbr", + "xmp", +}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-jsx-a11y(no-autofocus): The `autofocus` attribute is found here, which can cause usability issues for sighted and non-sighted users")] +#[diagnostic(severity(warning), help("Remove `autofocus` attribute"))] +struct NoAutofocusDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoAutofocus { + ignore_non_dom: bool, +} + +declare_oxc_lint!( + /// ### What it does + /// Enforce that autoFocus prop is not used on elements. Autofocusing elements can cause usability issues for sighted and non-sighted users, alike. + /// + /// ### Rule Option + /// This rule takes one optional object argument of type object: + /// + /// ``` + /// { + /// "rules": { + /// "jsx-a11y/no-autofocus": [ 2, { + /// "ignoreNonDOM": true + /// }], + /// } + /// } + /// ``` + /// + /// For the `ignoreNonDOM` option, this determines if developer created components are checked. + /// + /// ### Example + /// // good + /// + /// ```javascript + ///
+ /// ``` + /// + /// // bad + /// + /// ``` + /// + /// + /// + /// + /// ``` + /// + NoAutofocus, + correctness +); + +impl NoAutofocus { + pub fn set_option(&mut self, value: bool) { + self.ignore_non_dom = value; + } +} + +impl Rule for NoAutofocus { + fn from_configuration(value: serde_json::Value) -> Self { + let mut no_focus = Self::default(); + + let _ = value.as_array().unwrap().iter().find(|v| { + if let serde_json::Value::Object(obj) = v { + let config = obj.get("ignoreNonDOM").unwrap(); + if let serde_json::Value::Bool(val) = config { + no_focus.set_option(*val); + } + return true; + } + false + }); + + no_focus + } + + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_el) = node.kind() { + if let Option::Some(autofocus) = has_jsx_prop(&jsx_el.opening_element, "autoFocus") { + if self.ignore_non_dom { + let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else { + return; + }; + let name = ident.name.as_str(); + if HTML_TAG.contains(name) { + if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { + ctx.diagnostic(NoAutofocusDiagnostic(attr.span)); + } + } + return; + } + + if let oxc_ast::ast::JSXAttributeItem::Attribute(attr) = autofocus { + ctx.diagnostic(NoAutofocusDiagnostic(attr.span)); + } + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + fn array() -> serde_json::Value { + serde_json::json!([2,{ + "ignoreNonDOM": true + }]) + } + + let pass = vec![ + (";", None), + (";", None), + (";", None), + ("