feat(rome_js_analyze): rule `noConstAssign`
MODIFY
crates/rome_diagnostics_categories/src/categories.rs
crates/rome_diagnostics_categories/src/categories.rs
@@ -60,6 +60,7 @@ define_dategories! {
"lint/nursery/useKeyWithClickEvents": "https://rome.tools/docs/lint/rules/useKeyWithClickEvents",
"lint/nursery/noRestrictedGlobals": "https://rome.tools/docs/lint/rules/noRestrictedGlobals",
"lint/nursery/noPositiveTabindex": "https://rome.tools/docs/lint/rules/noPositiveTabindex",
+ "lint/nursery/noConstAssign": "https://rome.tools/docs/lint/rules/noConstAssign",
"lint/nursery/noExplicitAny": "https://rome.tools/docs/lint/rules/noExplicitAny",
"lint/style/noNegationElse": "https://rome.tools/docs/lint/rules/noNegationElse",
"lint/style/noShoutyConstants": "https://rome.tools/docs/lint/rules/noShoutyConstants",

MODIFY
crates/rome_js_analyze/src/semantic_analyzers/nursery.rs
crates/rome_js_analyze/src/semantic_analyzers/nursery.rs
use rome_analyze::declare_group;
mod no_array_index_key;
mod no_children_prop;
+mod no_const_assign;
mod no_dangerously_set_inner_html;
mod no_dangerously_set_inner_html_with_children;
mod no_explicit_any;
@@ -17,4 +18,4 @@ mod use_button_type;
mod use_camel_case;
mod use_fragment_syntax;
mod use_key_with_mouse_events;
-declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_array_index_key :: NoArrayIndexKey , self :: no_children_prop :: NoChildrenProp , self :: no_dangerously_set_inner_html :: NoDangerouslySetInnerHtml , self :: no_dangerously_set_inner_html_with_children :: NoDangerouslySetInnerHtmlWithChildren , self :: no_explicit_any :: NoExplicitAny , self :: no_positive_tabindex :: NoPositiveTabindex , self :: no_render_return_value :: NoRenderReturnValue , self :: no_restricted_globals :: NoRestrictedGlobals , self :: no_undeclared_variables :: NoUndeclaredVariables , self :: no_unused_variables :: NoUnusedVariables , self :: no_useless_fragments :: NoUselessFragments , self :: no_void_elements_with_children :: NoVoidElementsWithChildren , self :: use_button_type :: UseButtonType , self :: use_camel_case :: UseCamelCase , self :: use_fragment_syntax :: UseFragmentSyntax , self :: use_key_with_mouse_events :: UseKeyWithMouseEvents ,] } }
+declare_group! { pub (crate) Nursery { name : "nursery" , rules : [self :: no_array_index_key :: NoArrayIndexKey , self :: no_children_prop :: NoChildrenProp , self :: no_const_assign :: NoConstAssign , self :: no_dangerously_set_inner_html :: NoDangerouslySetInnerHtml , self :: no_dangerously_set_inner_html_with_children :: NoDangerouslySetInnerHtmlWithChildren , self :: no_explicit_any :: NoExplicitAny , self :: no_positive_tabindex :: NoPositiveTabindex , self :: no_render_return_value :: NoRenderReturnValue , self :: no_restricted_globals :: NoRestrictedGlobals , self :: no_undeclared_variables :: NoUndeclaredVariables , self :: no_unused_variables :: NoUnusedVariables , self :: no_useless_fragments :: NoUselessFragments , self :: no_void_elements_with_children :: NoVoidElementsWithChildren , self :: use_button_type :: UseButtonType , self :: use_camel_case :: UseCamelCase , self :: use_fragment_syntax :: UseFragmentSyntax , self :: use_key_with_mouse_events :: UseKeyWithMouseEvents ,] } }

ADD
crates/rome_js_analyze/src/semantic_analyzers/nursery/no_const_assign.rs
+use crate::semantic_services::Semantic;
+use rome_analyze::context::RuleContext;
+use rome_analyze::{declare_rule, Rule, RuleDiagnostic};
+use rome_console::markup;
+use rome_js_syntax::{JsIdentifierAssignment, JsVariableDeclaration};
+use rome_rowan::{AstNode, TextRange};
+
+declare_rule! {
+ /// Prevents from having `const` variables being re-assigned.
+ ///
+ /// Trying to assign a value to a `const` will cause an `TypeError` when the code is executed.
+ ///
+ /// ## Examples
+ ///
+ /// ### Invalid
+ ///
+ /// ```js,expect_diagnostic
+ /// const a = 1;
+ /// a = 4;
+ /// ```
+ ///
+ /// ```js,expect_diagnostic
+ /// const a = 2;
+ /// a += 1;
+ /// ```
+ ///
+ /// ```js,expect_diagnostic
+ /// const a = 1;
+ /// ++a;
+ /// ```
+ ///
+ /// ```js,expect_diagnostic
+ /// const a = 1, b = 2;
+ ///
+ /// a = 2;
+ /// ```
+ ///
+ /// ### Valid
+ ///
+ /// ```js
+ /// const a = 10;
+ /// let b = 10;
+ /// b = 20;
+ /// ```
+ ///
+ pub(crate) NoConstAssign {
+ version: "10.0.0",
+ name: "noConstAssign",
+ recommended: false,
+ }
+}
+
+impl Rule for NoConstAssign {
+ type Query = Semantic<JsIdentifierAssignment>;
+ type State = TextRange;
+ type Signals = Option<Self::State>;
+ type Options = ();
+
+ fn run(ctx: &RuleContext<Self>) -> Self::Signals {
+ let node = ctx.query();
+ let model = ctx.model();
+
+ let declared_binding = model.declaration(node)?;
+ if let Some(variable_declaration) = declared_binding
+ .syntax()
+ .ancestors()
+ .find_map(|ancestor| JsVariableDeclaration::cast_ref(&ancestor))
+ {
+ if variable_declaration.is_const() {
+ return Some(declared_binding.syntax().text_trimmed_range());
+ }
+ }
+
+ None
+ }
+
+ fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> {
+ let node = ctx.query();
+ let name = node.name_token().ok()?;
+ let name = name.text_trimmed();
+ Some(
+ RuleDiagnostic::new(
+ rule_category!(),
+ node.syntax().text_trimmed_range(),
+ markup! {"Can't assign "<Emphasis>{name}</Emphasis>" because it's a constant"},
+ )
+ .secondary(
+ state,
+ markup! {"This is where the variable is defined as constant"},
+ ),
+ )
+ }
+}

