diff --git a/.changeset/blue-parts-lie.md b/.changeset/blue-parts-lie.md new file mode 100644 index 000000000000..2c0168a2cc13 --- /dev/null +++ b/.changeset/blue-parts-lie.md @@ -0,0 +1,25 @@ +--- +"@biomejs/biome": patch +--- + +Fixed [#8233](https://github.com/biomejs/biome/issues/8233), where Biome CLI in +stdin mode didn't work correctly when handling files in projects with nested +configurations. For example, with the following structure, +`--stdin-file-path=subdirectory/...` would not use the nested configuration in +`subdirectory/biome.json`: + +``` +├── biome.json +└── subdirectory + ├── biome.json + └── lib.js +``` + +```shell +biome format --write --stdin-file-path=subdirectory/lib.js < subdirectory/lib.js +``` + +Now, the nested configuration is correctly picked up and applied. + +In addition, Biome now shows a warning if `--stdin-file-path` is provided but +that path is ignored and therefore not formatted or fixed. diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index e75e0aa01a98..cfb44be0d8e1 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -101,9 +101,7 @@ jobs: - name: Build Biome debug binary run: cargo build --bin biome - name: Run tests - run: | - cd e2e-tests - sh test-all.sh + run: ./e2e-tests/test-all.sh documentation: name: Documentation diff --git a/biome.json b/biome.json index c52df58e969f..6eaae321f1e0 100644 --- a/biome.json +++ b/biome.json @@ -33,6 +33,7 @@ "!**/undefined", "!**/benchmark/target", "!**/benches", + "!!**/e2e-tests", "!!**/target", "!!.cargo" ] diff --git a/crates/biome_cli/src/execute/mod.rs b/crates/biome_cli/src/execute/mod.rs index 16c3c1ca0516..6a3e63a19812 100644 --- a/crates/biome_cli/src/execute/mod.rs +++ b/crates/biome_cli/src/execute/mod.rs @@ -562,7 +562,15 @@ pub fn execute_mode( // don't do any traversal if there's some content coming from stdin if let Some(stdin) = execution.as_stdin_file() { - let biome_path = BiomePath::new(stdin.as_path()); + // Biome path must starts_with(the project directory), which will have been + // joined to workdir. Otherwise we won't find nested settings. + let working_dir = session + .app + .workspace + .fs() + .working_directory() + .unwrap_or_default(); + let biome_path = BiomePath::new(working_dir.join(stdin.as_path())); return std_in::run( session, project_key, diff --git a/crates/biome_cli/src/execute/std_in.rs b/crates/biome_cli/src/execute/std_in.rs index 6972f7750eee..05baa9201371 100644 --- a/crates/biome_cli/src/execute/std_in.rs +++ b/crates/biome_cli/src/execute/std_in.rs @@ -49,6 +49,10 @@ pub(crate) fn run<'a>( if file_features.is_ignored() { console.append(markup! {{content}}); + // Write error last because files may generally be long + console.error(markup! { + "The content was not formatted because the path `"{biome_path.as_str()}"` is ignored." + }); return Ok(()); } @@ -99,6 +103,7 @@ pub(crate) fn run<'a>( console.append(markup! { {content} }); + // Write error last because files may generally be long console.error(markup! { "The content was not formatted because the formatter is currently disabled." }); @@ -130,6 +135,10 @@ pub(crate) fn run<'a>( if file_features.is_ignored() { console.append(markup! {{content}}); + // Write error last because files may generally be long + console.error(markup! { + "The content was not fixed because the path `"{biome_path.as_str()}"` is ignored." + }); return Ok(()); } diff --git a/crates/biome_cli/tests/snapshots/main_commands_check/check_stdin_ignores_unknown_file_path.snap b/crates/biome_cli/tests/snapshots/main_commands_check/check_stdin_ignores_unknown_file_path.snap index 88585ac947b1..c9f8a1645258 100644 --- a/crates/biome_cli/tests/snapshots/main_commands_check/check_stdin_ignores_unknown_file_path.snap +++ b/crates/biome_cli/tests/snapshots/main_commands_check/check_stdin_ignores_unknown_file_path.snap @@ -13,3 +13,7 @@ function f() {var x=1; return{x}} class Foo {} ```block function f() {var x=1; return{x}} class Foo {} ``` + +```block +The content was not fixed because the path `mock.cc` is ignored. +``` diff --git a/crates/biome_cli/tests/snapshots/main_commands_format/format_stdin_does_not_error_with_ignore_unknown_file_extensions.snap b/crates/biome_cli/tests/snapshots/main_commands_format/format_stdin_does_not_error_with_ignore_unknown_file_extensions.snap index f61d4a0f1fb3..e33a80856272 100644 --- a/crates/biome_cli/tests/snapshots/main_commands_format/format_stdin_does_not_error_with_ignore_unknown_file_extensions.snap +++ b/crates/biome_cli/tests/snapshots/main_commands_format/format_stdin_does_not_error_with_ignore_unknown_file_extensions.snap @@ -13,3 +13,7 @@ function f() {return{}} ```block function f() {return{}} ``` + +```block +The content was not formatted because the path `mock.cc` is ignored. +``` diff --git a/e2e-tests/relative-path-ignore-file/biome.json b/e2e-tests/relative-path-ignore-file/biome.json index e84571ba3c6b..1aea52f104c2 100644 --- a/e2e-tests/relative-path-ignore-file/biome.json +++ b/e2e-tests/relative-path-ignore-file/biome.json @@ -1,5 +1,5 @@ { - "root": false, + "root": true, "files": { "includes": ["**", "!file.js"] }, diff --git a/e2e-tests/relative-path/biome.json b/e2e-tests/relative-path/biome.json index a972cb86c08f..5a881e23adc9 100644 --- a/e2e-tests/relative-path/biome.json +++ b/e2e-tests/relative-path/biome.json @@ -1,5 +1,5 @@ { - "root": false, + "root": true, "files": { "includes": ["**"] }, diff --git a/e2e-tests/stdin-nested-config/app.js b/e2e-tests/stdin-nested-config/app.js new file mode 100644 index 000000000000..c6184bf69097 --- /dev/null +++ b/e2e-tests/stdin-nested-config/app.js @@ -0,0 +1,5 @@ +let x = 5; + +function indent() { + return x; +} diff --git a/e2e-tests/stdin-nested-config/app.js.formatted b/e2e-tests/stdin-nested-config/app.js.formatted new file mode 100644 index 000000000000..99f9621d99dc --- /dev/null +++ b/e2e-tests/stdin-nested-config/app.js.formatted @@ -0,0 +1,5 @@ +let x = 5; + +function indent() { + return x; +} diff --git a/e2e-tests/stdin-nested-config/biome.jsonc b/e2e-tests/stdin-nested-config/biome.jsonc new file mode 100644 index 000000000000..42d507a3a185 --- /dev/null +++ b/e2e-tests/stdin-nested-config/biome.jsonc @@ -0,0 +1,16 @@ +{ + "root": true, + "vcs": { + "enabled": false + }, + "files": { + "ignoreUnknown": false, + // Include .js recursively, but not .ts + "includes": ["**/biome.jsonc", "**/*.js"] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 4 + } +} diff --git a/e2e-tests/stdin-nested-config/donotformat.ts b/e2e-tests/stdin-nested-config/donotformat.ts new file mode 100644 index 000000000000..c5b0f8cd8a06 --- /dev/null +++ b/e2e-tests/stdin-nested-config/donotformat.ts @@ -0,0 +1 @@ +let x = 10; diff --git a/e2e-tests/stdin-nested-config/subdirectory/biome.jsonc b/e2e-tests/stdin-nested-config/subdirectory/biome.jsonc new file mode 100644 index 000000000000..c553bc42e339 --- /dev/null +++ b/e2e-tests/stdin-nested-config/subdirectory/biome.jsonc @@ -0,0 +1,12 @@ +{ + "root": false, + "extends": "//", + "files": { + // This should be relative to the location of this config file, so we should not touch root/donotformat.ts + "includes": ["*.ts"] + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2 + } +} diff --git a/e2e-tests/stdin-nested-config/subdirectory/lib.js b/e2e-tests/stdin-nested-config/subdirectory/lib.js new file mode 100644 index 000000000000..44fd558cea34 --- /dev/null +++ b/e2e-tests/stdin-nested-config/subdirectory/lib.js @@ -0,0 +1,5 @@ +let y = 65; + +function indent() { + return y; +} diff --git a/e2e-tests/stdin-nested-config/subdirectory/lib.js.formatted b/e2e-tests/stdin-nested-config/subdirectory/lib.js.formatted new file mode 100644 index 000000000000..97fb2c8ca73b --- /dev/null +++ b/e2e-tests/stdin-nested-config/subdirectory/lib.js.formatted @@ -0,0 +1,5 @@ +let y = 65; + +function indent() { + return y; +} diff --git a/e2e-tests/stdin-nested-config/subdirectory/typed.ts b/e2e-tests/stdin-nested-config/subdirectory/typed.ts new file mode 100644 index 000000000000..ee2ce6d1b806 --- /dev/null +++ b/e2e-tests/stdin-nested-config/subdirectory/typed.ts @@ -0,0 +1,5 @@ +let y = 65; + +function indent() { + return y; +} diff --git a/e2e-tests/stdin-nested-config/subdirectory/typed.ts.formatted b/e2e-tests/stdin-nested-config/subdirectory/typed.ts.formatted new file mode 100644 index 000000000000..97fb2c8ca73b --- /dev/null +++ b/e2e-tests/stdin-nested-config/subdirectory/typed.ts.formatted @@ -0,0 +1,5 @@ +let y = 65; + +function indent() { + return y; +} diff --git a/e2e-tests/stdin-nested-config/test.sh b/e2e-tests/stdin-nested-config/test.sh new file mode 100755 index 000000000000..d6cb1acd4693 --- /dev/null +++ b/e2e-tests/stdin-nested-config/test.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +TEMP=$(mktemp) +trap 'rm -f $TEMP' EXIT + +biome() { + cargo run --bin biome -- "$@" +} + +STATUS=0 +diff_stdin_filepath() { + biome format --stdin-file-path="$1" < "$1" > "$TEMP" + if ! git diff --no-index "$1.formatted" "$TEMP"; then + STATUS=1 + fi +} + +diff_stdin_filepath app.js +diff_stdin_filepath subdirectory/lib.js +diff_stdin_filepath subdirectory/typed.ts + +biome format --stdin-file-path="donotformat.ts" < "donotformat.ts" > "$TEMP" +if ! git diff --no-index "donotformat.ts" "$TEMP"; then + STATUS=1 +fi + +exit $STATUS diff --git a/e2e-tests/test-all.sh b/e2e-tests/test-all.sh index 3e98bf744360..ef462d693b19 100755 --- a/e2e-tests/test-all.sh +++ b/e2e-tests/test-all.sh @@ -1,12 +1,35 @@ -#!/bin/sh +#!/usr/bin/env bash + +# Usage: +# ./test-all.sh +# ./test-all.sh stdin # to filter tests by name set -eu +# Change to the script's directory +cd "$(dirname "$0")" + +# Glob matcher +if [[ -z "${1:-}" ]]; then FILTER="*"; else FILTER="*$1*"; fi + +redecho() { + echo -e "\033[1;31m$1\033[0m" +} + +bail() { + redecho "Error: $1" + exit 1 +} + for x in *; do if test -d "$x"; then + if [[ "$x" != $FILTER ]]; then + echo "Skipping $x" + continue + fi echo "Testing $x..." - cd "$x" - sh test.sh - cd .. + pushd "$x" > /dev/null + bash test.sh || bail "Test failed: $x. To re-run: $0 $x" + popd > /dev/null fi done