mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): add typescript-eslint/prefer-keyword-namespce (#4438)
This commit is contained in:
parent
e6a8af6112
commit
4c4da561f4
3 changed files with 152 additions and 0 deletions
|
|
@ -158,6 +158,7 @@ mod typescript {
|
|||
pub mod prefer_for_of;
|
||||
pub mod prefer_function_type;
|
||||
pub mod prefer_literal_enum_member;
|
||||
pub mod prefer_namespace_keyword;
|
||||
pub mod prefer_ts_expect_error;
|
||||
pub mod triple_slash_reference;
|
||||
}
|
||||
|
|
@ -569,6 +570,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
typescript::prefer_as_const,
|
||||
typescript::prefer_for_of,
|
||||
typescript::prefer_function_type,
|
||||
typescript::prefer_namespace_keyword,
|
||||
typescript::prefer_ts_expect_error,
|
||||
typescript::triple_slash_reference,
|
||||
typescript::prefer_literal_enum_member,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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`.
|
||||
Loading…
Reference in a new issue