Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
24 changes: 22 additions & 2 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ impl CliRunner {
None
};

// Store max_warnings from config before oxlintrc is consumed
let config_max_warnings = oxlintrc.max_warnings;

let config_builder = match ConfigStoreBuilder::from_oxlintrc(
false,
oxlintrc,
Expand Down Expand Up @@ -301,7 +304,7 @@ impl CliRunner {
_ => None,
};
let (mut diagnostic_service, tx_error) =
Self::get_diagnostic_service(&output_formatter, &warning_options, &misc_options);
Self::get_diagnostic_service(&output_formatter, &warning_options, &misc_options, config_max_warnings);

let config_store = ConfigStore::new(lint_config, nested_configs, external_plugin_store);

Expand Down Expand Up @@ -421,13 +424,17 @@ impl CliRunner {
reporter: &OutputFormatter,
warning_options: &WarningOptions,
misc_options: &MiscOptions,
config_max_warnings: Option<usize>,
) -> (DiagnosticService, DiagnosticSender) {
// CLI flag takes precedence over config file
let max_warnings = warning_options.max_warnings.or(config_max_warnings);

let (service, sender) = DiagnosticService::new(reporter.get_diagnostic_reporter());
(
service
.with_quiet(warning_options.quiet)
.with_silent(misc_options.silent)
.with_max_warnings(warning_options.max_warnings),
.with_max_warnings(max_warnings),
sender,
)
}
Expand Down Expand Up @@ -519,6 +526,19 @@ impl CliRunner {

// iterate over each config and build the ConfigStore
for (dir, oxlintrc) in nested_oxlintrc {
// Validate that nested configs don't have maxWarnings
if oxlintrc.max_warnings.is_some() {
let config_path = oxlintrc.path.display();
print_and_flush_stdout(
stdout,
&format!(
"Error: 'maxWarnings' option is only valid in the root configuration file.\n\
Found in nested config: {config_path}\n"
),
);
return Err(CliRunResult::InvalidOptionConfig);
}

// Collect ignore patterns and their root
nested_ignore_patterns.push((
oxlintrc.ignore_patterns.clone(),
Expand Down
34 changes: 34 additions & 0 deletions crates/oxc_linter/src/config/oxlintrc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ pub struct Oxlintrc {
/// overriding the previous ones.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub extends: Vec<PathBuf>,
/// Specify a warning threshold, which can be used to force exit with an error status if there
/// are too many warning-level rule violations in your project.
#[serde(rename = "maxWarnings", skip_serializing_if = "Option::is_none")]
pub max_warnings: Option<usize>,
}

impl Oxlintrc {
Expand Down Expand Up @@ -237,6 +241,8 @@ impl Oxlintrc {
(None, None) => None,
};

let max_warnings = self.max_warnings.or(other.max_warnings);

Oxlintrc {
plugins,
external_plugins,
Expand All @@ -249,6 +255,7 @@ impl Oxlintrc {
path: self.path.clone(),
ignore_patterns: self.ignore_patterns.clone(),
extends: self.extends.clone(),
max_warnings,
}
}
}
Expand Down Expand Up @@ -350,4 +357,31 @@ mod test {
let config: Oxlintrc = serde_json::from_str(r#"{"extends": []}"#).unwrap();
assert_eq!(0, config.extends.len());
}

#[test]
fn test_oxlintrc_max_warnings() {
let config: Oxlintrc = serde_json::from_str(r#"{"maxWarnings": 0}"#).unwrap();
assert_eq!(config.max_warnings, Some(0));

let config: Oxlintrc = serde_json::from_str(r#"{"maxWarnings": 10}"#).unwrap();
assert_eq!(config.max_warnings, Some(10));

let config: Oxlintrc = serde_json::from_str(r#"{}"#).unwrap();
assert_eq!(config.max_warnings, None);
}

#[test]
fn test_oxlintrc_merge_max_warnings() {
let config1: Oxlintrc = serde_json::from_str(r#"{"maxWarnings": 5}"#).unwrap();
let config2: Oxlintrc = serde_json::from_str(r#"{"maxWarnings": 10}"#).unwrap();
let merged = config1.merge(config2);
// config1 takes precedence
assert_eq!(merged.max_warnings, Some(5));

let config1: Oxlintrc = serde_json::from_str(r#"{}"#).unwrap();
let config2: Oxlintrc = serde_json::from_str(r#"{"maxWarnings": 10}"#).unwrap();
let merged = config1.merge(config2);
// config2's value is used when config1 doesn't have it
assert_eq!(merged.max_warnings, Some(10));
}
}