From 25ddb3519289dfe2a51e53b9b0c355d8ad950bbc Mon Sep 17 00:00:00 2001 From: Guillaume Piedigrossi <30442116+Spoutnik97@users.noreply.github.com> Date: Tue, 17 Dec 2024 05:04:40 +0100 Subject: [PATCH] feat(linter): add the import/no_named_default rule (#7902) Co-authored-by: Boshen --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/import/no_named_default.rs | 71 +++++++++++++++++++ .../snapshots/import_no_named_default.snap | 24 +++++++ 3 files changed, 97 insertions(+) create mode 100644 crates/oxc_linter/src/rules/import/no_named_default.rs create mode 100644 crates/oxc_linter/src/snapshots/import_no_named_default.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 8a1c0686db7b7..4245090ed683e 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -23,6 +23,7 @@ mod import { pub mod no_dynamic_require; pub mod no_named_as_default; pub mod no_named_as_default_member; + pub mod no_named_default; pub mod no_namespace; pub mod no_self_import; pub mod no_webpack_loader_syntax; @@ -643,6 +644,7 @@ oxc_macros::declare_all_lint_rules! { import::default, import::export, import::first, + import::no_named_default, import::no_namespace, import::max_dependencies, import::named, diff --git a/crates/oxc_linter/src/rules/import/no_named_default.rs b/crates/oxc_linter/src/rules/import/no_named_default.rs new file mode 100644 index 0000000000000..94d3345d5fd92 --- /dev/null +++ b/crates/oxc_linter/src/rules/import/no_named_default.rs @@ -0,0 +1,71 @@ +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; + +fn no_named_default_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Replace default import with named import.") + .with_help("Forbid named default exports.") + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct NoNamedDefault; + +declare_oxc_lint!( + /// ### What it does + /// Reports use of a default export as a locally named import. + /// + /// ### Why is this bad? + /// Rationale: the syntax exists to import default exports expressively, let's use it. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// // message: Using exported name 'bar' as identifier for default export. + /// import { default as foo } from './foo.js'; + /// import { default as foo, bar } from './foo.js'; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// import foo from './foo.js'; + /// import foo, { bar } from './foo.js'; + /// ``` + NoNamedDefault, + style, +); + +impl Rule for NoNamedDefault { + fn run_once(&self, ctx: &LintContext) { + ctx.module_record().import_entries.iter().for_each(|entry| { + let ImportImportName::Name(import_name) = &entry.import_name else { + return; + }; + if import_name.name() == "default" && !entry.is_type { + ctx.diagnostic(no_named_default_diagnostic(import_name.span())); + } + }); + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + r#"import bar from "./bar";"#, + r#"import bar, { foo } from "./bar";"#, + r#"import { type default as Foo } from "./bar";"#, + ]; + + let fail = vec![ + r#"import { default as bar } from "./bar";"#, + r#"import { foo, default as bar } from "./bar";"#, + r#"import { "default" as bar } from "./bar";"#, + ]; + + Tester::new(NoNamedDefault::NAME, NoNamedDefault::CATEGORY, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/import_no_named_default.snap b/crates/oxc_linter/src/snapshots/import_no_named_default.snap new file mode 100644 index 0000000000000..0722ba14c65c2 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/import_no_named_default.snap @@ -0,0 +1,24 @@ +--- +source: crates/oxc_linter/src/tester.rs +snapshot_kind: text +--- + ⚠ eslint-plugin-import(no-named-default): Replace default import with named import. + ╭─[no_named_default.tsx:1:10] + 1 │ import { default as bar } from "./bar"; + · ─────── + ╰──── + help: Forbid named default exports. + + ⚠ eslint-plugin-import(no-named-default): Replace default import with named import. + ╭─[no_named_default.tsx:1:15] + 1 │ import { foo, default as bar } from "./bar"; + · ─────── + ╰──── + help: Forbid named default exports. + + ⚠ eslint-plugin-import(no-named-default): Replace default import with named import. + ╭─[no_named_default.tsx:1:10] + 1 │ import { "default" as bar } from "./bar"; + · ───────── + ╰──── + help: Forbid named default exports.