From 7350a99461046767afcc02ad8f8832753a515a81 Mon Sep 17 00:00:00 2001 From: Subhav Gautam <9058689+zubhav@users.noreply.github.com.> Date: Wed, 2 Apr 2025 10:08:42 +0400 Subject: [PATCH 1/7] Add tempfile dependency and fix plugin inheritance bug --- Cargo.lock | 1 + crates/oxc_linter/Cargo.toml | 1 + .../oxc_linter/src/config/config_builder.rs | 74 ++++++++++++++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 598cbdf8af8fc..f3bbf987b8f06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1886,6 +1886,7 @@ dependencies = [ "serde_json", "simdutf8", "smallvec", + "tempfile", ] [[package]] diff --git a/crates/oxc_linter/Cargo.toml b/crates/oxc_linter/Cargo.toml index b8220f55e38f9..d3b5c9334cfb8 100644 --- a/crates/oxc_linter/Cargo.toml +++ b/crates/oxc_linter/Cargo.toml @@ -71,3 +71,4 @@ smallvec = { workspace = true } insta = { workspace = true } markdown = { workspace = true } project-root = { workspace = true } +tempfile = "3.8.1" diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 40ad66fabb2a8..74a0b65af8286 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -147,9 +147,16 @@ impl ConfigStoreBuilder { // Use `ConfigStoreBuilder` to load extended config files and then apply rules from those let mut extended_config_store = ConfigStoreBuilder::from_oxlintrc(true, extended_config)?; + + // First apply the plugins from the extended config + // This needs to happen before adding the rules to ensure + // that plugins required by the rules are available + builder = builder.and_plugins(extended_config_store.plugins(), true); + + // Now get the rules from the extended config let rules = std::mem::take(&mut extended_config_store.rules); builder = builder.with_rules(rules); - builder = builder.and_plugins(extended_config_store.plugins(), true); + if !extended_config_store.overrides.is_empty() { let overrides = std::mem::take(&mut extended_config_store.overrides); @@ -301,7 +308,9 @@ impl ConfigStoreBuilder { // to be taken out. let plugins = self.plugins(); let mut rules = if self.cache.is_stale() { - self.rules.into_iter().filter(|r| plugins.contains(r.plugin_name().into())).collect() + // Restore the rule filtering based on enabled plugins + // This ensures rules are only included if their plugin is enabled + self.rules.into_iter().filter(|r| plugins.contains(r.rule.plugin_name().into())).collect() } else { self.rules.into_iter().collect::>() }; @@ -979,6 +988,67 @@ mod test { assert!(config.rules().is_empty()); } + #[test] + fn test_extends_config_inherits_plugins() { + // This test specifically validates the fix for issue #10173 where + // extended configs do not inherit plugins + + let temp_dir = tempfile::tempdir().unwrap(); + + let base_config_path = temp_dir.path().join("base.json"); + let base_config_content = r#"{ + "plugins": ["vitest"] + }"#; + std::fs::write(&base_config_path, base_config_content).unwrap(); + + let extends_config_path = temp_dir.path().join("extends.json"); + let extends_config_content = format!( + r#"{{ + "extends": ["{}"] + }}"#, + base_config_path.to_str().unwrap().replace('\\', "/") + ); + std::fs::write(&extends_config_path, extends_config_content).unwrap(); + + let base_oxlintrc = Oxlintrc::from_file(&base_config_path).unwrap(); + let base_config_store = ConfigStoreBuilder::from_oxlintrc(true, base_oxlintrc).unwrap().build().unwrap(); + + let extends_oxlintrc = Oxlintrc::from_file(&extends_config_path).unwrap(); + let extends_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends_oxlintrc).unwrap().build().unwrap(); + + assert!(base_config_store.plugins().contains(LintPlugins::VITEST), + "Base config should have vitest plugin"); + + // Verify the extending config inherits the vitest plugin + // even though it doesn't explicitly specify it + assert!(extends_config_store.plugins().contains(LintPlugins::VITEST), + "Extended config should inherit the vitest plugin from base config"); + + // Verify that extending a config with empty plugins array still inherits plugins + // Matches ESLint behavior + let base2_config_path = temp_dir.path().join("base2.json"); + let base2_config_content = r#"{ + "plugins": ["vitest"] + }"#; + std::fs::write(&base2_config_path, base2_config_content).unwrap(); + + let extends2_config_path = temp_dir.path().join("extends2.json"); + let extends2_config_content = format!( + r#"{{ + "extends": ["{}"], + "plugins": [] + }}"#, + base2_config_path.to_str().unwrap().replace('\\', "/") + ); + std::fs::write(&extends2_config_path, extends2_config_content).unwrap(); + + let extends2_oxlintrc = Oxlintrc::from_file(&extends2_config_path).unwrap(); + let extends2_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends2_oxlintrc).unwrap().build().unwrap(); + + assert!(extends2_config_store.plugins().contains(LintPlugins::VITEST), + "Extended config with empty plugins array should still inherit plugins"); + } + fn config_store_from_path(path: &str) -> ConfigStore { ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::from_file(&PathBuf::from(path)).unwrap()) .unwrap() From a2d6af167a5459c1dd18657121b5daefb1991638 Mon Sep 17 00:00:00 2001 From: Subhav Gautam <9058689+zubhav@users.noreply.github.com.> Date: Wed, 2 Apr 2025 10:31:34 +0400 Subject: [PATCH 2/7] Refactor plugin filtering logic for stale cache --- .../oxc_linter/src/config/config_builder.rs | 162 +++++++++++------- 1 file changed, 98 insertions(+), 64 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 74a0b65af8286..70278720c036d 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -308,9 +308,9 @@ impl ConfigStoreBuilder { // to be taken out. let plugins = self.plugins(); let mut rules = if self.cache.is_stale() { - // Restore the rule filtering based on enabled plugins - // This ensures rules are only included if their plugin is enabled - self.rules.into_iter().filter(|r| plugins.contains(r.rule.plugin_name().into())).collect() + self.rules.into_iter().filter(|r| { + plugins.contains(r.plugin_name().into()) + }).collect() } else { self.rules.into_iter().collect::>() }; @@ -898,6 +898,101 @@ mod test { ); } + #[test] + fn test_extends_config_inherits_plugins() { + // This test specifically validates the fix for issue #10173 where + // extended configs do not inherit plugins + + let temp_dir = tempfile::tempdir().unwrap(); + + // Base config with import plugin and an import rule enabled + let base_config_path = temp_dir.path().join("base.json"); + let base_config_content = r#"{ + "plugins": ["import"], + "rules": { + "import/no-duplicates": "error" + } + }"#; + std::fs::write(&base_config_path, base_config_content).unwrap(); + + // Extending config that doesn't specify any plugins + let extends_config_path = temp_dir.path().join("extends.json"); + let extends_config_content = format!( + r#"{{ + "extends": ["{}"] + }}"#, + base_config_path.to_str().unwrap().replace('\\', "/") + ); + std::fs::write(&extends_config_path, extends_config_content).unwrap(); + + // Load and build both configs + let base_oxlintrc = Oxlintrc::from_file(&base_config_path).unwrap(); + let base_config_store = ConfigStoreBuilder::from_oxlintrc(true, base_oxlintrc).unwrap().build().unwrap(); + + let extends_oxlintrc = Oxlintrc::from_file(&extends_config_path).unwrap(); + let extends_config_builder = ConfigStoreBuilder::from_oxlintrc(true, extends_oxlintrc).unwrap(); + + // Check if the cache is stale before building + println!("Cache stale status: {}", extends_config_builder.cache.is_stale()); + + let extends_config_store = extends_config_builder.build().unwrap(); + + // Check that base config has import plugin + assert!(base_config_store.plugins().contains(LintPlugins::IMPORT), + "Base config should have import plugin"); + + // Verify the extending config inherits the import plugin + // even though it doesn't explicitly specify it + assert!(extends_config_store.plugins().contains(LintPlugins::IMPORT), + "Extended config should inherit the import plugin from base config"); + + // The key test: Verify that the import rule is preserved in the extending config + let import_rule_name = "no-duplicates"; + let has_import_rule = extends_config_store + .rules() + .iter() + .any(|r| r.plugin_name() == "import" && r.name() == import_rule_name); + + assert!(has_import_rule, + "Extended config should have the import/no-duplicates rule inherited from base config"); + + // Verify that extending a config with empty plugins array still inherits plugins + // Matches ESLint behavior + let base2_config_path = temp_dir.path().join("base2.json"); + let base2_config_content = r#"{ + "plugins": ["import"], + "rules": { + "import/no-duplicates": "error" + } + }"#; + std::fs::write(&base2_config_path, base2_config_content).unwrap(); + + let extends2_config_path = temp_dir.path().join("extends2.json"); + let extends2_config_content = format!( + r#"{{ + "extends": ["{}"], + "plugins": [] + }}"#, + base2_config_path.to_str().unwrap().replace('\\', "/") + ); + std::fs::write(&extends2_config_path, extends2_config_content).unwrap(); + + let extends2_oxlintrc = Oxlintrc::from_file(&extends2_config_path).unwrap(); + let extends2_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends2_oxlintrc).unwrap().build().unwrap(); + + assert!(extends2_config_store.plugins().contains(LintPlugins::IMPORT), + "Extended config with empty plugins array should still inherit plugins"); + + // Also check that rules are preserved when plugins array is empty + let has_import_rule2 = extends2_config_store + .rules() + .iter() + .any(|r| r.plugin_name() == "import" && r.name() == import_rule_name); + + assert!(has_import_rule2, + "Extended config with empty plugins array should still inherit import rules"); + } + #[test] fn test_extends_invalid() { let invalid_config = ConfigStoreBuilder::from_oxlintrc( @@ -988,67 +1083,6 @@ mod test { assert!(config.rules().is_empty()); } - #[test] - fn test_extends_config_inherits_plugins() { - // This test specifically validates the fix for issue #10173 where - // extended configs do not inherit plugins - - let temp_dir = tempfile::tempdir().unwrap(); - - let base_config_path = temp_dir.path().join("base.json"); - let base_config_content = r#"{ - "plugins": ["vitest"] - }"#; - std::fs::write(&base_config_path, base_config_content).unwrap(); - - let extends_config_path = temp_dir.path().join("extends.json"); - let extends_config_content = format!( - r#"{{ - "extends": ["{}"] - }}"#, - base_config_path.to_str().unwrap().replace('\\', "/") - ); - std::fs::write(&extends_config_path, extends_config_content).unwrap(); - - let base_oxlintrc = Oxlintrc::from_file(&base_config_path).unwrap(); - let base_config_store = ConfigStoreBuilder::from_oxlintrc(true, base_oxlintrc).unwrap().build().unwrap(); - - let extends_oxlintrc = Oxlintrc::from_file(&extends_config_path).unwrap(); - let extends_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends_oxlintrc).unwrap().build().unwrap(); - - assert!(base_config_store.plugins().contains(LintPlugins::VITEST), - "Base config should have vitest plugin"); - - // Verify the extending config inherits the vitest plugin - // even though it doesn't explicitly specify it - assert!(extends_config_store.plugins().contains(LintPlugins::VITEST), - "Extended config should inherit the vitest plugin from base config"); - - // Verify that extending a config with empty plugins array still inherits plugins - // Matches ESLint behavior - let base2_config_path = temp_dir.path().join("base2.json"); - let base2_config_content = r#"{ - "plugins": ["vitest"] - }"#; - std::fs::write(&base2_config_path, base2_config_content).unwrap(); - - let extends2_config_path = temp_dir.path().join("extends2.json"); - let extends2_config_content = format!( - r#"{{ - "extends": ["{}"], - "plugins": [] - }}"#, - base2_config_path.to_str().unwrap().replace('\\', "/") - ); - std::fs::write(&extends2_config_path, extends2_config_content).unwrap(); - - let extends2_oxlintrc = Oxlintrc::from_file(&extends2_config_path).unwrap(); - let extends2_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends2_oxlintrc).unwrap().build().unwrap(); - - assert!(extends2_config_store.plugins().contains(LintPlugins::VITEST), - "Extended config with empty plugins array should still inherit plugins"); - } - fn config_store_from_path(path: &str) -> ConfigStore { ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::from_file(&PathBuf::from(path)).unwrap()) .unwrap() From 534fccf16d91b573fbdea6b392b59dd4852ecca7 Mon Sep 17 00:00:00 2001 From: Subhav Gautam <9058689+zubhav@users.noreply.github.com.> Date: Wed, 2 Apr 2025 10:34:28 +0400 Subject: [PATCH 3/7] Refactor filtering rules by plugin name in config_builder --- crates/oxc_linter/src/config/config_builder.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 70278720c036d..87eb0b8f72615 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -308,9 +308,7 @@ impl ConfigStoreBuilder { // to be taken out. let plugins = self.plugins(); let mut rules = if self.cache.is_stale() { - self.rules.into_iter().filter(|r| { - plugins.contains(r.plugin_name().into()) - }).collect() + self.rules.into_iter().filter(|r| plugins.contains(r.plugin_name().into())).collect() } else { self.rules.into_iter().collect::>() }; From e28301073758348d82e280bdfe3548abc6226ca3 Mon Sep 17 00:00:00 2001 From: Subhav Gautam <9058689+zubhav@users.noreply.github.com.> Date: Fri, 4 Apr 2025 12:54:40 +0400 Subject: [PATCH 4/7] Handle plugin inheritance in config builder --- .../oxc_linter/src/config/config_builder.rs | 266 +++++++++--------- 1 file changed, 138 insertions(+), 128 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 87eb0b8f72615..51f5f10a99885 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -147,16 +147,21 @@ impl ConfigStoreBuilder { // Use `ConfigStoreBuilder` to load extended config files and then apply rules from those let mut extended_config_store = ConfigStoreBuilder::from_oxlintrc(true, extended_config)?; - - // First apply the plugins from the extended config - // This needs to happen before adding the rules to ensure - // that plugins required by the rules are available - builder = builder.and_plugins(extended_config_store.plugins(), true); - - // Now get the rules from the extended config let rules = std::mem::take(&mut extended_config_store.rules); builder = builder.with_rules(rules); + // Handle plugin inheritance + let parent_plugins = extended_config_store.plugins(); + let child_plugins = builder.plugins(); + + if child_plugins == LintPlugins::default() { + // If child has default plugins, inherit from parent + builder = builder.with_plugins(parent_plugins); + } else if child_plugins != LintPlugins::empty() { + // If child specifies plugins, combine with parent's plugins + builder = builder.with_plugins(child_plugins.union(parent_plugins)); + } + if !extended_config_store.overrides.is_empty() { let overrides = std::mem::take(&mut extended_config_store.overrides); @@ -896,101 +901,6 @@ mod test { ); } - #[test] - fn test_extends_config_inherits_plugins() { - // This test specifically validates the fix for issue #10173 where - // extended configs do not inherit plugins - - let temp_dir = tempfile::tempdir().unwrap(); - - // Base config with import plugin and an import rule enabled - let base_config_path = temp_dir.path().join("base.json"); - let base_config_content = r#"{ - "plugins": ["import"], - "rules": { - "import/no-duplicates": "error" - } - }"#; - std::fs::write(&base_config_path, base_config_content).unwrap(); - - // Extending config that doesn't specify any plugins - let extends_config_path = temp_dir.path().join("extends.json"); - let extends_config_content = format!( - r#"{{ - "extends": ["{}"] - }}"#, - base_config_path.to_str().unwrap().replace('\\', "/") - ); - std::fs::write(&extends_config_path, extends_config_content).unwrap(); - - // Load and build both configs - let base_oxlintrc = Oxlintrc::from_file(&base_config_path).unwrap(); - let base_config_store = ConfigStoreBuilder::from_oxlintrc(true, base_oxlintrc).unwrap().build().unwrap(); - - let extends_oxlintrc = Oxlintrc::from_file(&extends_config_path).unwrap(); - let extends_config_builder = ConfigStoreBuilder::from_oxlintrc(true, extends_oxlintrc).unwrap(); - - // Check if the cache is stale before building - println!("Cache stale status: {}", extends_config_builder.cache.is_stale()); - - let extends_config_store = extends_config_builder.build().unwrap(); - - // Check that base config has import plugin - assert!(base_config_store.plugins().contains(LintPlugins::IMPORT), - "Base config should have import plugin"); - - // Verify the extending config inherits the import plugin - // even though it doesn't explicitly specify it - assert!(extends_config_store.plugins().contains(LintPlugins::IMPORT), - "Extended config should inherit the import plugin from base config"); - - // The key test: Verify that the import rule is preserved in the extending config - let import_rule_name = "no-duplicates"; - let has_import_rule = extends_config_store - .rules() - .iter() - .any(|r| r.plugin_name() == "import" && r.name() == import_rule_name); - - assert!(has_import_rule, - "Extended config should have the import/no-duplicates rule inherited from base config"); - - // Verify that extending a config with empty plugins array still inherits plugins - // Matches ESLint behavior - let base2_config_path = temp_dir.path().join("base2.json"); - let base2_config_content = r#"{ - "plugins": ["import"], - "rules": { - "import/no-duplicates": "error" - } - }"#; - std::fs::write(&base2_config_path, base2_config_content).unwrap(); - - let extends2_config_path = temp_dir.path().join("extends2.json"); - let extends2_config_content = format!( - r#"{{ - "extends": ["{}"], - "plugins": [] - }}"#, - base2_config_path.to_str().unwrap().replace('\\', "/") - ); - std::fs::write(&extends2_config_path, extends2_config_content).unwrap(); - - let extends2_oxlintrc = Oxlintrc::from_file(&extends2_config_path).unwrap(); - let extends2_config_store = ConfigStoreBuilder::from_oxlintrc(true, extends2_oxlintrc).unwrap().build().unwrap(); - - assert!(extends2_config_store.plugins().contains(LintPlugins::IMPORT), - "Extended config with empty plugins array should still inherit plugins"); - - // Also check that rules are preserved when plugins array is empty - let has_import_rule2 = extends2_config_store - .rules() - .iter() - .any(|r| r.plugin_name() == "import" && r.name() == import_rule_name); - - assert!(has_import_rule2, - "Extended config with empty plugins array should still inherit import rules"); - } - #[test] fn test_extends_invalid() { let invalid_config = ConfigStoreBuilder::from_oxlintrc( @@ -1010,49 +920,149 @@ mod test { #[test] fn test_extends_plugins() { + use std::fs; + + // Create a temporary directory for test files + let temp_dir = tempfile::tempdir().unwrap(); + let parent_path = temp_dir.path().join("parent.json"); + let child_no_plugins_path = temp_dir.path().join("child_no_plugins.json"); + let child_with_plugins_path = temp_dir.path().join("child_with_plugins.json"); + + // Create parent config file with explicitly specified plugins + fs::write( + &parent_path, + r#" + { + "plugins": ["react", "typescript"] + } + "#, + ).unwrap(); + + // Create child config with no plugins that extends parent + fs::write( + &child_no_plugins_path, + &format!( + r#" + {{ + "extends": ["{}"] + }} + "#, + parent_path.to_str().unwrap() + ), + ).unwrap(); + + // Create child config with plugins that extends parent + fs::write( + &child_with_plugins_path, + &format!( + r#" + {{ + "extends": ["{}"], + "plugins": ["jest"] + }} + "#, + parent_path.to_str().unwrap() + ), + ).unwrap(); + + // Test 1: Default plugins when none are specified + let default_config = config_store_from_str( + r#" + { + "rules": {} + } + "#, + ); + // Check that default plugins are correctly set + assert_eq!( + default_config.plugins(), + LintPlugins::default() + ); + + // Test 2: Parent config with explicitly specified plugins + let parent_config = config_store_from_str( + r#" + { + "plugins": ["react", "typescript"] + } + "#, + ); + assert_eq!( + parent_config.plugins(), + LintPlugins::REACT | LintPlugins::TYPESCRIPT + ); + + // Test 3: Child config that extends parent without specifying plugins + // Should inherit parent's plugins + let child_no_plugins_config = config_store_from_path(child_no_plugins_path.to_str().unwrap()); + assert_eq!( + child_no_plugins_config.plugins(), + LintPlugins::REACT | LintPlugins::TYPESCRIPT + ); + + // Test 4: Child config that extends parent and specifies additional plugins + // Should have parent's plugins plus its own + let child_with_plugins_config = config_store_from_path(child_with_plugins_path.to_str().unwrap()); + assert_eq!( + child_with_plugins_config.plugins(), + LintPlugins::REACT | LintPlugins::TYPESCRIPT | LintPlugins::JEST + ); + + // Test 5: Empty plugins array should result in empty plugins + let empty_plugins_config = config_store_from_str( + r#" + { + "plugins": [] + } + "#, + ); + assert_eq!( + empty_plugins_config.plugins(), + LintPlugins::empty() + ); + + // Test 6: Extending multiple config files with plugins let config = config_store_from_str( r#" - { - "extends": [ - "fixtures/extends_config/plugins/jest.json", - "fixtures/extends_config/plugins/react.json" - ] - } - "#, + { + "extends": [ + "fixtures/extends_config/plugins/jest.json", + "fixtures/extends_config/plugins/react.json" + ] + } + "#, ); - assert!(config.plugins().contains(LintPlugins::default())); assert!(config.plugins().contains(LintPlugins::JEST)); assert!(config.plugins().contains(LintPlugins::REACT)); - // Test adding more plugins + // Test 7: Adding more plugins to extended configs let config = config_store_from_str( r#" - { - "extends": [ - "fixtures/extends_config/plugins/jest.json", - "fixtures/extends_config/plugins/react.json" - ], - "plugins": ["typescript"] - } - "#, + { + "extends": [ + "fixtures/extends_config/plugins/jest.json", + "fixtures/extends_config/plugins/react.json" + ], + "plugins": ["typescript"] + } + "#, ); assert_eq!( config.plugins(), LintPlugins::JEST | LintPlugins::REACT | LintPlugins::TYPESCRIPT ); - // Test that extended a config with a plugin is the same as adding it directly + // Test 8: Extending a config with a plugin is the same as adding it directly let plugin_config = config_store_from_str(r#"{ "plugins": ["jest", "react"] }"#); let extends_plugin_config = config_store_from_str( r#" - { - "extends": [ - "fixtures/extends_config/plugins/jest.json", - "fixtures/extends_config/plugins/react.json" - ], - "plugins": [] - } - "#, + { + "extends": [ + "fixtures/extends_config/plugins/jest.json", + "fixtures/extends_config/plugins/react.json" + ] + } + "#, ); assert_eq!( plugin_config.plugins(), From dc146dfcfcbb05d0306f0718395019defa2f8e72 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 23:12:45 +0000 Subject: [PATCH 5/7] [autofix.ci] apply automated fixes --- .../oxc_linter/src/config/config_builder.rs | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 51f5f10a99885..70700d8f592c6 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -936,7 +936,8 @@ mod test { "plugins": ["react", "typescript"] } "#, - ).unwrap(); + ) + .unwrap(); // Create child config with no plugins that extends parent fs::write( @@ -949,7 +950,8 @@ mod test { "#, parent_path.to_str().unwrap() ), - ).unwrap(); + ) + .unwrap(); // Create child config with plugins that extends parent fs::write( @@ -963,7 +965,8 @@ mod test { "#, parent_path.to_str().unwrap() ), - ).unwrap(); + ) + .unwrap(); // Test 1: Default plugins when none are specified let default_config = config_store_from_str( @@ -974,10 +977,7 @@ mod test { "#, ); // Check that default plugins are correctly set - assert_eq!( - default_config.plugins(), - LintPlugins::default() - ); + assert_eq!(default_config.plugins(), LintPlugins::default()); // Test 2: Parent config with explicitly specified plugins let parent_config = config_store_from_str( @@ -987,22 +987,18 @@ mod test { } "#, ); - assert_eq!( - parent_config.plugins(), - LintPlugins::REACT | LintPlugins::TYPESCRIPT - ); + assert_eq!(parent_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT); // Test 3: Child config that extends parent without specifying plugins // Should inherit parent's plugins - let child_no_plugins_config = config_store_from_path(child_no_plugins_path.to_str().unwrap()); - assert_eq!( - child_no_plugins_config.plugins(), - LintPlugins::REACT | LintPlugins::TYPESCRIPT - ); + let child_no_plugins_config = + config_store_from_path(child_no_plugins_path.to_str().unwrap()); + assert_eq!(child_no_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT); // Test 4: Child config that extends parent and specifies additional plugins // Should have parent's plugins plus its own - let child_with_plugins_config = config_store_from_path(child_with_plugins_path.to_str().unwrap()); + let child_with_plugins_config = + config_store_from_path(child_with_plugins_path.to_str().unwrap()); assert_eq!( child_with_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT | LintPlugins::JEST @@ -1016,10 +1012,7 @@ mod test { } "#, ); - assert_eq!( - empty_plugins_config.plugins(), - LintPlugins::empty() - ); + assert_eq!(empty_plugins_config.plugins(), LintPlugins::empty()); // Test 6: Extending multiple config files with plugins let config = config_store_from_str( From e9ef8b31e871270e27688c48fe24f45adaa6db0e Mon Sep 17 00:00:00 2001 From: Subhav Gautam <9058689+zubhav@users.noreply.github.com.> Date: Mon, 7 Apr 2025 08:59:03 +0400 Subject: [PATCH 6/7] Add new child config files for extending parent plugins --- Cargo.lock | 1 - crates/oxc_linter/Cargo.toml | 1 - .../plugins/child_no_plugins.json | 5 ++ .../plugins/child_with_plugins.json | 8 +++ .../extends_config/plugins/parent.json | 6 +++ .../oxc_linter/src/config/config_builder.rs | 54 +------------------ 6 files changed, 21 insertions(+), 54 deletions(-) create mode 100644 crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json create mode 100644 crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json create mode 100644 crates/oxc_linter/fixtures/extends_config/plugins/parent.json diff --git a/Cargo.lock b/Cargo.lock index f3bbf987b8f06..598cbdf8af8fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1886,7 +1886,6 @@ dependencies = [ "serde_json", "simdutf8", "smallvec", - "tempfile", ] [[package]] diff --git a/crates/oxc_linter/Cargo.toml b/crates/oxc_linter/Cargo.toml index d3b5c9334cfb8..b8220f55e38f9 100644 --- a/crates/oxc_linter/Cargo.toml +++ b/crates/oxc_linter/Cargo.toml @@ -71,4 +71,3 @@ smallvec = { workspace = true } insta = { workspace = true } markdown = { workspace = true } project-root = { workspace = true } -tempfile = "3.8.1" diff --git a/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json b/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json new file mode 100644 index 0000000000000..3c1c4e792386d --- /dev/null +++ b/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "parent.json" + ] +} diff --git a/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json b/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json new file mode 100644 index 0000000000000..74c6f620fed58 --- /dev/null +++ b/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json @@ -0,0 +1,8 @@ +{ + "extends": [ + "parent.json" + ], + "plugins": [ + "jest" + ] +} diff --git a/crates/oxc_linter/fixtures/extends_config/plugins/parent.json b/crates/oxc_linter/fixtures/extends_config/plugins/parent.json new file mode 100644 index 0000000000000..5dda57cedbba2 --- /dev/null +++ b/crates/oxc_linter/fixtures/extends_config/plugins/parent.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "react", + "typescript" + ] +} diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 70700d8f592c6..7605a00558527 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -920,54 +920,6 @@ mod test { #[test] fn test_extends_plugins() { - use std::fs; - - // Create a temporary directory for test files - let temp_dir = tempfile::tempdir().unwrap(); - let parent_path = temp_dir.path().join("parent.json"); - let child_no_plugins_path = temp_dir.path().join("child_no_plugins.json"); - let child_with_plugins_path = temp_dir.path().join("child_with_plugins.json"); - - // Create parent config file with explicitly specified plugins - fs::write( - &parent_path, - r#" - { - "plugins": ["react", "typescript"] - } - "#, - ) - .unwrap(); - - // Create child config with no plugins that extends parent - fs::write( - &child_no_plugins_path, - &format!( - r#" - {{ - "extends": ["{}"] - }} - "#, - parent_path.to_str().unwrap() - ), - ) - .unwrap(); - - // Create child config with plugins that extends parent - fs::write( - &child_with_plugins_path, - &format!( - r#" - {{ - "extends": ["{}"], - "plugins": ["jest"] - }} - "#, - parent_path.to_str().unwrap() - ), - ) - .unwrap(); - // Test 1: Default plugins when none are specified let default_config = config_store_from_str( r#" @@ -991,14 +943,12 @@ mod test { // Test 3: Child config that extends parent without specifying plugins // Should inherit parent's plugins - let child_no_plugins_config = - config_store_from_path(child_no_plugins_path.to_str().unwrap()); + let child_no_plugins_config = config_store_from_path("fixtures/extends_config/plugins/child_no_plugins.json"); assert_eq!(child_no_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT); // Test 4: Child config that extends parent and specifies additional plugins // Should have parent's plugins plus its own - let child_with_plugins_config = - config_store_from_path(child_with_plugins_path.to_str().unwrap()); + let child_with_plugins_config = config_store_from_path("fixtures/extends_config/plugins/child_with_plugins.json"); assert_eq!( child_with_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT | LintPlugins::JEST From 195dd79285db0e879d31f35d43d862c26b34fa8c Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 04:09:21 +0000 Subject: [PATCH 7/7] [autofix.ci] apply automated fixes --- crates/oxc_linter/src/config/config_builder.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 03488e9b667c6..1045840dc24b9 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -943,12 +943,14 @@ mod test { // Test 3: Child config that extends parent without specifying plugins // Should inherit parent's plugins - let child_no_plugins_config = config_store_from_path("fixtures/extends_config/plugins/child_no_plugins.json"); + let child_no_plugins_config = + config_store_from_path("fixtures/extends_config/plugins/child_no_plugins.json"); assert_eq!(child_no_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT); // Test 4: Child config that extends parent and specifies additional plugins // Should have parent's plugins plus its own - let child_with_plugins_config = config_store_from_path("fixtures/extends_config/plugins/child_with_plugins.json"); + let child_with_plugins_config = + config_store_from_path("fixtures/extends_config/plugins/child_with_plugins.json"); assert_eq!( child_with_plugins_config.plugins(), LintPlugins::REACT | LintPlugins::TYPESCRIPT | LintPlugins::JEST