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): inherit rules via the extended config files #9308

Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions apps/oxlint/fixtures/extends_config/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log("test");
1 change: 1 addition & 0 deletions apps/oxlint/fixtures/extends_config/empty_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["./invalid_config.json"]
}
3 changes: 3 additions & 0 deletions apps/oxlint/fixtures/extends_config/extends_rules_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["./rules_config.json"]
}
3 changes: 3 additions & 0 deletions apps/oxlint/fixtures/extends_config/invalid_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
invalid
}
7 changes: 7 additions & 0 deletions apps/oxlint/fixtures/extends_config/rules_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"rules": {
"no-debugger": "off",
"no-console": "error",
"unicorn/no-null": "error"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"rules": {
"no-console": "off",
"no-var": "off",
"oxc/approx-constant": "off",
"unicorn/no-null": "off",
"unicorn/no-array-for-each": "off"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"rules": {
"no-console": "error",
"no-var": "error",
"oxc/approx-constant": "error",
"unicorn/no-null": "error",
"unicorn/no-array-for-each": "error"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"rules": {
"no-console": "warn",
"no-var": "warn",
"oxc/approx-constant": "warn",
"unicorn/no-null": "warn",
"unicorn/no-array-for-each": "warn"
}
}
50 changes: 45 additions & 5 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{

use cow_utils::CowUtils;
use ignore::{gitignore::Gitignore, overrides::OverrideBuilder};
use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler};
use oxc_diagnostics::{DiagnosticService, GraphicalReportHandler, OxcDiagnostic};
use oxc_linter::{
AllowWarnDeny, ConfigStore, ConfigStoreBuilder, InvalidFilterKind, LintFilter, LintOptions,
LintService, LintServiceOptions, Linter, Oxlintrc, loader::LINT_PARTIAL_LOADER_EXT,
Expand Down Expand Up @@ -200,9 +200,28 @@ impl Runner for LintRunner {

// iterate over each config and build the ConfigStore
for (dir, oxlintrc) in nested_oxlintrc {
// TODO(refactor): clean up all of the error handling in this function
let builder = match ConfigStoreBuilder::from_oxlintrc(false, oxlintrc) {
Ok(builder) => builder,
Err(e) => {
let handler = GraphicalReportHandler::new();
let mut err = String::new();
handler
.render_report(&mut err, &OxcDiagnostic::error(e.to_string()))
.unwrap();
stdout
.write_all(
format!("Failed to parse configuration file.\n{err}\n").as_bytes(),
)
.or_else(Self::check_for_writer_error)
.unwrap();
stdout.flush().unwrap();

return CliRunResult::InvalidOptionConfig;
}
}
// TODO(perf): figure out if we can avoid cloning `filter`
let builder =
ConfigStoreBuilder::from_oxlintrc(false, oxlintrc).with_filters(filter.clone());
.with_filters(filter.clone());
match builder.build() {
Ok(config) => nested_configs.insert(dir.to_path_buf(), config),
Err(diagnostic) => {
Expand Down Expand Up @@ -230,8 +249,22 @@ impl Runner for LintRunner {
} else {
None
};
let config_builder =
ConfigStoreBuilder::from_oxlintrc(false, oxlintrc).with_filters(filter);
let config_builder = match ConfigStoreBuilder::from_oxlintrc(false, oxlintrc) {
Ok(builder) => builder,
Err(e) => {
let handler = GraphicalReportHandler::new();
let mut err = String::new();
handler.render_report(&mut err, &OxcDiagnostic::error(e.to_string())).unwrap();
stdout
.write_all(format!("Failed to parse configuration file.\n{err}\n").as_bytes())
.or_else(Self::check_for_writer_error)
.unwrap();
stdout.flush().unwrap();

return CliRunResult::InvalidOptionConfig;
}
}
.with_filters(filter);

if let Some(basic_config_file) = oxlintrc_for_print {
let config_file = config_builder.resolve_final_config_file(basic_config_file);
Expand Down Expand Up @@ -982,4 +1015,11 @@ mod test {
];
Tester::new().with_cwd("fixtures/nested_config".into()).test_and_snapshot(args);
}

#[test]
fn test_extends_explicit_config() {
// Check that referencing a config file that extends other config files works as expected
let args = &["--config", "extends_rules_config.json", "console.js"];
Tester::new().with_cwd("fixtures/extends_config".into()).test_and_snapshot(args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments: --config extends_rules_config.json console.js
working directory: fixtures/extends_config
----------

x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-console.html\eslint(no-console)]8;;\: eslint(no-console): Unexpected console statement.
,-[console.js:1:1]
1 | console.log("test");
: ^^^^^^^^^^^
`----
help: Delete this console statement.

Found 0 warnings and 1 error.
Finished in <variable>ms on 1 file with 101 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
1 change: 1 addition & 0 deletions crates/oxc_language_server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ impl Backend {
let config = Oxlintrc::from_file(&config_path)
.expect("should have initialized linter with new options");
let config_store = ConfigStoreBuilder::from_oxlintrc(true, config.clone())
.expect("failed to build config")
.build()
.expect("failed to build config");
*linter = ServerLinter::new_with_linter(
Expand Down
Loading
Loading