feat(rome_js_analyze): add noHeaderScope rule
Closes
MODIFY
crates/rome_diagnostics_categories/src/categories.rs
crates/rome_diagnostics_categories/src/categories.rs
@@ -81,6 +81,7 @@ define_dategories! {
"lint/nursery/noDupeKeys":"https://docs.rome.tools/lint/rules/noDupeKeys",
"lint/nursery/noEmptyInterface": "https://docs.rome.tools/lint/rules/noEmptyInterface",
"lint/nursery/noExplicitAny": "https://docs.rome.tools/lint/rules/noExplicitAny",
+ "lint/nursery/noHeaderScope": "https://docs.rome.tools/lint/rules/noHeaderScope",
"lint/nursery/noInvalidConstructorSuper": "https://docs.rome.tools/lint/rules/noInvalidConstructorSuper",
"lint/nursery/noPrecisionLoss": "https://docs.rome.tools/lint/rules/noPrecisionLoss",
"lint/nursery/noUnsafeFinally": "https://docs.rome.tools/lint/rules/noUnsafeFinally",

MODIFY
crates/rome_js_analyze/src/analyzers/nursery.rs
crates/rome_js_analyze/src/analyzers/nursery.rs
@@ -6,10 +6,11 @@ mod no_conditional_assignment;
mod no_dupe_keys;
mod no_empty_interface;
mod no_explicit_any;
+mod no_header_scope;
mod no_invalid_constructor_super;
mod no_precision_loss;
mod no_unsafe_finally;
mod use_flat_map;
mod use_numeric_literals;
mod use_valid_for_direction;
-declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_banned_types :: NoBannedTypes , self :: no_conditional_assignment :: NoConditionalAssignment , self :: no_dupe_keys :: NoDupeKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_explicit_any :: NoExplicitAny , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_precision_loss :: NoPrecisionLoss , self :: no_unsafe_finally :: NoUnsafeFinally , self :: use_flat_map :: UseFlatMap , self :: use_numeric_literals :: UseNumericLiterals , self :: use_valid_for_direction :: UseValidForDirection ,] } }
+declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_banned_types :: NoBannedTypes , self :: no_conditional_assignment :: NoConditionalAssignment , self :: no_dupe_keys :: NoDupeKeys , self :: no_empty_interface :: NoEmptyInterface , self :: no_explicit_any :: NoExplicitAny , self :: no_header_scope :: NoHeaderScope , self :: no_invalid_constructor_super :: NoInvalidConstructorSuper , self :: no_precision_loss :: NoPrecisionLoss , self :: no_unsafe_finally :: NoUnsafeFinally , self :: use_flat_map :: UseFlatMap , self :: use_numeric_literals :: UseNumericLiterals , self :: use_valid_for_direction :: UseValidForDirection ,] } }

ADD
crates/rome_js_analyze/src/analyzers/nursery/no_header_scope.rs
+use rome_analyze::context::RuleContext;
+use rome_analyze::{declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic};
+use rome_console::markup;
+use rome_diagnostics::Applicability;
+use rome_js_syntax::{
+ JsxAnyElementName, JsxAttribute, JsxAttributeList, JsxOpeningElement, JsxSelfClosingElement,
+};
+use rome_rowan::{declare_node_union, AstNode, BatchMutationExt};
+
+use crate::JsRuleAction;
+
+declare_rule! {
+ /// Check that the scope attribute is only used on `th` elements.
+ ///
+ /// ESLint Equivalent: [scope](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/master/docs/rules/scope.md)
+ ///
+ /// ## Examples
+ ///
+ /// ### Invalid
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <div scope={scope} />
+ /// ```
+ ///
+ /// ```jsx,expect_diagnostic
+ /// <div scope="col" />
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```jsx
+ /// <th scope={scope}></th>
+ /// ```
+ ///
+ /// ```jsx
+ /// <th scope="col"></th>
+ /// ```
+ pub(crate) NoHeaderScope {
+ version: "11.0.0",
+ name: "noHeaderScope",
+ recommended: false,
+ }
+}
+
+declare_node_union! {
+ pub(crate) JsxAnyElement = JsxOpeningElement | JsxSelfClosingElement
+}
+
+impl JsxAnyElement {
+ fn name(&self) -> Option<JsxAnyElementName> {
+ match self {
+ JsxAnyElement::JsxOpeningElement(element) => element.name().ok(),
+ JsxAnyElement::JsxSelfClosingElement(element) => element.name().ok(),
+ }
+ }
+}
+
+impl Rule for NoHeaderScope {
+ type Query = Ast<JsxAttribute>;
+ type State = ();
+ type Signals = Option<Self::State>;
+ type Options = ();
+
+ fn run(ctx: &RuleContext<Self>) -> Self::Signals {
+ let attr = ctx.query();
+
+ if attr.name().ok()?.syntax().text_trimmed() != "scope" {
+ return None;
+ }
+
+ let jsx_element = attr
+ .parent::<JsxAttributeList>()?
+ .parent::<JsxAnyElement>()?;
+
+ if jsx_element.name()?.as_jsx_name()?.syntax().text_trimmed() != "th" {
+ return Some(());
+ }
+
+ None
+ }
+
+ fn diagnostic(ctx: &RuleContext<Self>, _: &Self::State) -> Option<RuleDiagnostic> {
+ let diagnostic = RuleDiagnostic::new(
+ rule_category!(),
+ ctx.query().range(),
+ markup! {"Avoid using the "<Emphasis>"scope"</Emphasis>" attribute on elements other than "<Emphasis>"th"</Emphasis>" elements."}
+ .to_owned(),
+ );
+
+ Some(diagnostic)
+ }
+
+ fn action(ctx: &RuleContext<Self>, _: &Self::State) -> Option<JsRuleAction> {
+ let mut mutation = ctx.root().begin();
+
+ mutation.remove_node(ctx.query().clone());
+
+ Some(JsRuleAction {
+ category: ActionCategory::QuickFix,
+ applicability: Applicability::MaybeIncorrect,
+ message: markup! { "Remove the "<Emphasis>"scope"</Emphasis>" attribute." }.to_owned(),
+ mutation,
+ })
+ }
+}

