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

Commit d33ce14

Browse files
committed
feat(rome_js_analyze): useNamespaceKeyword
1 parent dd3a94d commit d33ce14

File tree

15 files changed

+376
-12
lines changed

15 files changed

+376
-12
lines changed

crates/rome_diagnostics_categories/src/categories.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ define_categories! {
100100
"lint/nursery/noParameterAssign": "https://docs.rome.tools/lint/rules/noParameterAssign",
101101
// Insert new nursery rule here
102102
"lint/nursery/noRedeclaration": "https://docs.rome.tools/lint/rules/noRedeclaration",
103+
"lint/nursery/useNamespaceKeyword": "https://docs.rome.tools/lint/rules/useNamespaceKeyword",
103104

104105
// performance
105106
"lint/performance/noDelete": "https://docs.rome.tools/lint/rules/noDelete",

crates/rome_js_analyze/src/analyzers/nursery.rs

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use rome_analyze::{context::RuleContext, declare_rule, ActionCategory, Ast, Rule, RuleDiagnostic};
2+
use rome_console::markup;
3+
use rome_diagnostics::Applicability;
4+
use rome_js_factory::make;
5+
use rome_js_syntax::{JsSyntaxToken, TsModuleDeclaration, T};
6+
use rome_rowan::BatchMutationExt;
7+
8+
use crate::JsRuleAction;
9+
10+
declare_rule! {
11+
/// Require using the `namespace` keyword over the `module` keyword to declare TypeScript namespaces.
12+
///
13+
/// TypeScript historically allowed a code organization called _namespace_.
14+
/// [_ECMAScript modules_ are preferred](https://www.typescriptlang.org/docs/handbook/2/modules.html#typescript-namespaces) (`import` / `export`).
15+
///
16+
/// For projects still using _namespaces_, it's preferred to use the `namespace` keyword instead of the `module` keyword.
17+
/// The `module` keyword is deprecated to avoid any confusion with the _ECMAScript modules_ which are often called _modules_.
18+
///
19+
/// Note that TypeScript `module` declarations to describe external APIs (`declare module "foo" {}`) are still allowed.
20+
///
21+
/// Source: https://typescript-eslint.io/rules/prefer-namespace-keyword
22+
///
23+
/// See also: https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html
24+
///
25+
/// ## Examples
26+
///
27+
/// ### Invalid
28+
///
29+
/// ```ts,expect_diagnostic
30+
/// module Example {}
31+
/// ```
32+
///
33+
/// ## Valid
34+
///
35+
/// ```ts
36+
/// namespace Example {}
37+
/// ```
38+
///
39+
/// ```ts
40+
/// declare module "foo" {}
41+
/// ```
42+
///
43+
pub(crate) UseNamespaceKeyword {
44+
version: "12.0.0",
45+
name: "useNamespaceKeyword",
46+
recommended: false,
47+
}
48+
}
49+
50+
impl Rule for UseNamespaceKeyword {
51+
type Query = Ast<TsModuleDeclaration>;
52+
type State = JsSyntaxToken;
53+
type Signals = Option<Self::State>;
54+
type Options = ();
55+
56+
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
57+
let ts_module = ctx.query();
58+
let token = ts_module.module_or_namespace().ok()?;
59+
ts_module.is_module().ok()?.then_some(token)
60+
}
61+
62+
fn diagnostic(_: &RuleContext<Self>, module_token: &Self::State) -> Option<RuleDiagnostic> {
63+
Some(RuleDiagnostic::new(
64+
rule_category!(),
65+
module_token.text_trimmed_range(),
66+
markup! {
67+
"Use the "<Emphasis>"namespace"</Emphasis>" keyword instead of the outdated "<Emphasis>"module"</Emphasis>" keyword."
68+
},
69+
).note(markup! {
70+
"The "<Emphasis>"module"</Emphasis>" keyword is deprecated to avoid any confusion with the "<Emphasis>"ECMAScript modules"</Emphasis>" which are often called "<Emphasis>"modules"</Emphasis>"."
71+
}))
72+
}
73+
74+
fn action(ctx: &RuleContext<Self>, module_token: &Self::State) -> Option<JsRuleAction> {
75+
let mut mutation = ctx.root().begin();
76+
mutation.replace_token_transfer_trivia(module_token.clone(), make::token(T![namespace]));
77+
Some(JsRuleAction {
78+
category: ActionCategory::QuickFix,
79+
applicability: Applicability::Always,
80+
message: markup! {"Use "<Emphasis>"namespace"</Emphasis>" instead."}.to_owned(),
81+
mutation,
82+
})
83+
}
84+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*before*/module/*after*/ foo {}
2+
3+
declare module bar {}
4+
5+
declare module outer {
6+
export module inner {}
7+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
source: crates/rome_js_analyze/tests/spec_tests.rs
3+
assertion_line: 91
4+
expression: invalid.ts
5+
---
6+
# Input
7+
```js
8+
/*before*/module/*after*/ foo {}
9+
10+
declare module bar {}
11+
12+
declare module outer {
13+
export module inner {}
14+
}
15+
```
16+
17+
# Diagnostics
18+
```
19+
invalid.ts:1:11 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
20+
21+
! Use the namespace keyword instead of the outdated module keyword.
22+
23+
> 1 │ /*before*/module/*after*/ foo {}
24+
│ ^^^^^^
25+
2 │
26+
3 │ declare module bar {}
27+
28+
i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.
29+
30+
i Safe fix: Use namespace instead.
31+
32+
1 │ - /*before*/module/*after*/·foo·{}
33+
1 │ + /*before*/namespace/*after*/·foo·{}
34+
2 2 │
35+
3 3 │ declare module bar {}
36+
37+
38+
```
39+
40+
```
41+
invalid.ts:3:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
42+
43+
! Use the namespace keyword instead of the outdated module keyword.
44+
45+
1 │ /*before*/module/*after*/ foo {}
46+
2 │
47+
> 3 │ declare module bar {}
48+
│ ^^^^^^
49+
4 │
50+
5 │ declare module outer {
51+
52+
i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.
53+
54+
i Safe fix: Use namespace instead.
55+
56+
1 1/*before*/module/*after*/ foo {}
57+
2 2
58+
3- declare·module·bar·{}
59+
3+ declare·namespace·bar·{}
60+
4 4
61+
5 5declare module outer {
62+
63+
64+
```
65+
66+
```
67+
invalid.ts:5:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
68+
69+
! Use the namespace keyword instead of the outdated module keyword.
70+
71+
3declare module bar {}
72+
4
73+
> 5declare module outer {
74+
│ ^^^^^^
75+
6 │ export module inner {}
76+
7 │ }
77+
78+
i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.
79+
80+
i Safe fix: Use namespace instead.
81+
82+
3 3declare module bar {}
83+
4 4
84+
5- declare·module·outer·{
85+
5 │ + declare·namespace·outer·{
86+
6 6 │ export module inner {}
87+
7 7}
88+
89+
90+
```
91+
92+
```
93+
invalid.ts:6:9 lint/nursery/useNamespaceKeyword FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
94+
95+
! Use the namespace keyword instead of the outdated module keyword.
96+
97+
5 │ declare module outer {
98+
> 6export module inner {}
99+
^^^^^^
100+
7}
101+
102+
i The module keyword is deprecated to avoid any confusion with the ECMAScript modules which are often called modules.
103+
104+
i Safe fix: Use namespace instead.
105+
106+
4 4 │
107+
5 5 │ declare module outer {
108+
6-export·module·inner·{}
109+
6+export·namespace·inner·{}
110+
7 7}
111+
112+
113+
```
114+
115+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
declare module 'foo';
2+
3+
declare module "foo" {}
4+
5+
namespace foo {}
6+
7+
declare namespace foo {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/rome_js_analyze/tests/spec_tests.rs
3+
assertion_line: 91
4+
expression: valid.ts
5+
---
6+
# Input
7+
```js
8+
declare module 'foo';
9+
10+
declare module "foo" {}
11+
12+
namespace foo {}
13+
14+
declare namespace foo {}
15+
16+
```
17+
18+

crates/rome_js_syntax/src/stmt_ext.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::{
44
AnyJsArrayAssignmentPatternElement, AnyJsAssignmentPattern, AnyJsSwitchClause,
55
JsForVariableDeclaration, JsStatementList, JsSyntaxToken as SyntaxToken, JsVariableDeclaration,
6-
T,
6+
TsModuleDeclaration, T,
77
};
88
use rome_rowan::SyntaxResult;
99

@@ -104,6 +104,16 @@ impl AnyJsArrayAssignmentPatternElement {
104104
}
105105
}
106106

107+
impl TsModuleDeclaration {
108+
pub fn is_module(&self) -> SyntaxResult<bool> {
109+
Ok(self.module_or_namespace()?.kind() == T![module])
110+
}
111+
112+
pub fn is_namespace(&self) -> SyntaxResult<bool> {
113+
Ok(self.module_or_namespace()?.kind() == T![namespace])
114+
}
115+
}
116+
107117
#[cfg(test)]
108118
mod tests {
109119
use rome_js_factory::syntax::{JsSyntaxKind::*, JsVariableDeclaration};

0 commit comments

Comments
 (0)