diff --git a/apps/oxlint/fixtures/extends_config/plugins/jest.json b/apps/oxlint/fixtures/extends_config/plugins/jest.json new file mode 100644 index 0000000000000..9f2b224f4a5e0 --- /dev/null +++ b/apps/oxlint/fixtures/extends_config/plugins/jest.json @@ -0,0 +1,3 @@ +{ + "plugins": ["jest"] +} diff --git a/apps/oxlint/fixtures/extends_config/plugins/react.json b/apps/oxlint/fixtures/extends_config/plugins/react.json new file mode 100644 index 0000000000000..0667076440a8b --- /dev/null +++ b/apps/oxlint/fixtures/extends_config/plugins/react.json @@ -0,0 +1,3 @@ +{ + "plugins": ["react"] +} diff --git a/crates/oxc_linter/src/config/config_builder.rs b/crates/oxc_linter/src/config/config_builder.rs index 239101f1d4aaf..c1842be3feff0 100644 --- a/crates/oxc_linter/src/config/config_builder.rs +++ b/crates/oxc_linter/src/config/config_builder.rs @@ -130,9 +130,11 @@ impl ConfigStoreBuilder { .rules .override_rules(&mut builder.rules, &builder.cache.borrow()); // Use `ConfigStoreBuilder` to load extended config files and then apply rules from those - let extended_config_store = + let mut extended_config_store = ConfigStoreBuilder::from_oxlintrc(true, extended_config)?; - builder = builder.with_rules(extended_config_store.rules); + let rules = std::mem::take(&mut extended_config_store.rules); + builder = builder.with_rules(rules); + builder = builder.and_plugins(extended_config_store.plugins(), true); } Err(err) => { return Err(ConfigBuilderError::InvalidConfigFile { @@ -878,6 +880,59 @@ mod test { } } + #[test] + fn test_extends_plugins() { + let config = config_store_from_str( + r#" + { + "extends": [ + "../../apps/oxlint/fixtures/extends_config/plugins/jest.json", + "../../apps/oxlint/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 + let config = config_store_from_str( + r#" + { + "extends": [ + "../../apps/oxlint/fixtures/extends_config/plugins/jest.json", + "../../apps/oxlint/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 + let plugin_config = config_store_from_str(r#"{ "plugins": ["jest", "react"] }"#); + let extends_plugin_config = config_store_from_str( + r#" + { + "extends": [ + "../../apps/oxlint/fixtures/extends_config/plugins/jest.json", + "../../apps/oxlint/fixtures/extends_config/plugins/react.json" + ], + "plugins": [] + } + "#, + ); + assert_eq!( + plugin_config.plugins(), + extends_plugin_config.plugins(), + "Extending a config with a plugin is the same as adding it directly" + ); + } + fn config_store_from_path(path: &str) -> ConfigStore { ConfigStoreBuilder::from_oxlintrc(true, Oxlintrc::from_file(&PathBuf::from(path)).unwrap()) .unwrap() diff --git a/crates/oxc_linter/src/config/config_store.rs b/crates/oxc_linter/src/config/config_store.rs index 7409d14f13702..b789a2968353d 100644 --- a/crates/oxc_linter/src/config/config_store.rs +++ b/crates/oxc_linter/src/config/config_store.rs @@ -55,6 +55,10 @@ impl ConfigStore { &self.base.base.rules } + pub fn plugins(&self) -> LintPlugins { + self.base.base.config.plugins + } + pub(crate) fn resolve(&self, path: &Path) -> ResolvedLinterState { // TODO: based on the `path` provided, resolve the configuration file to use. let resolved_config = &self.base;