ADD
crates/rome_js_analyze/tests/specs/nursery/noHeaderScope/invalid.jsx
+<>
+ <div scope={scope} />
+ <div scope={scope}></div>
+ <div scope="col" />
+ <div scope="col"></div>
+</>

ADD
crates/rome_js_analyze/tests/specs/nursery/noHeaderScope/valid.jsx
+<>
+ <th scope="col"></th>
+ <th scope={scope}></th>
+</>

ADD
crates/rome_js_analyze/tests/specs/nursery/noHeaderScope/valid.jsx.snap
+---
+source: crates/rome_js_analyze/tests/spec_tests.rs
+assertion_line: 73
+expression: valid.jsx
+---
+# Input
+```js
+<>
+ <th scope="col"></th>
+ <th scope={scope}></th>
+</>
+
+```
+
+

MODIFY
crates/rome_service/src/configuration/linter/rules.rs
crates/rome_service/src/configuration/linter/rules.rs
@@ -741,6 +741,8 @@ struct NurserySchema {
no_empty_interface: Option<RuleConfiguration>,
#[doc = "Disallow the any type usage"]
no_explicit_any: Option<RuleConfiguration>,
+ #[doc = "Check that the scope attribute is only used on th elements."]
+ no_header_scope: Option<RuleConfiguration>,
#[doc = "Prevents the incorrect use of super() inside classes. It also checks whether a call super() is missing from classes that extends other constructors."]
no_invalid_constructor_super: Option<RuleConfiguration>,
#[doc = "Disallow literal numbers that lose precision"]
@@ -762,13 +764,14 @@ 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] = [
"noBannedTypes",
"noConditionalAssignment",
"noConstAssign",
"noDupeKeys",
"noEmptyInterface",
"noExplicitAny",
+ "noHeaderScope",
"noInvalidConstructorSuper",
"noPrecisionLoss",
"noUnsafeFinally",

MODIFY
editors/vscode/configuration_schema.json
editors/vscode/configuration_schema.json
}
]
},
+ "noHeaderScope": {
+ "description": "Check that the scope attribute is only used on th elements.",
+ "anyOf": [
+ {
+ "$ref": "#/definitions/RuleConfiguration"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"noInvalidConstructorSuper": {
"description": "Prevents the incorrect use of super() inside classes. It also checks whether a call super() is missing from classes that extends other constructors.",
"anyOf": [

MODIFY
npm/backend-jsonrpc/src/workspace.ts
npm/backend-jsonrpc/src/workspace.ts
@@ -371,6 +371,10 @@ export interface Nursery {
* Disallow the any type usage
*/
noExplicitAny?: RuleConfiguration;
+ /**
+ * Check that the scope attribute is only used on th elements.
+ */
+ noHeaderScope?: RuleConfiguration;
/**
* Prevents the incorrect use of super() inside classes. It also checks whether a call super() is missing from classes that extends other constructors.
*/
@@ -621,6 +625,7 @@ export type Category =
| "lint/nursery/noDupeKeys"
| "lint/nursery/noEmptyInterface"
| "lint/nursery/noExplicitAny"
+ | "lint/nursery/noHeaderScope"
| "lint/nursery/noInvalidConstructorSuper"
| "lint/nursery/noPrecisionLoss"
| "lint/nursery/noUnsafeFinally"

MODIFY
npm/rome/configuration_schema.json
npm/rome/configuration_schema.json
}
]
},
+ "noHeaderScope": {
+ "description": "Check that the scope attribute is only used on th elements.",
+ "anyOf": [
+ {
+ "$ref": "#/definitions/RuleConfiguration"
+ },
+ {
+ "type": "null"
+ }
+ ]
+ },
"noInvalidConstructorSuper": {
"description": "Prevents the incorrect use of super() inside classes. It also checks whether a call super() is missing from classes that extends other constructors.",
"anyOf": [

MODIFY
website/src/pages/lint/rules/index.mdx
website/src/pages/lint/rules/index.mdx
@@ -479,6 +479,12 @@ Disallow the declaration of empty interfaces.
Disallow the <code>any</code> type usage
</section>
<section class="rule">
+<h3 data-toc-exclude id="noHeaderScope">
+ <a href="/lint/rules/noHeaderScope">noHeaderScope</a>
+</h3>
+Check that the scope attribute is only used on <code>th</code> elements.
+</section>
+<section class="rule">
<h3 data-toc-exclude id="noInvalidConstructorSuper">
<a href="/lint/rules/noInvalidConstructorSuper">noInvalidConstructorSuper</a>
</h3>

feat(rome_js_analyze): add noHeaderScope rule (#3804)

Closes https://github.com/rome/tools/issues/3815