diff --git a/.changeset/tricky-worms-allow.md b/.changeset/tricky-worms-allow.md new file mode 100644 index 000000000000..8c73e856c53f --- /dev/null +++ b/.changeset/tricky-worms-allow.md @@ -0,0 +1,33 @@ +--- +"@biomejs/biome": patch +--- + +Fixed a bug where the combination `files.includes` and `extends` might result in an incorrect list of glob patterns. +As per requirements, the list of `files.includes` must have `**` at the beginning *if there are only negated patterns*. + +The bug was caused by an incorrect merging of the `files.includes` and `extends` fields. When the `extends` field was merged into the `files.includes` field, which could result in `**` not being in at the first place of the list. + +After this fix, if a configuration file coming from `extends` defines `**` in the first place, and the user configuration uses its own `files.includes`, the final list will contain `**` in the first place. + +The following example, the final `files.includes` list will be `["**", "!**/dist", "!components"]` + +**Example** + +```json5 +// shared.json, some shared configuration +{ + "files": { + "includes": ["**", "!**/dist"] + } +} +``` + +```json5 +// biome.json, the user configuration +{ + "files": { + "includes": ["!components"] + } +} +``` +Plus, Biome now removes duplicates diff --git a/AGENTS..md b/AGENTS.md similarity index 100% rename from AGENTS..md rename to AGENTS.md diff --git a/crates/biome_cli/src/commands/init.rs b/crates/biome_cli/src/commands/init.rs index ae1efca4ac28..3b9822f71521 100644 --- a/crates/biome_cli/src/commands/init.rs +++ b/crates/biome_cli/src/commands/init.rs @@ -23,10 +23,13 @@ pub(crate) fn init(session: CliSession, emit_jsonc: bool) -> Result<(), CliDiagn if fs.path_exists(&working_directory.join("dist")) { dist_enabled = true; config.files = Some(FilesConfiguration { - includes: Some(vec![ - "**".parse::().unwrap(), - "!!**/dist".parse::().unwrap(), - ]), + includes: Some( + vec![ + "**".parse::().unwrap(), + "!!**/dist".parse::().unwrap(), + ] + .into(), + ), ignore_unknown: None, max_size: None, experimental_scanner_ignores: None, diff --git a/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs index 4529fa231b42..b73cc597ab7e 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_to_biome.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use super::{eslint_any_rule_to_biome::migrate_eslint_any_rule, eslint_eslint, eslint_typescript}; use biome_configuration::analyzer::SeverityOrGroup; +use biome_configuration::glob_list::GlobList; use biome_configuration::{self as biome_config}; use biome_console::markup; use biome_deserialize::Merge; @@ -625,11 +626,8 @@ fn migrate_eslint_rule( } } -fn to_biome_includes( - files: &[impl AsRef], - ignores: &[impl AsRef], -) -> Vec { - let mut includes: Vec = Vec::new(); +fn to_biome_includes(files: &[impl AsRef], ignores: &[impl AsRef]) -> GlobList { + let mut includes = GlobList::new(); if !files.is_empty() { includes.extend(files.iter().filter_map(|glob| glob.as_ref().parse().ok())); } @@ -687,7 +685,7 @@ mod tests { let linter = biome_config.linter.unwrap(); assert_eq!( linter.includes.unwrap(), - ["*.js".parse().unwrap(), "!*.test.js".parse().unwrap()], + vec!["*.js".parse().unwrap(), "!*.test.js".parse().unwrap()].into(), ); assert!(linter.rules.is_some()); } @@ -736,11 +734,12 @@ mod tests { let linter = biome_config.linter.unwrap(); assert_eq!( linter.includes.unwrap(), - [ + vec![ "**".parse().unwrap(), "!*.test.js".parse().unwrap(), "!*.spec.js".parse().unwrap() ] + .into() ); assert_eq!( linter @@ -759,7 +758,12 @@ mod tests { let override0 = overrides.0.into_iter().next().unwrap(); assert_eq!( override0.includes.unwrap(), - OverrideGlobs::Globs(["*.ts".parse().unwrap()].into_iter().collect()), + OverrideGlobs::Globs(Box::new( + ["*.ts".parse().unwrap()] + .into_iter() + .collect::>() + .into() + )), ); assert_eq!( override0 diff --git a/crates/biome_cli/src/execute/migrate/prettier.rs b/crates/biome_cli/src/execute/migrate/prettier.rs index 066ea58712e5..68ac0eb1ead7 100644 --- a/crates/biome_cli/src/execute/migrate/prettier.rs +++ b/crates/biome_cli/src/execute/migrate/prettier.rs @@ -13,6 +13,7 @@ use biome_formatter::{ AttributePosition, BracketSpacing, Expand, IndentWidth, LineEnding, LineWidth, QuoteStyle, }; use biome_fs::{FileSystem, OpenOptions}; +use biome_glob::NormalizedGlob; use biome_html_formatter::context::SelfCloseVoidElements; use biome_js_formatter::context::{ArrowParentheses, QuoteProperties, Semicolons, TrailingCommas}; use biome_json_parser::JsonParserOptions; @@ -316,12 +317,13 @@ impl TryFrom for biome_configuration::OverridePattern { type Error = Error; fn try_from(Override { files, options }: Override) -> anyhow::Result { let mut result = Self { - includes: Some(biome_configuration::OverrideGlobs::Globs( + includes: Some(biome_configuration::OverrideGlobs::Globs(Box::new( files .into_iter() .filter_map(|glob| glob.parse().ok()) - .collect(), - )), + .collect::>() + .into(), + ))), ..Default::default() }; if options.print_width.is_some() diff --git a/crates/biome_cli/tests/cases/config_extends.rs b/crates/biome_cli/tests/cases/config_extends.rs index 0688047c5d42..10f62c7751fa 100644 --- a/crates/biome_cli/tests/cases/config_extends.rs +++ b/crates/biome_cli/tests/cases/config_extends.rs @@ -550,3 +550,53 @@ fn extends_config_merge_overrides() { result, )); } + +#[test] +fn extends_config_by_moving_double_star_at_the_beginning_from_extend_config() { + let fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let shared = Utf8Path::new("shared.json"); + fs.insert( + shared.into(), + r#"{ + "files": { + "includes": ["**", "!**/dist"] + } + }"#, + ); + + let biome_json = Utf8Path::new("biome.json"); + fs.insert( + biome_json.into(), + r#"{ + "extends": ["shared.json"], + "files": { + "includes": ["!components"] + } + }"#, + ); + + let test_file = Utf8Path::new("components/test.js"); + fs.insert(test_file.into(), "debugger; const a = 0;"); + + let test_file = Utf8Path::new("dist/test.js"); + fs.insert(test_file.into(), "debugger; const a = 0;"); + + let test_file = Utf8Path::new("trigger.js"); + fs.insert(test_file.into(), "debugger; const a = 0;"); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["lint", test_file.as_str()].as_slice()), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_config_by_moving_double_star_at_the_beginning_from_extend_config", + fs, + console, + result, + )); +} diff --git a/crates/biome_cli/tests/snapshots/main_cases_config_extends/extends_config_by_moving_double_star_at_the_beginning_from_extend_config.snap b/crates/biome_cli/tests/snapshots/main_cases_config_extends/extends_config_by_moving_double_star_at_the_beginning_from_extend_config.snap new file mode 100644 index 000000000000..59a577babbee --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_cases_config_extends/extends_config_by_moving_double_star_at_the_beginning_from_extend_config.snap @@ -0,0 +1,94 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: redactor(content) +--- +## `biome.json` + +```json +{ + "extends": ["shared.json"], + "files": { + "includes": ["!components"] + } +} +``` + +## `components/test.js` + +```js +debugger; const a = 0; +``` + +## `dist/test.js` + +```js +debugger; const a = 0; +``` + +## `shared.json` + +```json +{ + "files": { + "includes": ["**", "!**/dist"] + } + } +``` + +## `trigger.js` + +```js +debugger; const a = 0; +``` + +# Termination Message + +```block +lint ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Some errors were emitted while running checks. + + + +``` + +# Emitted Messages + +```block +trigger.js:1:17 lint/correctness/noUnusedVariables FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! This variable a is unused. + + > 1 │ debugger; const a = 0; + │ ^ + + i Unused variables are often the result of an incomplete refactoring, typos, or other sources of bugs. + + i Unsafe fix: If this is intentional, prepend a with an underscore. + + - debugger;·const·a·=·0; + + debugger;·const·_a·=·0; + + +``` + +```block +trigger.js:1:1 lint/suspicious/noDebugger FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × This is an unexpected use of the debugger statement. + + > 1 │ debugger; const a = 0; + │ ^^^^^^^^^ + + i Unsafe fix: Remove debugger statement + + 1 │ debugger;·const·a·=·0; + │ ---------- + +``` + +```block +Checked 1 file in