-
-
Notifications
You must be signed in to change notification settings - Fork 874
feat(biome_js_analyze): implement noExcessiveLinesPerFile #8639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ematipico
merged 15 commits into
biomejs:main
from
ohnoah:feat/no-excessive-lines-per-file
Jan 4, 2026
Merged
Changes from 11 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
5efba8f
feat(biome_js_analyze): implement noExcessiveLinesPerFile
ohnoah 036867a
fix(biome_js_analyze): align noExcessiveLinesPerFile behavior
ohnoah ec08ce3
chore(biome_js_analyze): add issue link for noExcessiveLinesPerFile
ohnoah f8d582b
test(biome_js_analyze): add suppression coverage for noExcessiveLines…
ohnoah 83043eb
chore(changeset): narrow to @biomejs/biome
ohnoah e2acb9c
docs(changeset): tweak rule summary
ohnoah 980d16f
docs(biome_js_analyze): fix suppression example
ohnoah d2b4d25
chore(biome_js_analyze): align rule docs and state
ohnoah 89f4a03
Merge branch 'main' into feat/no-excessive-lines-per-file
ohnoah ba74948
test(biome_js_analyze): add edge case line count tests
ohnoah dab35ce
[autofix.ci] apply automated fixes
autofix-ci[bot] 8b6eae4
Merge branch 'main' into feat/no-excessive-lines-per-file
ohnoah 2f16347
test(biome_js_analyze): update whitespace-only snapshot
ohnoah eea3931
Merge branch 'main' into feat/no-excessive-lines-per-file
ohnoah bb360a9
Merge branch 'main' into feat/no-excessive-lines-per-file
ohnoah File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| --- | ||
| "@biomejs/biome": patch | ||
| --- | ||
|
|
||
| Added the nursery lint rule [`noExcessiveLinesPerFile`](https://biomejs.dev/linter/rules/no-excessive-lines-per-file/). | ||
| Biome now reports files that exceed a configurable line limit. | ||
|
|
||
| ```js | ||
| // maxLines: 2 | ||
| const a = 1; | ||
| const b = 2; | ||
| const c = 3; | ||
| ``` |
16 changes: 16 additions & 0 deletions
16
crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
173 changes: 173 additions & 0 deletions
173
crates/biome_js_analyze/src/lint/nursery/no_excessive_lines_per_file.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| use biome_analyze::{ | ||
| Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule, | ||
| }; | ||
| use biome_console::markup; | ||
| use biome_js_syntax::{AnyJsRoot, JsSyntaxKind}; | ||
| use biome_rowan::AstNode; | ||
| use biome_rule_options::no_excessive_lines_per_file::NoExcessiveLinesPerFileOptions; | ||
|
|
||
| declare_lint_rule! { | ||
| /// Restrict the number of lines in a file. | ||
| /// | ||
| /// This rule checks the number of lines in a file and reports a diagnostic if it exceeds a specified limit. | ||
| /// Some people consider large files a code smell. Large files tend to do many things and can make it hard to follow what's going on. | ||
| /// Many coding style guides dictate a limit of the number of lines that a file can comprise of. This rule can help enforce that style. | ||
| /// | ||
| /// ## Examples | ||
| /// | ||
| /// ### Invalid | ||
| /// | ||
| /// The following example will show a diagnostic when `maxLines` is set to 2: | ||
| /// | ||
| /// ```json,options | ||
| /// { | ||
| /// "options": { | ||
| /// "maxLines": 2 | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// ```js,expect_diagnostic,use_options | ||
| /// const a = 1; | ||
| /// const b = 2; | ||
| /// const c = 3; | ||
| /// ``` | ||
| /// | ||
| /// ### Valid | ||
| /// | ||
| /// ```js | ||
| /// const a = 1; | ||
| /// const b = 2; | ||
| /// ``` | ||
| /// | ||
| /// ## Options | ||
| /// | ||
| /// The following options are available: | ||
| /// | ||
| /// ### `maxLines` | ||
| /// | ||
| /// This option sets the maximum number of lines allowed in a file. | ||
| /// If the file exceeds this limit, a diagnostic will be reported. | ||
| /// | ||
| /// Default: `300` | ||
| /// | ||
| /// When `maxLines: 2`, the following file will be considered invalid: | ||
| /// ```json,options | ||
| /// { | ||
| /// "options": { | ||
| /// "maxLines": 2 | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// ```js,expect_diagnostic,use_options | ||
| /// const a = 1; | ||
| /// const b = 2; | ||
| /// const c = 3; | ||
| /// ``` | ||
| /// | ||
| /// ### `skipBlankLines` | ||
| /// | ||
| /// When this option is set to `true`, blank lines are not counted towards the maximum line limit. | ||
| /// This means that only lines with actual code or comments will be counted. | ||
| /// | ||
| /// Default: `false` | ||
| /// | ||
| /// When `maxLines: 3` and `skipBlankLines: true`, the following file will be considered valid | ||
| /// even though it has 5 total lines, because only 3 lines contain code: | ||
| /// ```json,options | ||
| /// { | ||
| /// "options": { | ||
| /// "maxLines": 3, | ||
| /// "skipBlankLines": true | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// ```js,use_options | ||
| /// const a = 1; | ||
| /// | ||
| /// const b = 2; | ||
| /// | ||
| /// const c = 3; | ||
| /// ``` | ||
| /// | ||
| /// ## Suppressions | ||
| /// | ||
| /// If you need to exceed the line limit in a specific file, you can suppress this rule | ||
| /// at the top of the file: | ||
| /// | ||
| /// ```json,options | ||
| /// { | ||
| /// "options": { | ||
| /// "maxLines": 2 | ||
| /// } | ||
| /// } | ||
| /// ``` | ||
| /// ```js,use_options | ||
| /// // biome-ignore lint/nursery/noExcessiveLinesPerFile: generated file | ||
| /// const a = 1; | ||
| /// const b = 2; | ||
| /// const c = 3; | ||
| /// ``` | ||
| /// | ||
| pub NoExcessiveLinesPerFile { | ||
| version: "next", | ||
| name: "noExcessiveLinesPerFile", | ||
| language: "js", | ||
| recommended: false, | ||
| sources: &[RuleSource::Eslint("max-lines").inspired()], | ||
| } | ||
| } | ||
|
|
||
| impl Rule for NoExcessiveLinesPerFile { | ||
| type Query = Ast<AnyJsRoot>; | ||
| type State = usize; | ||
| type Signals = Option<Self::State>; | ||
| type Options = NoExcessiveLinesPerFileOptions; | ||
|
|
||
| fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
| let node = ctx.query(); | ||
| let options = ctx.options(); | ||
|
|
||
| let file_lines_count = node | ||
| .syntax() | ||
| .descendants() | ||
| .flat_map(|descendant| descendant.tokens().collect::<Vec<_>>()) | ||
| .filter(|token| token.kind() != JsSyntaxKind::EOF) | ||
| .fold(0, |acc, token| { | ||
| if options.skip_blank_lines() { | ||
| return acc + token.has_leading_newline() as usize; | ||
| }; | ||
dyc3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| acc + token | ||
| .trim_trailing_trivia() | ||
| .leading_trivia() | ||
| .pieces() | ||
| .filter(|piece| piece.is_newline()) | ||
| .count() | ||
| }) | ||
dyc3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| + 1; // Add 1 for the first line | ||
dyc3 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if file_lines_count > options.max_lines().get().into() { | ||
| return Some(file_lines_count); | ||
| } | ||
|
|
||
| None | ||
| } | ||
ohnoah marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| fn diagnostic(ctx: &RuleContext<Self>, state: &Self::State) -> Option<RuleDiagnostic> { | ||
| let node = ctx.query(); | ||
| let options = ctx.options(); | ||
|
|
||
| Some( | ||
| RuleDiagnostic::new( | ||
| rule_category!(), | ||
| node.range(), | ||
| markup! { | ||
| "This file has too many lines ("{state}"). Maximum allowed is "{options.max_lines().to_string()}"." | ||
| }, | ||
| ) | ||
| .note(markup! { | ||
| "Consider splitting this file into smaller files." | ||
| }), | ||
| ) | ||
| } | ||
| } | ||
Empty file.
8 changes: 8 additions & 0 deletions
8
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/empty.js.snap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| source: crates/biome_js_analyze/tests/spec_tests.rs | ||
| expression: empty.js | ||
| --- | ||
| # Input | ||
| ```js | ||
|
|
||
| ``` |
15 changes: 15 additions & 0 deletions
15
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/empty.options.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", | ||
| "linter": { | ||
| "rules": { | ||
| "nursery": { | ||
| "noExcessiveLinesPerFile": { | ||
| "level": "error", | ||
| "options": { | ||
| "maxLines": 1 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
4 changes: 4 additions & 0 deletions
4
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/invalid.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| const a = 1; | ||
| const b = 2; | ||
| const c = 3; | ||
| const d = 4; |
33 changes: 33 additions & 0 deletions
33
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/invalid.js.snap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| --- | ||
| source: crates/biome_js_analyze/tests/spec_tests.rs | ||
| expression: invalid.js | ||
| --- | ||
| # Input | ||
| ```js | ||
| const a = 1; | ||
| const b = 2; | ||
| const c = 3; | ||
| const d = 4; | ||
|
|
||
| ``` | ||
|
|
||
| # Diagnostics | ||
| ``` | ||
| invalid.js:1:1 lint/nursery/noExcessiveLinesPerFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
|
||
| i This file has too many lines (4). Maximum allowed is 2. | ||
|
|
||
| > 1 │ const a = 1; | ||
| │ ^^^^^^^^^^^^ | ||
| > 2 │ const b = 2; | ||
| > 3 │ const c = 3; | ||
| > 4 │ const d = 4; | ||
| │ ^^^^^^^^^^^^ | ||
| 5 │ | ||
|
|
||
| i Consider splitting this file into smaller files. | ||
|
|
||
| i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information. | ||
|
|
||
|
|
||
| ``` |
15 changes: 15 additions & 0 deletions
15
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/invalid.options.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", | ||
| "linter": { | ||
| "rules": { | ||
| "nursery": { | ||
| "noExcessiveLinesPerFile": { | ||
| "level": "error", | ||
| "options": { | ||
| "maxLines": 2 | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
5 changes: 5 additions & 0 deletions
5
crates/biome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/invalidSkipBlankLines.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| const a = 1; | ||
|
|
||
| const b = 2; | ||
| const c = 3; | ||
|
|
34 changes: 34 additions & 0 deletions
34
...iome_js_analyze/tests/specs/nursery/noExcessiveLinesPerFile/invalidSkipBlankLines.js.snap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| --- | ||
| source: crates/biome_js_analyze/tests/spec_tests.rs | ||
| expression: invalidSkipBlankLines.js | ||
| --- | ||
| # Input | ||
| ```js | ||
| const a = 1; | ||
|
|
||
| const b = 2; | ||
| const c = 3; | ||
|
|
||
|
|
||
| ``` | ||
|
|
||
| # Diagnostics | ||
| ``` | ||
| invalidSkipBlankLines.js:1:1 lint/nursery/noExcessiveLinesPerFile ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
|
|
||
| i This file has too many lines (3). Maximum allowed is 2. | ||
|
|
||
| > 1 │ const a = 1; | ||
| │ ^^^^^^^^^^^^ | ||
| > 2 │ | ||
| > 3 │ const b = 2; | ||
| > 4 │ const c = 3; | ||
| │ ^^^^^^^^^^^^ | ||
| 5 │ | ||
|
|
||
| i Consider splitting this file into smaller files. | ||
|
|
||
| i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information. | ||
|
|
||
|
|
||
| ``` |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.