-
-
Notifications
You must be signed in to change notification settings - Fork 482
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter): add typescript-eslint/prefer-keyword-namespce (#4438)
- Loading branch information
Showing
3 changed files
with
152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
crates/oxc_linter/src/rules/typescript/prefer_namespace_keyword.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
use oxc_ast::{ | ||
ast::{TSModuleDeclarationKind, TSModuleDeclarationName}, | ||
AstKind, | ||
}; | ||
use oxc_diagnostics::OxcDiagnostic; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
|
||
use crate::{context::LintContext, rule::Rule, AstNode}; | ||
|
||
fn prefer_namespace_keyword_diagnostic(span: Span) -> OxcDiagnostic { | ||
OxcDiagnostic::warn("Use 'namespace' instead of 'module' to declare custom TypeScript modules.") | ||
.with_label(span) | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct PreferNamespaceKeyword; | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// This rule reports when the module keyword is used instead of namespace. | ||
/// This rule does not report on the use of TypeScript module declarations to describe external APIs (declare module 'foo' {}). | ||
/// | ||
/// ### Why is this bad? | ||
/// Namespaces are an outdated way to organize TypeScript code. ES2015 module syntax is now preferred (import/export). | ||
/// For projects still using custom modules / namespaces, it's preferred to refer to them as namespaces. | ||
/// | ||
/// ### Example | ||
/// ```typescript | ||
/// module Example {} | ||
/// ``` | ||
PreferNamespaceKeyword, | ||
style | ||
); | ||
|
||
impl Rule for PreferNamespaceKeyword { | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
let AstKind::TSModuleDeclaration(module) = node.kind() else { return }; | ||
if module.id.is_string_literal() | ||
|| !matches!(module.id, TSModuleDeclarationName::Identifier(_)) | ||
|| module.kind != TSModuleDeclarationKind::Module | ||
{ | ||
return; | ||
} | ||
|
||
ctx.diagnostic_with_fix(prefer_namespace_keyword_diagnostic(module.span), |fixer| { | ||
let span_size = u32::try_from("module".len()).unwrap_or(6); | ||
let span_start = if module.declare { | ||
module.span.start + u32::try_from("declare ".len()).unwrap_or(8) | ||
} else { | ||
module.span.start | ||
}; | ||
fixer.replace(Span::sized(span_start, span_size), "namespace") | ||
}); | ||
} | ||
|
||
fn should_run(&self, ctx: &LintContext) -> bool { | ||
ctx.source_type().is_typescript() | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
"declare module 'foo';", | ||
"declare module 'foo' {}", | ||
"namespace foo {}", | ||
"declare namespace foo {}", | ||
"declare global {}", | ||
]; | ||
|
||
let fail = vec![ | ||
"module foo {}", | ||
"declare module foo {}", | ||
" | ||
declare module foo { | ||
declare module bar {} | ||
} | ||
", | ||
"declare global { | ||
module foo {} | ||
} | ||
", | ||
]; | ||
|
||
let fix = vec![ | ||
("module foo {}", "namespace foo {}", None), | ||
("declare module foo {}", "declare namespace foo {}", None), | ||
( | ||
" | ||
declare module foo { | ||
declare module bar {} | ||
} | ||
", | ||
" | ||
declare namespace foo { | ||
declare namespace bar {} | ||
} | ||
", | ||
None, | ||
), | ||
]; | ||
Tester::new(PreferNamespaceKeyword::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); | ||
} |
44 changes: 44 additions & 0 deletions
44
crates/oxc_linter/src/snapshots/prefer_namespace_keyword.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
--- | ||
source: crates/oxc_linter/src/tester.rs | ||
--- | ||
⚠ typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules. | ||
╭─[prefer_namespace_keyword.tsx:1:1] | ||
1 │ module foo {} | ||
· ───────────── | ||
╰──── | ||
help: Replace `module` with `namespace`. | ||
|
||
⚠ typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules. | ||
╭─[prefer_namespace_keyword.tsx:1:1] | ||
1 │ declare module foo {} | ||
· ───────────────────── | ||
╰──── | ||
help: Replace `module` with `namespace`. | ||
|
||
⚠ typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules. | ||
╭─[prefer_namespace_keyword.tsx:2:4] | ||
1 │ | ||
2 │ ╭─▶ declare module foo { | ||
3 │ │ declare module bar {} | ||
4 │ ╰─▶ } | ||
5 │ | ||
╰──── | ||
help: Replace `module` with `namespace`. | ||
|
||
⚠ typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules. | ||
╭─[prefer_namespace_keyword.tsx:3:6] | ||
2 │ declare module foo { | ||
3 │ declare module bar {} | ||
· ───────────────────── | ||
4 │ } | ||
╰──── | ||
help: Replace `module` with `namespace`. | ||
|
||
⚠ typescript-eslint(prefer-namespace-keyword): Use 'namespace' instead of 'module' to declare custom TypeScript modules. | ||
╭─[prefer_namespace_keyword.tsx:2:13] | ||
1 │ declare global { | ||
2 │ module foo {} | ||
· ───────────── | ||
3 │ } | ||
╰──── | ||
help: Replace `module` with `namespace`. |