feat(rome_js_analyzer): rule `noAutofocus`
MODIFY
crates/rome_diagnostics_categories/src/categories.rs
crates/rome_diagnostics_categories/src/categories.rs
@@ -52,6 +52,7 @@ define_dategories! {
"lint/nursery/useFragmentSyntax": "https://rome.tools/docs/lint/rules/useFragmentSyntax",
"lint/nursery/noArrayIndexKey": "https://rome.tools/docs/lint/rules/noArrayIndexKey",
"lint/nursery/noDangerouslySetInnerHtmlWithChildren": "https://rome.tools/docs/lint/rules/noDangerouslySetInnerHtmlWithChildren",
+ "lint/nursery/noAutofocus": "https://rome.tools/docs/lint/rules/noAutofocus",
"lint/style/noNegationElse": "https://rome.tools/docs/lint/rules/noNegationElse",
"lint/style/noShoutyConstants": "https://rome.tools/docs/lint/rules/noShoutyConstants",
"lint/style/useSelfClosingElements": "https://rome.tools/docs/lint/rules/useSelfClosingElements",

MODIFY
crates/rome_js_analyze/src/analyzers/nursery.rs
crates/rome_js_analyze/src/analyzers/nursery.rs
//! Generated file, do not edit by hand, see `xtask/codegen`
use rome_analyze::declare_group;
+mod no_auto_focus;
mod no_new_symbol;
mod no_unreachable;
mod use_optional_chain;
-declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_new_symbol :: NoNewSymbol , self :: no_unreachable :: NoUnreachable , self :: use_optional_chain :: UseOptionalChain ,] } }
+declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_auto_focus :: NoAutoFocus , self :: no_new_symbol :: NoNewSymbol , self :: no_unreachable :: NoUnreachable , self :: use_optional_chain :: UseOptionalChain ,] } }

ADD
crates/rome_js_analyze/src/analyzers/nursery/no_auto_focus.rs
+use crate::JsRuleAction;
+use rome_analyze::{context::RuleContext, declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic};
+use rome_console::markup;
+use rome_diagnostics::Applicability;
+use rome_js_syntax::{JsxAttribute, JsxOpeningElement, JsxSelfClosingElement};
+use rome_rowan::{declare_node_union, AstNode, BatchMutationExt};
+
+declare_rule! {
+ /// Avoid the `autoFocus` attribute
+ ///
+ /// ## Examples
+ ///
+ /// ### Invalid
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <input autoFocus />
+ /// ```
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <input autoFocus="true" />
+ /// ```
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <input autoFocus={"false"} />
+ /// ```
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <input autoFocus={undefined} />
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```jsx
+ /// <input />
+ ///```
+ ///
+ /// ```jsx
+ /// <div />
+ ///```
+ ///
+ /// ```jsx
+ /// <button />
+ ///```
+ ///
+ /// ```jsx
+ /// // `autoFocus` prop in user created component is valid
+ /// <MyComponent autoFocus={true} />
+ ///```
+ pub(crate) NoAutoFocus {
+ version: "10.0.0",
+ name: "noAutofocus",
+ recommended: false,
+ }
+}
+
+declare_node_union! {
+ pub(crate) JsxAnyElement = JsxOpeningElement | JsxSelfClosingElement
+}
+
+impl Rule for NoAutoFocus {
+ type Query = Ast<JsxAnyElement>;
+ type State = JsxAttribute;
+ type Signals = Option<Self::State>;
+
+ fn run(ctx: &RuleContext<Self>) -> Self::Signals {
+ let node = ctx.query();
+ match node {
+ JsxAnyElement::JsxOpeningElement(element) => {
+ element.name().ok()?.as_jsx_name()?;
+ element.find_attribute_by_name("autoFocus").ok()?
+ }
+ JsxAnyElement::JsxSelfClosingElement(element) => {
+ element.name().ok()?.as_jsx_name()?;
+ element.find_attribute_by_name("autoFocus").ok()?
+ }
+ }
+ }
+
+ fn diagnostic(_ctx: &RuleContext<Self>, attr: &Self::State) -> Option<RuleDiagnostic> {
+ Some(RuleDiagnostic::new(
+ rule_category!(),
+ attr.syntax().text_trimmed_range(),
+ markup! {
+ "Avoid the "<Emphasis>"autoFocus"</Emphasis>" attribute."
+ },
+ ))
+ }
+
+ fn action(ctx: &RuleContext<Self>, attr: &Self::State) -> Option<JsRuleAction> {
+ let mut mutation = ctx.root().begin();
+
+ mutation.remove_node(attr.clone());
+
+ Some(JsRuleAction {
+ category: ActionCategory::QuickFix,
+ applicability: Applicability::MaybeIncorrect,
+ message: markup! { "Remove the "<Emphasis>"autoFocus"</Emphasis>" attribute." }
+ .to_owned(),
+ mutation,
+ })
+ }
+}

