Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

feat(rome_js_analyze): useNamespaceKeyword #4266

Merged
merged 1 commit into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/rome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ define_categories! {
"lint/nursery/noParameterAssign": "https://docs.rome.tools/lint/rules/noParameterAssign",
// Insert new nursery rule here
"lint/nursery/noRedeclaration": "https://docs.rome.tools/lint/rules/noRedeclaration",
"lint/nursery/useNamespaceKeyword": "https://docs.rome.tools/lint/rules/useNamespaceKeyword",

// performance
"lint/performance/noDelete": "https://docs.rome.tools/lint/rules/noDelete",
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_js_analyze/src/analyzers/nursery.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use rome_analyze::{context::RuleContext, declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic};
use rome_console::markup;
use rome_diagnostics::Applicability;
use rome_js_factory::make;
use rome_js_syntax::{JsSyntaxToken, TsModuleDeclaration, T};
use rome_rowan::BatchMutationExt;

use crate::JsRuleAction;

declare_rule! {
/// Require using the `namespace` keyword over the `module` keyword to declare TypeScript namespaces.
///
/// TypeScript historically allowed a code organization called _namespace_.
/// [_ECMAScript modules_ are preferred](https://www.typescriptlang.org/docs/handbook/2/modules.html#typescript-namespaces) (`import` / `export`).
///
/// For projects still using _namespaces_, it's preferred to use the `namespace` keyword instead of the `module` keyword.
/// The `module` keyword is deprecated to avoid any confusion with the _ECMAScript modules_ which are often called _modules_.
///
/// Note that TypeScript `module` declarations to describe external APIs (`declare module "foo" {}`) are still allowed.
///
/// Source: https://typescript-eslint.io/rules/prefer-namespace-keyword
Conaclos marked this conversation as resolved.
Show resolved Hide resolved
///
/// See also: https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html
///
/// ## Examples
///
/// ### Invalid
///
/// ```ts,expect_diagnostic
/// module Example {}
/// ```
///
/// ## Valid
///
/// ```ts
/// namespace Example {}
/// ```
///
/// ```ts
/// declare module "foo" {}
/// ```
///
pub(crate) UseNamespaceKeyword {
version: "12.0.0",
name: "useNamespaceKeyword",
recommended: false,
}
}

impl Rule for UseNamespaceKeyword {
type Query = Ast<TsModuleDeclaration>;
type State = JsSyntaxToken;
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let ts_module = ctx.query();
let token = ts_module.module_or_namespace().ok()?;
ts_module.is_module().ok()?.then_some(token)
}

fn diagnostic(_: &RuleContext<Self>, module_token: &Self::State) -> Option<RuleDiagnostic> {
Some(RuleDiagnostic::new(
Conaclos marked this conversation as resolved.
Show resolved Hide resolved
rule_category!(),
module_token.text_trimmed_range(),
markup! {
"Use the "<Emphasis>"namespace"</Emphasis>" keyword instead of the outdated "<Emphasis>"module"</Emphasis>" keyword."
},
).note(markup! {
"The "<Emphasis>"module"</Emphasis>" keyword is deprecated to avoid any confusion with the "<Emphasis>"ECMAScript modules"</Emphasis>" which are often called "<Emphasis>"modules"</Emphasis>"."
}))
}

fn action(ctx: &RuleContext<Self>, module_token: &Self::State) -> Option<JsRuleAction> {
let mut mutation = ctx.root().begin();
mutation.replace_token_transfer_trivia(module_token.clone(), make::token(T![namespace]));
Some(JsRuleAction {
category: ActionCategory::QuickFix,
applicability: Applicability::Always,
message: markup! {"Use "<Emphasis>"namespace"</Emphasis>" instead."}.to_owned(),
mutation,
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*before*/module/*after*/ foo {}

declare module bar {}

declare module outer {
export module inner {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
assertion_line: 91
expression: invalid.ts
---
# Input
```js
/*before*/module/*after*/ foo {}

declare module bar {}

declare module outer {
export module inner {}
}
```

# Diagnostics
```
invalid.ts:1:11 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Use the namespace keyword instead of the outdated module keyword.

> 1 │ /*before*/module/*after*/ foo {}
│ ^^^^^^
2 │
3 │ declare module bar {}

i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.

i Safe fix: Use namespace instead.

1 │ - /*before*/module/*after*/·foo·{}
1 │ + /*before*/namespace/*after*/·foo·{}
2 2 │
3 3 │ declare module bar {}


```

```
invalid.ts:3:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Use the namespace keyword instead of the outdated module keyword.

1 │ /*before*/module/*after*/ foo {}
2 │
> 3 │ declare module bar {}
│ ^^^^^^
4 │
5 │ declare module outer {

i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.

i Safe fix: Use namespace instead.

1 1 │ /*before*/module/*after*/ foo {}
2 2 │
3 │ - declare·module·bar·{}
3 │ + declare·namespace·bar·{}
4 4 │
5 5 │ declare module outer {


```

```
invalid.ts:5:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Use the namespace keyword instead of the outdated module keyword.

3 │ declare module bar {}
4 │
> 5 │ declare module outer {
│ ^^^^^^
6 │ export module inner {}
7 │ }

i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.

i Safe fix: Use namespace instead.

3 3 │ declare module bar {}
4 4 │
5 │ - declare·module·outer·{
5 │ + declare·namespace·outer·{
6 6 │ export module inner {}
7 7 │ }


```

```
invalid.ts:6:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! Use the namespace keyword instead of the outdated module keyword.

5 │ declare module outer {
> 6 │ export module inner {}
│ ^^^^^^
7 │ }

i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.

i Safe fix: Use namespace instead.

4 4 │
5 5 │ declare module outer {
6 │ - → export·module·inner·{}
6 │ + → export·namespace·inner·{}
7 7 │ }


```


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'foo';

declare module "foo" {}

namespace foo {}

declare namespace foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
source: crates/rome_js_analyze/tests/spec_tests.rs
assertion_line: 91
expression: valid.ts
---
# Input
```js
declare module 'foo';

declare module "foo" {}

namespace foo {}

declare namespace foo {}

```


12 changes: 11 additions & 1 deletion crates/rome_js_syntax/src/stmt_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
AnyJsArrayAssignmentPatternElement, AnyJsAssignmentPattern, AnyJsSwitchClause,
JsForVariableDeclaration, JsStatementList, JsSyntaxToken as SyntaxToken, JsVariableDeclaration,
T,
TsModuleDeclaration, T,
};
use rome_rowan::SyntaxResult;

Expand Down Expand Up @@ -104,6 +104,16 @@ impl AnyJsArrayAssignmentPatternElement {
}
}

impl TsModuleDeclaration {
pub fn is_module(&self) -> SyntaxResult<bool> {
Ok(self.module_or_namespace()?.kind() == T![module])
}

pub fn is_namespace(&self) -> SyntaxResult<bool> {
Ok(self.module_or_namespace()?.kind() == T![namespace])
}
}

#[cfg(test)]
mod tests {
use rome_js_factory::syntax::{JsSyntaxKind::*, JsVariableDeclaration};
Expand Down
Loading