Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"options": {
"reportUnusedDisableDirectives": "warn"
},
"rules": {
"no-console": "warn",
"no-debugger": "warn"
}
}
8 changes: 8 additions & 0 deletions apps/oxlint/src-js/package/config.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ export interface OxlintGlobals {
* Options for the linter.
*/
export interface OxlintOptions {
/**
* Report unused disable directives (e.g. `// oxlint-disable-line` or `// eslint-disable-line`).
*
* Equivalent to passing `--report-unused-disable-directives-severity` on the CLI.
* CLI flags take precedence over this value when both are set.
* Only supported in the root configuration file.
*/
reportUnusedDisableDirectives?: AllowWarnDeny | null;
/**
* Ensure warnings produce a non-zero exit code.
*
Expand Down
32 changes: 32 additions & 0 deletions apps/oxlint/src/config_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@ impl<'a> ConfigLoader<'a> {
)));
continue;
}
if builder.report_unused_disable_directives().is_some() {
errors.push(ConfigLoadError::Diagnostic(
nested_report_unused_disable_directives_not_supported(&path),
));
continue;
}
}

let extended_paths = builder.extended_paths.clone();
Expand Down Expand Up @@ -677,6 +683,14 @@ fn nested_max_warnings_not_supported(path: &Path) -> OxcDiagnostic {
.with_help("Move `options.maxWarnings` to the root configuration file.")
}

fn nested_report_unused_disable_directives_not_supported(path: &Path) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
"The `options.reportUnusedDisableDirectives` option is only supported in the root config, but it was found in {}.",
path.display()
))
.with_help("Move `options.reportUnusedDisableDirectives` to the root configuration file.")
}

#[cfg(test)]
mod test {
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -815,6 +829,24 @@ mod test {
let nested_path = root_dir.path().join("nested/.oxlintrc.json");
std::fs::create_dir_all(nested_path.parent().unwrap()).unwrap();
std::fs::write(&nested_path, r#"{ "options": { "denyWarnings": true } }"#).unwrap();
let mut external_plugin_store = ExternalPluginStore::new(false);
let mut loader = ConfigLoader::new(None, &mut external_plugin_store, &[], None);
let (_configs, errors) = loader
.load_discovered_with_root_dir(root_dir.path(), [DiscoveredConfig::Json(nested_path)]);
assert_eq!(errors.len(), 1);
assert!(matches!(errors[0], ConfigLoadError::Diagnostic(_)));
}

#[test]
fn test_nested_json_config_rejects_report_unused_disable_directives() {
let root_dir = tempfile::tempdir().unwrap();
let nested_path = root_dir.path().join("nested/.oxlintrc.json");
std::fs::create_dir_all(nested_path.parent().unwrap()).unwrap();
std::fs::write(
&nested_path,
r#"{ "options": { "reportUnusedDisableDirectives": "warn" } }"#,
)
.unwrap();

let mut external_plugin_store = ExternalPluginStore::new(false);
let mut loader = ConfigLoader::new(None, &mut external_plugin_store, &[], None);
Expand Down
31 changes: 29 additions & 2 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,17 @@ impl CliRunner {
let deny_warnings = warning_options.deny_warnings || config_store.deny_warnings();
let max_warnings = warning_options.max_warnings.or(config_store.max_warnings());

// Only propagate Warn/Deny; treat Allow (off) as disabling reports.
let report_unused_directives = match inline_config_options.report_unused_directives {
ReportUnusedDirectives::WithoutSeverity(true) => Some(AllowWarnDeny::Warn),
ReportUnusedDirectives::WithSeverity(Some(severity)) => Some(severity),
_ => None,
ReportUnusedDirectives::WithSeverity(Some(severity)) if severity.is_warn_deny() => {
Some(severity)
}
ReportUnusedDirectives::WithSeverity(Some(_)) => None,
_ => match config_store.report_unused_disable_directives() {
Some(severity) if severity.is_warn_deny() => Some(severity),
_ => None,
},
};
let (mut diagnostic_service, tx_error) = Self::get_diagnostic_service(
&output_formatter,
Expand Down Expand Up @@ -1059,6 +1066,26 @@ mod test {
Tester::new().with_cwd("fixtures/report_unused_directives".into()).test_and_snapshot(args);
}

#[test]
fn test_report_unused_directives_from_config() {
// Verify that `reportUnusedDisableDirectives` in the config file enables reporting
// without needing a CLI flag.
let args = &["-c", ".oxlintrc-with-rudd.json"];

Tester::new().with_cwd("fixtures/report_unused_directives".into()).test_and_snapshot(args);
}

#[test]
fn test_report_unused_directives_cli_overrides_config() {
// Verify that the CLI flag takes precedence over the config file value.
// Config has `reportUnusedDisableDirectives: "warn"`, but CLI passes `off`,
// so no unused-directive diagnostics should be reported.
let args =
&["-c", ".oxlintrc-with-rudd.json", "--report-unused-disable-directives-severity=off"];

Tester::new().with_cwd("fixtures/report_unused_directives".into()).test_and_snapshot(args);
}

#[test]
fn test_nested_config() {
let args = &[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
source: apps/oxlint/src/tester.rs
---
##########
arguments: -c .oxlintrc-with-rudd.json --report-unused-disable-directives-severity=off
working directory: fixtures/report_unused_directives
----------

! eslint(no-console): Unexpected console statement.
,-[test-multiple-scripts.vue:7:1]
6 | // eslint-disable-next-line no-debugger
7 | console.log('regular script');
: ^^^^^^^^^^^
8 |
`----
help: Delete this console statement.

! eslint(no-debugger): `debugger` statement is not allowed
,-[test-multiple-scripts.vue:10:1]
9 | // eslint-disable-next-line no-console
10 | debugger;
: ^^^^^^^^^
11 | </script>
`----
help: Remove the debugger statement

! eslint(no-console): Unexpected console statement.
,-[test-multiple-scripts.vue:31:1]
30 | // oxlint-disable-next-line no-debugger, no-for-loop
31 | console.log("complete line");
: ^^^^^^^^^^^
32 |
`----
help: Delete this console statement.

! eslint(no-debugger): `debugger` statement is not allowed
,-[test.astro:11:1]
10 | // eslint-disable-next-line no-console
11 | debugger;
: ^^^^^^^^^
12 | ---
`----
help: Remove the debugger statement

! eslint(no-console): Unexpected console statement.
,-[test.astro:32:1]
31 | // oxlint-disable-next-line no-debugger, no-for-loop
32 | console.log("complete line");
: ^^^^^^^^^^^
33 |
`----
help: Delete this console statement.

! eslint(no-debugger): `debugger` statement is not allowed
,-[test.js:10:1]
9 | // eslint-disable-next-line no-console
10 | debugger;
: ^^^^^^^^^
11 |
`----
help: Remove the debugger statement

! eslint(no-console): Unexpected console statement.
,-[test.js:27:1]
26 | // oxlint-disable-next-line no-debugger, no-for-loop
27 | console.log("complete line");
: ^^^^^^^^^^^
28 |
`----
help: Delete this console statement.

! eslint(no-debugger): `debugger` statement is not allowed
,-[test.svelte:11:1]
10 | // eslint-disable-next-line no-console
11 | debugger;
: ^^^^^^^^^
12 |
`----
help: Remove the debugger statement

! eslint(no-console): Unexpected console statement.
,-[test.svelte:28:1]
27 | // oxlint-disable-next-line no-debugger, no-for-loop
28 | console.log("complete line");
: ^^^^^^^^^^^
29 |
`----
help: Delete this console statement.

! eslint(no-debugger): `debugger` statement is not allowed
,-[test.vue:15:1]
14 | // eslint-disable-next-line no-console
15 | debugger;
: ^^^^^^^^^
16 |
`----
help: Remove the debugger statement

! eslint(no-console): Unexpected console statement.
,-[test.vue:32:1]
31 | // oxlint-disable-next-line no-debugger, no-for-loop
32 | console.log("complete line");
: ^^^^^^^^^^^
33 |
`----
help: Delete this console statement.

Found 11 warnings and 0 errors.
Finished in <variable>ms on 5 files with 94 rules using 1 threads.
----------
CLI result: LintSucceeded
----------
Loading
Loading