Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(linter): allowing plugins to be extended with extends #9473

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(linter): allowing plugins to be extended with extends (#9473)
Adds support for merging the `plugins` arrays in configuration files referenced in `extends` to the top-level `plugins` array of a configuration file. Essentially acts as if you had placed it in the plugins array at the top-level to begin with.
camchenry committed Mar 4, 2025
commit fb7cf10549a6742449f80614ab4d82fb95eca107
3 changes: 3 additions & 0 deletions apps/oxlint/fixtures/extends_config/plugins/jest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["jest"]
}
3 changes: 3 additions & 0 deletions apps/oxlint/fixtures/extends_config/plugins/react.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["react"]
}
59 changes: 57 additions & 2 deletions crates/oxc_linter/src/config/config_builder.rs
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 4 additions & 0 deletions crates/oxc_linter/src/config/config_store.rs
Original file line number Diff line number Diff line change
@@ -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;