From 10cbd3ed9a3f482bc089309d8b2cce4ba51772eb Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Fri, 24 Oct 2025 15:54:21 -0700 Subject: [PATCH 1/2] Fix extended overrides priority --- .../overrides/base_override.json | 10 +++++ .../overrides/current_override.json | 11 ++++++ .../oxc_linter/src/config/config_builder.rs | 38 +++++++++++++++++++ crates/oxc_linter/src/config/oxlintrc.rs | 4 +- 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 crates/oxc_linter/fixtures/extends_config/overrides/base_override.json create mode 100644 crates/oxc_linter/fixtures/extends_config/overrides/current_override.json diff --git a/crates/oxc_linter/fixtures/extends_config/overrides/base_override.json b/crates/oxc_linter/fixtures/extends_config/overrides/base_override.json new file mode 100644 index 0000000000000..675a07478b48e --- /dev/null +++ b/crates/oxc_linter/fixtures/extends_config/overrides/base_override.json @@ -0,0 +1,10 @@ +{ + "overrides": [ + { + "files": ["*.test.ts"], + "rules": { + "no-const-assign": "error" + } + } + ] +} diff --git a/crates/oxc_linter/fixtures/extends_config/overrides/current_override.json b/crates/oxc_linter/fixtures/extends_config/overrides/current_override.json new file mode 100644 index 0000000000000..ff6b55db7b39f --- /dev/null +++ b/crates/oxc_linter/fixtures/extends_config/overrides/current_override.json @@ -0,0 +1,11 @@ +{ + "extends": ["./base_override.json"], + "overrides": [ + { + "files": ["*.test.ts"], + "rules": { + "no-const-assign": "off" + } + } + ] +} diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index b300ee5ef86ac..bfdde8bf3a46c 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -1240,6 +1240,44 @@ mod test { assert!(config.rules().is_empty()); } + #[test] + fn test_extends_overrides_precedence() { + // Test that current config's overrides take priority over extended config's overrides + // This is consistent with how base-level rules work (current overrides extended) + + // Load the oxlintrc that extends a base config + let current_oxlintrc = Oxlintrc::from_file(&PathBuf::from( + "fixtures/extends_config/overrides/current_override.json", + )) + .unwrap(); + + // Build the config with from_oxlintrc which will handle extends + let mut external_plugin_store = ExternalPluginStore::default(); + let builder = ConfigStoreBuilder::from_oxlintrc( + false, // start_empty = false to get default rules + current_oxlintrc, + None, + &mut external_plugin_store, + ) + .unwrap(); + + let config = builder.build(&external_plugin_store).unwrap(); + + // Apply overrides for a foo.test.ts file (matches both overrides) + let resolved = config.apply_overrides(Path::new("foo.test.ts")); + + // The no-const-assign rule should be "off" (disabled, not present in rules) + // because current config's override sets it to "off", which should take priority + // over the extended config's override which sets it to "error" + let no_const_assign_rule = + resolved.rules.iter().find(|(rule, _)| rule.name() == "no-const-assign"); + + assert!( + no_const_assign_rule.is_none(), + "no-const-assign should be disabled (off) by current config's override, not error from extended config" + ); + } + fn config_store_from_path(path: &str) -> Config { let mut external_plugin_store = ExternalPluginStore::default(); ConfigStoreBuilder::from_oxlintrc( diff --git a/crates/oxc_linter/src/config/oxlintrc.rs b/crates/oxc_linter/src/config/oxlintrc.rs index 692d892ce0a0c..decac79ba01b0 100644 --- a/crates/oxc_linter/src/config/oxlintrc.rs +++ b/crates/oxc_linter/src/config/oxlintrc.rs @@ -242,8 +242,8 @@ impl Oxlintrc { let env = self.env.clone(); let globals = self.globals.clone(); - let mut overrides = self.overrides.clone(); - overrides.extend(other.overrides); + let mut overrides = other.overrides.clone(); + overrides.extend(self.overrides.clone()); let plugins = match (self.plugins, other.plugins) { (Some(self_plugins), Some(other_plugins)) => Some(self_plugins | other_plugins), From e2a347f296efcfabedba93fc17c48de4ecbdaa85 Mon Sep 17 00:00:00 2001 From: Peter Wagenet Date: Sat, 25 Oct 2025 21:40:16 -0700 Subject: [PATCH 2/2] Fix lints --- crates/oxc_linter/src/config/config_builder.rs | 2 +- crates/oxc_linter/src/config/oxlintrc.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index bfdde8bf3a46c..ece65cba25884 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -137,7 +137,7 @@ impl ConfigStoreBuilder { let (extends, extends_paths) = resolve_oxlintrc_config(extends_oxlintrc)?; - oxlintrc = oxlintrc.merge(extends); + oxlintrc = oxlintrc.merge(&extends); extended_paths.extend(extends_paths); } diff --git a/crates/oxc_linter/src/config/oxlintrc.rs b/crates/oxc_linter/src/config/oxlintrc.rs index decac79ba01b0..ba6f631a220b9 100644 --- a/crates/oxc_linter/src/config/oxlintrc.rs +++ b/crates/oxc_linter/src/config/oxlintrc.rs @@ -218,7 +218,7 @@ impl Oxlintrc { /// Merges two [Oxlintrc] files together /// [Self] takes priority over `other` #[must_use] - pub fn merge(&self, other: Oxlintrc) -> Oxlintrc { + pub fn merge(&self, other: &Oxlintrc) -> Oxlintrc { let mut categories = other.categories.clone(); categories.extend(self.categories.iter());