Skip to content

Commit

Permalink
feat(linter): add the import/no_named_default rule (#7902)
Browse files Browse the repository at this point in the history
Co-authored-by: Boshen <[email protected]>
  • Loading branch information
Spoutnik97 and Boshen authored Dec 17, 2024
1 parent 99a431b commit 25ddb35
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
71 changes: 71 additions & 0 deletions crates/oxc_linter/src/rules/import/no_named_default.rs
Original file line number Diff line number Diff line change
@@ -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();
}
24 changes: 24 additions & 0 deletions crates/oxc_linter/src/snapshots/import_no_named_default.snap
Original file line number Diff line number Diff line change
@@ -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]
1import { 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]
1import { 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]
1import { "default" as bar } from "./bar";
· ─────────
╰────
help: Forbid named default exports.

0 comments on commit 25ddb35

Please sign in to comment.