ADD
crates/rome_js_analyze/tests/specs/nursery/noConstAssign.js
+const a = 1;
+a = 2;
+
+const b = 2, c = 43;
+b = 4;
+++b;
+b += 45;
+b--;
+function f() {
+ b++;
+}
\ No newline at end of file

MODIFY
crates/rome_service/src/configuration/linter/rules.rs
crates/rome_service/src/configuration/linter/rules.rs
@@ -389,6 +389,7 @@ struct NurserySchema {
no_array_index_key: Option<RuleConfiguration>,
no_autofocus: Option<RuleConfiguration>,
no_children_prop: Option<RuleConfiguration>,
+ no_const_assign: Option<RuleConfiguration>,
no_dangerously_set_inner_html: Option<RuleConfiguration>,
no_dangerously_set_inner_html_with_children: Option<RuleConfiguration>,
no_explicit_any: Option<RuleConfiguration>,
@@ -413,10 +414,11 @@ struct NurserySchema {
}
impl Nursery {
const CATEGORY_NAME: &'static str = "nursery";
- pub(crate) const CATEGORY_RULES: [&'static str; 24] = [
+ pub(crate) const CATEGORY_RULES: [&'static str; 25] = [
"noArrayIndexKey",
"noAutofocus",
"noChildrenProp",
+ "noConstAssign",
"noDangerouslySetInnerHtml",
"noDangerouslySetInnerHtmlWithChildren",
"noExplicitAny",

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

MODIFY
npm/backend-jsonrpc/src/workspace.ts
npm/backend-jsonrpc/src/workspace.ts
@@ -158,6 +158,7 @@ export interface Nursery {
noArrayIndexKey?: RuleConfiguration;
noAutofocus?: RuleConfiguration;
noChildrenProp?: RuleConfiguration;
+ noConstAssign?: RuleConfiguration;
noDangerouslySetInnerHtml?: RuleConfiguration;
noDangerouslySetInnerHtmlWithChildren?: RuleConfiguration;
noExplicitAny?: RuleConfiguration;
@@ -323,6 +324,7 @@ export type Category =
| "lint/nursery/useKeyWithClickEvents"
| "lint/nursery/noRestrictedGlobals"
| "lint/nursery/noPositiveTabindex"
+ | "lint/nursery/noConstAssign"
| "lint/nursery/noExplicitAny"
| "lint/style/noNegationElse"
| "lint/style/noShoutyConstants"

MODIFY
rome.json
rome.json
"noNewSymbol": "error",
"noVoidElementsWithChildren": "error",
"noDangerouslySetInnerHtmlWithChildren": "error",
+ "noConstAssign": "error",
"noAutofocus": "error",
"useKeyWithClickEvents": "error"
}

MODIFY
website/playground/src/romeWorker.ts
website/playground/src/romeWorker.ts
@@ -122,6 +122,7 @@ self.addEventListener("message", async (e) => {
noUselessFragments: "error",
noVoidElementsWithChildren: "error",
noDangerouslySetInnerHtmlWithChildren: "error",
+ noConstAssign: "error",
},
},
};

MODIFY
website/src/docs/lint/rules/index.md
website/src/docs/lint/rules/index.md
@@ -276,6 +276,13 @@ Avoid the <code>autoFocus</code> attribute
Prevent passing of <strong>children</strong> as props.
</div>
<div class="rule">
+<h3 data-toc-exclude id="noConstAssign">
+ <a href="/docs/lint/rules/noConstAssign">noConstAssign (since v10.0.0)</a>
+ <a class="header-anchor" href="#noConstAssign"></a>
+</h3>
+Prevents from having <code>const</code> variables being re-assigned.
+</div>
+<div class="rule">
<h3 data-toc-exclude id="noDangerouslySetInnerHtml">
<a href="/docs/lint/rules/noDangerouslySetInnerHtml">noDangerouslySetInnerHtml (since v0.10.0)</a>
<a class="header-anchor" href="#noDangerouslySetInnerHtml"></a>

feat(rome_js_analyze): rule `noConstAssign` (#3409)