diff --git a/apps/oxlint/fixtures/jsonc_config_auto_detection/.oxlintrc.jsonc b/apps/oxlint/fixtures/jsonc_config_auto_detection/.oxlintrc.jsonc new file mode 100644 index 0000000000000..b6c9f60ef98d1 --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_config_auto_detection/.oxlintrc.jsonc @@ -0,0 +1,10 @@ +{ + "categories": { + "correctness": "off" + }, + // This is a comment in JSONC format + "rules": { + // Deny debugger statements + "no-debugger": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/jsonc_config_auto_detection/test.js b/apps/oxlint/fixtures/jsonc_config_auto_detection/test.js new file mode 100644 index 0000000000000..f8f6f25ec7265 --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_config_auto_detection/test.js @@ -0,0 +1,4 @@ +debugger; + +// this `console.log` should be allowed, because correctness is turned off, and only `debugger` is enabled +console.log(""); diff --git a/apps/oxlint/fixtures/jsonc_config_explicit/config.jsonc b/apps/oxlint/fixtures/jsonc_config_explicit/config.jsonc new file mode 100644 index 0000000000000..a27b0e0db2879 --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_config_explicit/config.jsonc @@ -0,0 +1,6 @@ +{ + // Test explicit config with comments + "rules": { + "no-console": "warn" // Warn on console usage + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/jsonc_config_explicit/test.js b/apps/oxlint/fixtures/jsonc_config_explicit/test.js new file mode 100644 index 0000000000000..ad86b2ec3f95d --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_config_explicit/test.js @@ -0,0 +1 @@ +console.log("hello"); \ No newline at end of file diff --git a/apps/oxlint/fixtures/jsonc_nested_config/.oxlintrc.jsonc b/apps/oxlint/fixtures/jsonc_nested_config/.oxlintrc.jsonc new file mode 100644 index 0000000000000..dbf8bdcb3432e --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_nested_config/.oxlintrc.jsonc @@ -0,0 +1,6 @@ +{ + // Root config + "rules": { + "no-debugger": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/jsonc_nested_config/root.js b/apps/oxlint/fixtures/jsonc_nested_config/root.js new file mode 100644 index 0000000000000..618a1028afea4 --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_nested_config/root.js @@ -0,0 +1,3 @@ +debugger; + +console.log("don't report here as i'm not enabled"); diff --git a/apps/oxlint/fixtures/jsonc_nested_config/subdir/.oxlintrc.jsonc b/apps/oxlint/fixtures/jsonc_nested_config/subdir/.oxlintrc.jsonc new file mode 100644 index 0000000000000..5a85d8545b49a --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_nested_config/subdir/.oxlintrc.jsonc @@ -0,0 +1,6 @@ +{ + // Nested config + "rules": { + "no-console": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/jsonc_nested_config/subdir/nested.js b/apps/oxlint/fixtures/jsonc_nested_config/subdir/nested.js new file mode 100644 index 0000000000000..02525d0fb872c --- /dev/null +++ b/apps/oxlint/fixtures/jsonc_nested_config/subdir/nested.js @@ -0,0 +1 @@ +console.log("test"); \ No newline at end of file diff --git a/apps/oxlint/src/command/lint.rs b/apps/oxlint/src/command/lint.rs index fa7e0f6cf8cae..a80c009466c62 100644 --- a/apps/oxlint/src/command/lint.rs +++ b/apps/oxlint/src/command/lint.rs @@ -106,11 +106,11 @@ impl LintCommand { #[derive(Debug, Clone, Bpaf)] pub struct BasicOptions { /// Oxlint configuration file (experimental) - /// * only `.json` extension is supported + /// * `.json` and `.jsonc` extensions are supported /// * you can use comments in configuration files. /// * tries to be compatible with the ESLint v8's format /// - /// If not provided, Oxlint will look for `.oxlintrc.json` in the current working directory. + /// If not provided, Oxlint will look for `.oxlintrc.jsonc` or `.oxlintrc.json` in the current working directory. #[bpaf(long, short, argument("./.oxlintrc.json"))] pub config: Option, diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index 8049aeb5756d4..a3a941fb4805f 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -413,6 +413,7 @@ impl CliRunner { impl CliRunner { const DEFAULT_OXLINTRC: &'static str = ".oxlintrc.json"; + const DEFAULT_OXLINTRC_JSONC: &'static str = ".oxlintrc.jsonc"; #[must_use] pub fn with_cwd(mut self, cwd: PathBuf) -> Self { @@ -572,12 +573,22 @@ impl CliRunner { // when no config is provided, it will search for the default file names in the current working directory // when no file is found, the default configuration is returned fn find_oxlint_config(cwd: &Path, config: Option<&PathBuf>) -> Result { - let path: &Path = config.map_or(Self::DEFAULT_OXLINTRC.as_ref(), PathBuf::as_ref); - let full_path = cwd.join(path); - - if config.is_some() || full_path.exists() { + if let Some(path) = config { + let full_path = cwd.join(path); return Oxlintrc::from_file(&full_path); } + + // Try .oxlintrc.jsonc first, then .oxlintrc.json + let jsonc_path = cwd.join(Self::DEFAULT_OXLINTRC_JSONC); + if jsonc_path.exists() { + return Oxlintrc::from_file(&jsonc_path); + } + + let json_path = cwd.join(Self::DEFAULT_OXLINTRC); + if json_path.exists() { + return Oxlintrc::from_file(&json_path); + } + Ok(Oxlintrc::default()) } @@ -585,12 +596,18 @@ impl CliRunner { /// and returns `Err` if none exists or the file is invalid. Does not apply the default /// config file. fn find_oxlint_config_in_directory(dir: &Path) -> Result, OxcDiagnostic> { - let possible_config_path = dir.join(Self::DEFAULT_OXLINTRC); - if possible_config_path.is_file() { - Oxlintrc::from_file(&possible_config_path).map(Some) - } else { - Ok(None) + // Try .oxlintrc.jsonc first, then .oxlintrc.json + let jsonc_path = dir.join(Self::DEFAULT_OXLINTRC_JSONC); + if jsonc_path.is_file() { + return Oxlintrc::from_file(&jsonc_path).map(Some); + } + + let json_path = dir.join(Self::DEFAULT_OXLINTRC); + if json_path.is_file() { + return Oxlintrc::from_file(&json_path).map(Some); } + + Ok(None) } } @@ -1276,6 +1293,26 @@ mod test { .test_and_snapshot(args); } + #[test] + fn test_jsonc_config_auto_detection() { + let args = &["test.js"]; + Tester::new() + .with_cwd("fixtures/jsonc_config_auto_detection".into()) + .test_and_snapshot(args); + } + + #[test] + fn test_jsonc_config_explicit() { + let args = &["-c", "config.jsonc", "test.js"]; + Tester::new().with_cwd("fixtures/jsonc_config_explicit".into()).test_and_snapshot(args); + } + + #[test] + fn test_jsonc_nested_config() { + let args = &["."]; + Tester::new().with_cwd("fixtures/jsonc_nested_config".into()).test_and_snapshot(args); + } + // ToDo: `tsgolint` does not support `big-endian`? #[test] #[cfg(not(target_endian = "big"))] diff --git a/apps/oxlint/src/snapshots/fixtures__jsonc_config_auto_detection_test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__jsonc_config_auto_detection_test.js@oxlint.snap new file mode 100644 index 0000000000000..7baf887904583 --- /dev/null +++ b/apps/oxlint/src/snapshots/fixtures__jsonc_config_auto_detection_test.js@oxlint.snap @@ -0,0 +1,20 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: test.js +working directory: fixtures/jsonc_config_auto_detection +---------- + + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed + ,-[test.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + `---- + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in ms on 1 file using 1 threads. +---------- +CLI result: LintFoundErrors +---------- diff --git a/apps/oxlint/src/snapshots/fixtures__jsonc_config_explicit_-c config.jsonc test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__jsonc_config_explicit_-c config.jsonc test.js@oxlint.snap new file mode 100644 index 0000000000000..91614c733ffeb --- /dev/null +++ b/apps/oxlint/src/snapshots/fixtures__jsonc_config_explicit_-c config.jsonc test.js@oxlint.snap @@ -0,0 +1,20 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: -c config.jsonc test.js +working directory: fixtures/jsonc_config_explicit +---------- + + ! ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-console.html\eslint(no-console)]8;;\: Unexpected console statement. + ,-[test.js:1:1] + 1 | console.log("hello"); + : ^^^^^^^^^^^ + `---- + help: Delete this console statement. + +Found 1 warning and 0 errors. +Finished in ms on 1 file with 90 rules using 1 threads. +---------- +CLI result: LintSucceeded +---------- diff --git a/apps/oxlint/src/snapshots/fixtures__jsonc_nested_config_.@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__jsonc_nested_config_.@oxlint.snap new file mode 100644 index 0000000000000..45634f0f8f077 --- /dev/null +++ b/apps/oxlint/src/snapshots/fixtures__jsonc_nested_config_.@oxlint.snap @@ -0,0 +1,27 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +arguments: . +working directory: fixtures/jsonc_nested_config +---------- + + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed + ,-[root.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + `---- + help: Remove the debugger statement + + x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-console.html\eslint(no-console)]8;;\: Unexpected console statement. + ,-[subdir/nested.js:1:1] + 1 | console.log("test"); + : ^^^^^^^^^^^ + `---- + help: Delete this console statement. + +Found 0 warnings and 2 errors. +Finished in ms on 2 files using 1 threads. +---------- +CLI result: LintFoundErrors +---------- diff --git a/crates/oxc_linter/src/config/oxlintrc.rs b/crates/oxc_linter/src/config/oxlintrc.rs index a33d5ee784ce4..d70296c7d27f2 100644 --- a/crates/oxc_linter/src/config/oxlintrc.rs +++ b/crates/oxc_linter/src/config/oxlintrc.rs @@ -24,7 +24,7 @@ use super::{ /// /// ::: danger NOTE /// -/// Only the `.json` format is supported. You can use comments in configuration files. +/// Both `.json` and `.jsonc` formats are supported. You can use comments in configuration files. /// /// ::: ///