ADD
crates/rome_js_analyze/tests/specs/nursery/noAutofocus/noAutoFocusInvalid.jsx
+<>
+ <button autoFocus />
+ <input autoFocus />
+ <input autoFocus="true" />
+ <input autoFocus={undefined} />
+ <input autoFocus={"false"} />
+</>
\ No newline at end of file

ADD
crates/rome_js_analyze/tests/specs/nursery/noAutofocus/noAutoFocusValid.jsx
+<>
+ <div />
+ <button />
+ <input />
+ <MyComponent autoFocus={true} />
+</>
\ No newline at end of file

ADD
crates/rome_js_analyze/tests/specs/nursery/noAutofocus/noAutoFocusValid.jsx.snap
+---
+source: crates/rome_js_analyze/tests/spec_tests.rs
+expression: noAutoFocusValid.jsx
+---
+# Input
+```js
+<>
+ <div />
+ <button />
+ <input />
+ <MyComponent autoFocus={true} />
+</>
+```
+
+

MODIFY
crates/rome_service/src/configuration/linter/rules.rs
crates/rome_service/src/configuration/linter/rules.rs
@@ -387,6 +387,7 @@ pub struct Nursery {
#[doc = r" A list of rules that belong to this group"]
struct NurserySchema {
no_array_index_key: Option<RuleConfiguration>,
+ no_autofocus: Option<RuleConfiguration>,
no_children_prop: Option<RuleConfiguration>,
no_dangerously_set_inner_html: Option<RuleConfiguration>,
no_dangerously_set_inner_html_with_children: Option<RuleConfiguration>,
@@ -404,8 +405,9 @@ struct NurserySchema {
}
impl Nursery {
const CATEGORY_NAME: &'static str = "nursery";
- pub(crate) const CATEGORY_RULES: [&'static str; 15] = [
+ pub(crate) const CATEGORY_RULES: [&'static str; 16] = [
"noArrayIndexKey",
+ "noAutofocus",
"noChildrenProp",
"noDangerouslySetInnerHtml",
"noDangerouslySetInnerHtmlWithChildren",

MODIFY
editors/vscode/configuration_schema.json
editors/vscode/configuration_schema.json
}
]
},
+ "noAutofocus": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/RuleConfiguration"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"noChildrenProp": {
"anyOf": [
{

MODIFY
npm/backend-jsonrpc/src/workspace.ts
npm/backend-jsonrpc/src/workspace.ts
@@ -156,6 +156,7 @@ export interface Correctness {
*/
export interface Nursery {
noArrayIndexKey?: RuleConfiguration;
+ noAutofocus?: RuleConfiguration;
noChildrenProp?: RuleConfiguration;
noDangerouslySetInnerHtml?: RuleConfiguration;
noDangerouslySetInnerHtmlWithChildren?: RuleConfiguration;
@@ -306,6 +307,7 @@ export type Category =
| "lint/nursery/useFragmentSyntax"
| "lint/nursery/noArrayIndexKey"
| "lint/nursery/noDangerouslySetInnerHtmlWithChildren"
+ | "lint/nursery/noAutofocus"
| "lint/style/noNegationElse"
| "lint/style/noShoutyConstants"
| "lint/style/useSelfClosingElements"

MODIFY
rome.json
rome.json
"noUselessFragments": "error",
"noNewSymbol": "error",
"noVoidElementsWithChildren": "error",
- "noDangerouslySetInnerHtmlWithChildren": "error"
+ "noDangerouslySetInnerHtmlWithChildren": "error",
+ "noAutofocus": "error"
}
}
}

MODIFY
website/src/docs/lint/rules/index.md
website/src/docs/lint/rules/index.md
@@ -260,6 +260,13 @@ to promote these rules into a more appropriate category.
Discourage the usage of Array index in keys.
</div>
<div class="rule">
+<h3 data-toc-exclude id="noAutofocus">
+ <a href="/docs/lint/rules/noAutofocus">noAutofocus (since v10.0.0)</a>
+ <a class="header-anchor" href="#noAutofocus"></a>
+</h3>
+Avoid the <code>autoFocus</code> attribute
+</div>
+<div class="rule">
<h3 data-toc-exclude id="noChildrenProp">
<a href="/docs/lint/rules/noChildrenProp">noChildrenProp (since v0.10.0)</a>
<a class="header-anchor" href="#noChildrenProp"></a>

feat(rome_js_analyzer): rule `noAutofocus` (#3333)