mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): no barrel file. (#3030)
closes #3004
I've based it on [this plugin](https://github.com/thepassle/eslint-plugin-barrel-files/blob/main/lib/rules/avoid-barrel-files.js) instead of [biome](4130ff0e4c/crates/biome_js_analyze/src/lint/performance/no_barrel_file.rs (L70)) Since the original plugin is less likely to detect a false positive.
I didn't understand your statement [here](https://github.com/oxc-project/oxc/issues/3004#issue-2245574895), Where you've mentioned:
> In oxlint, we can do better when --import-plugin is enabled, by displaying the total number of dependencies pulled into a given file by walking
I would appreciate it if you expand upon it.
------
#### Edit:
I've added it under `eslint-plugin-import` even though it is not; I wasn't sure If I should create a whole new category for this single rule, It is a different story if we would like to adopt the other rules in [here](https://github.com/thepassle/eslint-plugin-barrel-files/tree/main/lib/rules).
Also, check my diagnosis messages; It is my first contribution to the linters so I'm just not familiar enough with the conventions of messages, rules, etc.
This commit is contained in:
parent
e6d11c6190
commit
5cf55c212e
3 changed files with 159 additions and 0 deletions
|
|
@ -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,
|
||||
|
|
|
|||
114
crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
Normal file
114
crates/oxc_linter/src/rules/oxc/no_barrel_file.rs
Normal file
|
|
@ -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: <https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/>"))]
|
||||
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;
|
||||
|
||||
/// <https://github.com/thepassle/eslint-plugin-barrel-files/blob/main/docs/rules/avoid-barrel-files.md>
|
||||
#[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();
|
||||
}
|
||||
43
crates/oxc_linter/src/snapshots/no_barrel_file.snap
Normal file
43
crates/oxc_linter/src/snapshots/no_barrel_file.snap
Normal file
|
|
@ -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: <https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/>
|
||||
|
||||
⚠ 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: <https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/>
|
||||
|
||||
⚠ 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: <https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/>
|
||||
|
||||
⚠ 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: <https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/>
|
||||
Loading…
Reference in a new issue