Skip to content

Commit

Permalink
feat(analyzer): implement noBarrelFile (#1912)
Browse files Browse the repository at this point in the history
  • Loading branch information
togami2864 committed Feb 28, 2024
1 parent 2a84139 commit f73af5d
Show file tree
Hide file tree
Showing 26 changed files with 513 additions and 89 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,12 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
```

Contributed by @fujiyamaorange
- Add rule [noBarrelFile](https://biomejs.dev/linter/rules/no-barrel-file), to report the usage of barrel file:

```js
export * from "foo";
```
Contributed by @togami2864

#### Enhancements

Expand Down
1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ define_categories! {
"lint/correctness/useValidForDirection": "https://biomejs.dev/linter/rules/use-valid-for-direction",
"lint/correctness/useYield": "https://biomejs.dev/linter/rules/use-yield",
"lint/nursery/noApproximativeNumericConstant": "https://biomejs.dev/linter/rules/no-approximative-numeric-constant",
"lint/nursery/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file",
"lint/nursery/noConsole": "https://biomejs.dev/linter/rules/no-console",
"lint/nursery/noDuplicateJsonKeys": "https://biomejs.dev/linter/rules/no-duplicate-json-keys",
"lint/nursery/noDuplicateTestHooks": "https://biomejs.dev/linter/rules/no-duplicate-test-hooks",
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/analyzers/nursery.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 105 additions & 0 deletions crates/biome_js_analyze/src/analyzers/nursery/no_barrel_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use biome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic};
use biome_analyze::{Ast, RuleSource, RuleSourceKind};
use biome_console::markup;
use biome_js_syntax::{JsExport, JsExportFromClause, JsExportNamedFromClause, JsModule};
use biome_rowan::AstNode;

declare_rule! {
/// Disallow the use of barrel file.
///
/// A barrel file is a file that re-exports all of the exports from other files in a directory.
/// This structure results in the unnecessary loading of many modules, significantly impacting performance in large-scale applications.
/// Additionally, it complicates the codebase, making it difficult to navigate and understand the project's dependency graph.
///
/// For a more detailed explanation, check out https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/
///
/// ## Examples
///
/// ### Invalid
///
/// ```ts,expect_diagnostic
/// export * from "foo";
/// export * from "bar";
/// ```
///
/// ```ts,expect_diagnostic
/// export { foo } from "foo";
/// export { bar } from "bar";
/// ```
///
/// ```ts,expect_diagnostic
/// export { default as module1 } from "./module1";
/// ```
///
/// ### Valid
///
/// ```ts
/// export type * from "foo";
/// export type { foo } from "foo";
/// ```
///
pub NoBarrelFile {
version: "next",
name: "noBarrelFile",
recommended: false,
source: RuleSource::EslintBarrelFiles("avoid-namespace-import"),
source_kind: RuleSourceKind::Inspired,
}
}

impl Rule for NoBarrelFile {
type Query = Ast<JsModule>;
type State = JsExport;
type Signals = Option<Self::State>;
type Options = ();

fn run(ctx: &RuleContext<Self>) -> Self::Signals {
let items = ctx.query().items();

for i in items {
if let Some(export) = JsExport::cast(i.into()) {
if let Ok(export_from_clause) = export.export_clause() {
if let Some(export_from_clause) =
JsExportFromClause::cast(export_from_clause.clone().into())
{
if export_from_clause.type_token().is_some() {
return None;
}
}

if let Some(export_from_clause) =
JsExportNamedFromClause::cast(export_from_clause.into())
{
if export_from_clause.type_token().is_some() {
return None;
}
if export_from_clause
.specifiers()
.into_iter()
.flatten()
.all(|s| s.type_token().is_some())
{
return None;
}
}
return Some(export);
}
}
}
None
}

fn diagnostic(_: &RuleContext<Self>, js_export: &Self::State) -> Option<RuleDiagnostic> {
let span = js_export.range();
Some(RuleDiagnostic::new(
rule_category!(),
span,
markup! {
"Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused."
},
).note(
markup! {
"Check "<Hyperlink href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/">"this thorough explanation"</Hyperlink>" to better understand the context."
}))
}
}
2 changes: 2 additions & 0 deletions crates/biome_js_analyze/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub type NoAssignInExpressions = < analyzers :: suspicious :: no_assign_in_expre
pub type NoAsyncPromiseExecutor = < analyzers :: suspicious :: no_async_promise_executor :: NoAsyncPromiseExecutor as biome_analyze :: Rule > :: Options ;
pub type NoAutofocus = <analyzers::a11y::no_autofocus::NoAutofocus as biome_analyze::Rule>::Options;
pub type NoBannedTypes = < semantic_analyzers :: complexity :: no_banned_types :: NoBannedTypes as biome_analyze :: Rule > :: Options ;
pub type NoBarrelFile =
<analyzers::nursery::no_barrel_file::NoBarrelFile as biome_analyze::Rule>::Options;
pub type NoBlankTarget =
<analyzers::a11y::no_blank_target::NoBlankTarget as biome_analyze::Rule>::Options;
pub type NoCatchAssign = < semantic_analyzers :: suspicious :: no_catch_assign :: NoCatchAssign as biome_analyze :: Rule > :: Options ;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { foo, type Bar } from "foo";
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid.ts
---
# Input
```ts
export { foo, type Bar } from "foo";
```

# Diagnostics
```
invalid.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export { foo, type Bar } from "foo";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as module2 } from "./module2";
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid_default_named_alias_reexport.ts
---
# Input
```ts
export { default as module2 } from "./module2";
```

# Diagnostics
```
invalid_default_named_alias_reexport.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export { default as module2 } from "./module2";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { module as module1 } from "./module1";
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid_named_alias_reexport.ts
---
# Input
```ts
export { module as module1 } from "./module1";

```

# Diagnostics
```
invalid_named_alias_reexport.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export { module as module1 } from "./module1";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { baz, qux } from "foobar";
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid_named_reexprt.ts
---
# Input
```ts
export { baz, qux } from "foobar";

```

# Diagnostics
```
invalid_named_reexprt.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export { baz, qux } from "foobar";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as bar from "foo";
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid_wild_alias_reexport.ts
---
# Input
```ts
export * as bar from "foo";

```

# Diagnostics
```
invalid_wild_alias_reexport.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export * as bar from "foo";
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2 │
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "foo";
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid_wild_reexport.ts
---
# Input
```ts
export * from "foo";

```

# Diagnostics
```
invalid_wild_reexport.ts:1:1 lint/nursery/noBarrelFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! Avoid barrel files, they slow down performance, and cause large module graphs with modules that go unused.
> 1 │ export * from "foo";
│ ^^^^^^^^^^^^^^^^^^^^
2 │
i Check this thorough explanation to better understand the context.
```


Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type * from "foo";
export type * as bar from "foo";
export type { foo } from "foo";
export type { baz, qux } from "foobar";
export type { moduleType as moduleType1 } from "module1";
export type { default as moduleType2 } from "module2";
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: valid.ts
---
# Input
```ts
export type * from "foo";
export type * as bar from "foo";
export type { foo } from "foo";
export type { baz, qux } from "foobar";
export type { moduleType as moduleType1 } from "module1";
export type { default as moduleType2 } from "module2";
```


0 comments on commit f73af5d

Please sign in to comment.