Environment information
Details
CLI:
Version: 2.5.0
Color support: true
Platform:
CPU Architecture: x86_64
OS: linux
Environment:
BIOME_DISTRIBUTION: npm
BIOME_LOG_PATH: unset
BIOME_LOG_PREFIX_NAME: unset
BIOME_LOG_LEVEL: unset
BIOME_LOG_KIND: unset
BIOME_CONFIG_PATH: unset
BIOME_THREADS: unset
BIOME_WATCHER_KIND: unset
BIOME_WATCHER_POLLING_INTERVAL: unset
NO_COLOR: unset
TERM: xterm-256color
JS_RUNTIME_VERSION: v26.3.1
JS_RUNTIME_NAME: node
NODE_PACKAGE_MANAGER: npm/11.16.0
Biome Configuration:
Status: Loaded successfully.
Path: biome.json
Formatter enabled: true
Linter enabled: true
Assist enabled: true
VCS enabled: false
HTML full support enabled: unset
Formatter:
Format with errors: unset
Indent style: Space
Indent width: 2
Line ending: unset
Line width: unset
Attribute position: unset
Bracket spacing: unset
Includes: unset
JavaScript Formatter:
Enabled: unset
JSX quote style: unset
Quote properties: unset
Trailing commas: unset
Semicolons: unset
Arrow parentheses: unset
Bracket spacing: unset
Bracket same line: unset
Quote style: unset
Indent style: unset
Indent width: unset
Line ending: unset
Line width: unset
Attribute position: unset
JSON Formatter:
Enabled: unset
Indent style: unset
Indent width: unset
Line ending: unset
Line width: unset
Trailing Commas: unset
Expand lists: unset
CSS Formatter:
Enabled: unset
Indent style: unset
Indent width: unset
Line ending: unset
Line width: unset
Quote style: unset
GraphQL Formatter:
Enabled: unset
Indent style: unset
Indent width: unset
Line ending: unset
Line width: unset
Bracket spacing: unset
Quote style: unset
Workspace:
Open Documents: 0
Environment
- Biome: reproduced on 2.3.6 → 2.5.0 (latest). Last good version: 2.3.5.
- Prettier (for comparison): 3.x
Problem
Since v2.3.6, the formatter collapses the arguments of a table-driven test call test.each([...])("description", (args) => { ... }) onto a single line, the ])("description", (args) => { opening line even when it clearly exceeds the configured lineWidth.
This output no longer matches Prettier. Up to and including v2.3.5, Biome broke the arguments onto separate lines, identical to Prettier's output; since v2.3.6 Biome diverges from Prettier on this pattern.
The change appears to be an unintended side effect of a lint-rule fix (#7287), not a deliberate formatter change (there is no formatter entry in the 2.3.6 changelog).
Reproduction
repro.ts:
test.each([[1, 2]])(
"a description that is long enough to push the hugged opening line beyond the print width",
(a, b) => {
expect(a).toBe(b);
},
);
Run with default options (lineWidth/printWidth = 80):
biome format repro.ts
prettier repro.ts
Expected (Biome ≤ 2.3.5 and Prettier)
No fixes.
Actual (Biome 2.3.6+)
test.each([
[1, 2],
])("a description that is long enough to push the hugged opening line beyond the print width", (a, b) => {
expect(a).toBe(b);
});
The opening line is ~100+ columns wide, well over lineWidth = 80.
Bisect
| version |
result |
| ≤ 2.3.5 |
expanded (matches Prettier) |
| 2.3.6 … 2.5.0 |
single-line / overflows lineWidth |
Only recognized test names trigger it (test.each / it.each / describe.each → collapsed; a non-test callee such as foo.each(...) is unaffected and still expands). This confirms it is gated on test-call detection, not on width.
Root cause
The formatter's "put all arguments on one line" layout for test calls is here:
|
|| (is_test_call? && is_first_arg_string_literal_or_template) |
|| (is_test_call? && is_first_arg_string_literal_or_template)
is_test_call comes from the shared JsCallExpression::is_test_call_expression() in below, which is used by both the linter and the formatter.
|
pub fn is_test_call_expression(&self) -> SyntaxResult<bool> { |
Commit aa55c8d (#7287) extended that shared method to also recognize the .each / .for double-call pattern:
(Some(Ok(AnyJsCallArgument::AnyJsExpression(_))), Some(Ok(second)), third)
- if arguments.args().len() <= 3 && callee.contains_a_test_pattern()? =>
+ if arguments.args().len() <= 3
+ && (callee.contains_a_test_pattern()
+ || matches!(callee, AnyJsExpression::JsCallExpression(call_expression)
+ if call_expression.callee()?.contains_a_test_each_pattern())) =>
The PR targeted the noDuplicateTestHooks lint rule, but because is_test_call_expression() is shared, test.each([...])("desc", cb) is now classified as a test call and takes the single-line layout in call_arguments.rs so overflowing lineWidth. The formatter code itself was not changed in 2.3.6.
Configuration
{
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
}
}
Playground link
https://biomejs.dev/playground/?indentStyle=space&tab=formatter&pane=Diagnostics&code=dABlAHMAdAAuAGUAYQBjAGgAKABbAFsAMQAsACAAMgBdAF0AKQAoAAoAIAAgACIAYQAgAGQAZQBzAGMAcgBpAHAAdABpAG8AbgAgAHQAaABhAHQAIABpAHMAIABsAG8AbgBnACAAZQBuAG8AdQBnAGgAIAB0AG8AIABwAHUAcwBoACAAdABoAGUAIABoAHUAZwBnAGUAZAAgAG8AcABlAG4AaQBuAGcAIABsAGkAbgBlACAAYgBlAHkAbwBuAGQAIAB0AGgAZQAgAHAAcgBpAG4AdAAgAHcAaQBkAHQAaAAiACwACgAgACAAKABhACwAIABiACkAIAA9AD4AIAB7AAoAIAAgACAAIABlAHgAcABlAGMAdAAoAGEAKQAuAHQAbwBCAGUAKABiACkAOwAKACAAIAB9ACwACgApADsACgA%3D
Code of Conduct
Environment information
Details
Environment
Problem
Since v2.3.6, the formatter collapses the arguments of a table-driven test call
test.each([...])("description", (args) => { ... })onto a single line, the])("description", (args) => {opening line even when it clearly exceeds the configuredlineWidth.This output no longer matches Prettier. Up to and including v2.3.5, Biome broke the arguments onto separate lines, identical to Prettier's output; since v2.3.6 Biome diverges from Prettier on this pattern.
The change appears to be an unintended side effect of a lint-rule fix (#7287), not a deliberate formatter change (there is no formatter entry in the 2.3.6 changelog).
Reproduction
repro.ts:Run with default options (
lineWidth/printWidth= 80):Expected (Biome ≤ 2.3.5 and Prettier)
No fixes.
Actual (Biome 2.3.6+)
The opening line is ~100+ columns wide, well over
lineWidth = 80.Bisect
lineWidthOnly recognized test names trigger it (
test.each/it.each/describe.each→ collapsed; a non-test callee such asfoo.each(...)is unaffected and still expands). This confirms it is gated on test-call detection, not on width.Root cause
The formatter's "put all arguments on one line" layout for test calls is here:
biome/crates/biome_js_formatter/src/js/expressions/call_arguments.rs
Line 72 in c0b9832
is_test_callcomes from the sharedJsCallExpression::is_test_call_expression()in below, which is used by both the linter and the formatter.biome/crates/biome_js_syntax/src/expr_ext.rs
Line 2080 in c0b9832
Commit aa55c8d (#7287) extended that shared method to also recognize the
.each/.fordouble-call pattern:The PR targeted the
noDuplicateTestHookslint rule, but becauseis_test_call_expression()is shared,test.each([...])("desc", cb)is now classified as a test call and takes the single-line layout incall_arguments.rsso overflowinglineWidth. The formatter code itself was not changed in 2.3.6.Configuration
{ "formatter": { "enabled": true, "indentStyle": "space", "indentWidth": 2 } }Playground link
https://biomejs.dev/playground/?indentStyle=space&tab=formatter&pane=Diagnostics&code=dABlAHMAdAAuAGUAYQBjAGgAKABbAFsAMQAsACAAMgBdAF0AKQAoAAoAIAAgACIAYQAgAGQAZQBzAGMAcgBpAHAAdABpAG8AbgAgAHQAaABhAHQAIABpAHMAIABsAG8AbgBnACAAZQBuAG8AdQBnAGgAIAB0AG8AIABwAHUAcwBoACAAdABoAGUAIABoAHUAZwBnAGUAZAAgAG8AcABlAG4AaQBuAGcAIABsAGkAbgBlACAAYgBlAHkAbwBuAGQAIAB0AGgAZQAgAHAAcgBpAG4AdAAgAHcAaQBkAHQAaAAiACwACgAgACAAKABhACwAIABiACkAIAA9AD4AIAB7AAoAIAAgACAAIABlAHgAcABlAGMAdAAoAGEAKQAuAHQAbwBCAGUAKABiACkAOwAKACAAIAB9ACwACgApADsACgA%3D
Code of Conduct