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

Commit

Permalink
feat(rome_js_analyze): useNamespaceKeyword (#4266)
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos committed Mar 8, 2023
1 parent d1dc465 commit 8d021b9
Show file tree
Hide file tree
Showing 15 changed files with 376 additions and 12 deletions.
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
///
/// 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(
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

0 comments on commit 8d021b9

Please sign in to comment.