Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/oxc_linter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,4 @@ smallvec = { workspace = true }
insta = { workspace = true }
markdown = { workspace = true }
project-root = { workspace = true }
tempfile = "3.8.1"
166 changes: 139 additions & 27 deletions crates/oxc_linter/src/config/config_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,19 @@ impl ConfigStoreBuilder {
ConfigStoreBuilder::from_oxlintrc(true, 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);

// Handle plugin inheritance
let parent_plugins = extended_config_store.plugins();
let child_plugins = builder.plugins();
Comment thread
camchenry marked this conversation as resolved.

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);
Expand Down Expand Up @@ -908,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");
Comment thread
camchenry marked this conversation as resolved.
Outdated

// 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(),
Expand Down