diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs
index a293dc7b4..041969d1e 100644
--- a/crates/oxc_linter/src/rules.rs
+++ b/crates/oxc_linter/src/rules.rs
@@ -333,6 +333,7 @@ mod oxc {
pub mod erasing_op;
pub mod misrefactored_assign_op;
pub mod no_accumulating_spread;
+ pub mod no_barrel_file;
pub mod only_used_in_recursion;
}
@@ -671,6 +672,7 @@ oxc_macros::declare_all_lint_rules! {
oxc::erasing_op,
oxc::misrefactored_assign_op,
oxc::no_accumulating_spread,
+ oxc::no_barrel_file,
oxc::only_used_in_recursion,
nextjs::google_font_display,
nextjs::google_font_preconnect,
diff --git a/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
new file mode 100644
index 000000000..4e248965f
--- /dev/null
+++ b/crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
@@ -0,0 +1,114 @@
+use oxc_ast::{ast::Statement, AstKind};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+use oxc_syntax::module_graph_visitor::{ModuleGraphVisitorBuilder, VisitFoldWhile};
+
+use crate::{context::LintContext, rule::Rule};
+
+#[derive(Debug, Error, Diagnostic)]
+#[error(
+ "oxc(no-barrel-file): \
+ Avoid barrel files, they slow down performance, \
+ and cause large module graphs with modules that go unused.\n\
+ Loading this barrel file results in importing {1:?} modules."
+)]
+#[diagnostic(severity(warning), help("For more information visit this link: "))]
+struct NoBarrelFileDiagnostic(#[label] pub Span, pub u32);
+
+/// Minimum amount of exports to consider module as barrelfile
+const AMOUNT_OF_EXPORTS_TO_CONSIDER_MODULE_AS_BARREL: u8 = 3;
+
+///
+#[derive(Debug, Default, Clone)]
+pub struct NoBarrelFile;
+
+declare_oxc_lint!(
+ /// ### What it does
+ ///
+ /// Disallow the use of barrel files.
+ ///
+ /// ### Example
+ ///
+ /// Invalid:
+ /// ```javascript
+ /// export { foo } from 'foo';
+ /// export { bar } from 'bar';
+ /// export { baz } from 'baz';
+ /// export { qux } from 'qux';
+ /// ```
+ /// Valid:
+ /// ```javascript
+ /// export type { foo } from './foo.js';
+ /// ```
+ NoBarrelFile,
+ nursery
+);
+
+impl Rule for NoBarrelFile {
+ fn run_once(&self, ctx: &LintContext<'_>) {
+ let semantic = ctx.semantic();
+ let module_record = semantic.module_record();
+ let root = semantic.nodes().root_node();
+
+ let AstKind::Program(program) = root.kind() else { unreachable!() };
+
+ let declarations = program.body.iter().fold(0, |acc, node| match node {
+ Statement::Declaration(_) => acc + 1,
+ _ => acc,
+ });
+ let exports =
+ module_record.star_export_entries.len() + module_record.indirect_export_entries.len();
+
+ if exports > declarations
+ && exports > AMOUNT_OF_EXPORTS_TO_CONSIDER_MODULE_AS_BARREL as usize
+ {
+ let loaded_modules_count = ModuleGraphVisitorBuilder::default()
+ .visit_fold(0, module_record, |acc, _, _| VisitFoldWhile::Next(acc + 1))
+ .result;
+ ctx.diagnostic(NoBarrelFileDiagnostic(program.span, loaded_modules_count));
+ }
+ }
+}
+
+#[test]
+fn test() {
+ use crate::tester::Tester;
+
+ let pass = vec![
+ r#"export type * from "foo";"#,
+ r#"export type { foo } from "foo";"#,
+ r#"export type * from "foo";
+ export type { bar } from "bar";"#,
+ r#"import { foo, bar, baz } from "../feature";
+ export { foo };
+ export { bar };"#,
+ ];
+
+ let fail = vec![
+ r#"export * from "./deep/a.js";
+ export * from "./deep/b.js";
+ export * from "./deep/c.js";
+ export * from "./deep/d.js";"#,
+ r#"export { foo } from "foo";
+ export { bar } from "bar";
+ export { baz } from "baz";
+ export { qux } from "qux";"#,
+ r#"export { default as module1 } from "./module1";
+ export { default as module2 } from "./module2";
+ export { default as module3 } from "./module3";
+ export { default as module4 } from "./module4";"#,
+ r#"export { foo, type Foo } from "foo";
+ export { bar, type Bar } from "bar";
+ export { baz, type Baz } from "baz";
+ export { qux, type Qux } from "qux";"#,
+ ];
+
+ Tester::new(NoBarrelFile::NAME, pass, fail)
+ .change_rule_path("index.ts")
+ .with_import_plugin(true)
+ .test_and_snapshot();
+}
diff --git a/crates/oxc_linter/src/snapshots/no_barrel_file.snap b/crates/oxc_linter/src/snapshots/no_barrel_file.snap
new file mode 100644
index 000000000..38fb1c178
--- /dev/null
+++ b/crates/oxc_linter/src/snapshots/no_barrel_file.snap
@@ -0,0 +1,43 @@
+---
+source: crates/oxc_linter/src/tester.rs
+expression: no_barrel_file
+---
+ ⚠ oxc(no-barrel-file): Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
+ │ Loading this barrel file results in importing 4 modules.
+ ╭─[index.ts:1:1]
+ 1 │ ╭─▶ export * from "./deep/a.js";
+ 2 │ │ export * from "./deep/b.js";
+ 3 │ │ export * from "./deep/c.js";
+ 4 │ ╰─▶ export * from "./deep/d.js";
+ ╰────
+ help: For more information visit this link:
+
+ ⚠ oxc(no-barrel-file): Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
+ │ Loading this barrel file results in importing 0 modules.
+ ╭─[index.ts:1:1]
+ 1 │ ╭─▶ export { foo } from "foo";
+ 2 │ │ export { bar } from "bar";
+ 3 │ │ export { baz } from "baz";
+ 4 │ ╰─▶ export { qux } from "qux";
+ ╰────
+ help: For more information visit this link:
+
+ ⚠ oxc(no-barrel-file): Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
+ │ Loading this barrel file results in importing 0 modules.
+ ╭─[index.ts:1:1]
+ 1 │ ╭─▶ export { default as module1 } from "./module1";
+ 2 │ │ export { default as module2 } from "./module2";
+ 3 │ │ export { default as module3 } from "./module3";
+ 4 │ ╰─▶ export { default as module4 } from "./module4";
+ ╰────
+ help: For more information visit this link:
+
+ ⚠ oxc(no-barrel-file): Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
+ │ Loading this barrel file results in importing 0 modules.
+ ╭─[index.ts:1:1]
+ 1 │ ╭─▶ export { foo, type Foo } from "foo";
+ 2 │ │ export { bar, type Bar } from "bar";
+ 3 │ │ export { baz, type Baz } from "baz";
+ 4 │ ╰─▶ export { qux, type Qux } from "qux";
+ ╰────
+ help: For more information visit this link: