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
2 changes: 1 addition & 1 deletion apps/oxlint/src-js/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function destroyWorkspaceWrapper(workspace: string): undefined {
* Lazy-loads the js_config module on first call.
* Uses native Node.js TypeScript support to import config files.
*
* @param paths - Array of absolute paths to oxlint.config.ts files
* @param paths - Array of absolute paths to JavaScript/TypeScript config files
* @returns JSON-stringified result with all configs or error
*/
function loadJsConfigsWrapper(paths: string[]): Promise<string> {
Expand Down
2 changes: 1 addition & 1 deletion apps/oxlint/src-js/js_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function validateConfigExtends(root: object): void {
* Uses native Node.js TypeScript support to import the config files.
* Each config file should have a default export containing the oxlint configuration.
*
* @param paths - Array of absolute paths to oxlint.config.ts files
* @param paths - Array of absolute paths to JavaScript/TypeScript config files
* @returns JSON-stringified result with all configs or error
*/
export async function loadJsConfigs(paths: string[]): Promise<string> {
Expand Down
3 changes: 2 additions & 1 deletion apps/oxlint/src/command/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ impl LintCommand {
#[derive(Debug, Clone, Bpaf)]
pub struct BasicOptions {
/// Oxlint configuration file
/// * only `.json` extension is supported
/// * `.json` config files are supported in all runtimes
/// * JavaScript/TypeScript config files are experimental and require running via Node.js
/// * you can use comments in configuration files.
/// * tries to be compatible with ESLint v8's format
///
Expand Down
50 changes: 34 additions & 16 deletions apps/oxlint/src/config_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub enum ConfigLoadError {
error: String,
},

TypeScriptConfigFileFoundButJsRuntimeNotAvailable,
JsConfigFileFoundButJsRuntimeNotAvailable,

Diagnostic(OxcDiagnostic),
}
Expand Down Expand Up @@ -273,12 +273,12 @@ impl<'a> ConfigLoader<'a> {

#[cfg(not(feature = "napi"))]
{
return Err(vec![ConfigLoadError::TypeScriptConfigFileFoundButJsRuntimeNotAvailable]);
return Err(vec![ConfigLoadError::JsConfigFileFoundButJsRuntimeNotAvailable]);
}

#[cfg(feature = "napi")]
let Some(js_config_loader) = self.js_config_loader else {
return Err(vec![ConfigLoadError::TypeScriptConfigFileFoundButJsRuntimeNotAvailable]);
return Err(vec![ConfigLoadError::JsConfigFileFoundButJsRuntimeNotAvailable]);
};

let paths_as_strings: Vec<String> =
Expand Down Expand Up @@ -414,7 +414,7 @@ impl<'a> ConfigLoader<'a> {
}

if ts_exists {
return self.load_root_ts_config(&ts_path).map(Some);
return self.load_root_js_config(&ts_path).map(Some);
}

if json_exists {
Expand All @@ -431,8 +431,8 @@ impl<'a> ConfigLoader<'a> {
) -> Result<Oxlintrc, OxcDiagnostic> {
if let Some(config_path) = config_path {
let full_path = cwd.join(config_path);
if full_path.file_name() == Some(OsStr::new(DEFAULT_TS_OXLINTRC_NAME)) {
return self.load_root_ts_config(&full_path);
if is_js_config_path(&full_path) {
return self.load_root_js_config(&full_path);
}
return Oxlintrc::from_file(&full_path);
}
Expand Down Expand Up @@ -462,8 +462,8 @@ impl<'a> ConfigLoader<'a> {
// If an explicit config path is provided, use it directly
if let Some(config_path) = config_path {
let full_path = cwd.join(config_path);
if full_path.file_name() == Some(OsStr::new(DEFAULT_TS_OXLINTRC_NAME)) {
return self.load_root_ts_config(&full_path);
if is_js_config_path(&full_path) {
return self.load_root_js_config(&full_path);
}
return Oxlintrc::from_file(&full_path);
}
Expand All @@ -482,14 +482,14 @@ impl<'a> ConfigLoader<'a> {
Ok(Oxlintrc::default())
}

fn load_root_ts_config(&self, path: &Path) -> Result<Oxlintrc, OxcDiagnostic> {
fn load_root_js_config(&self, path: &Path) -> Result<Oxlintrc, OxcDiagnostic> {
match self.load_js_configs(&[path.to_path_buf()]) {
Ok(mut configs) => Ok(configs.pop().unwrap_or_default()),
Err(errors) => {
if let Some(first) = errors.into_iter().next() {
match first {
ConfigLoadError::TypeScriptConfigFileFoundButJsRuntimeNotAvailable => {
Err(ts_config_not_supported_diagnostic(path))
ConfigLoadError::JsConfigFileFoundButJsRuntimeNotAvailable => {
Err(js_config_not_supported_diagnostic(path))
}
ConfigLoadError::Diagnostic(diag) => Err(diag),
// `load_js_configs` only returns the two variants above, but keep this
Expand All @@ -498,7 +498,7 @@ impl<'a> ConfigLoader<'a> {
ConfigLoadError::Build { error, .. } => Err(OxcDiagnostic::error(error)),
}
} else {
Err(OxcDiagnostic::error("Failed to load TypeScript config."))
Err(OxcDiagnostic::error("Failed to load JavaScript/TypeScript config."))
}
}
}
Expand Down Expand Up @@ -599,21 +599,28 @@ fn config_conflict_diagnostic(dir: &Path) -> OxcDiagnostic {
.with_help("Delete one of the configuration files.")
}

fn ts_config_not_supported_diagnostic(path: &Path) -> OxcDiagnostic {
fn js_config_not_supported_diagnostic(path: &Path) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
"TypeScript config files ({}) found but JS runtime not available.",
"JavaScript/TypeScript config file ({}) found but JS runtime not available.",
path.display()
))
.with_help("Run oxlint via the npm package, or use JSON config files (.oxlintrc.json).")
}

fn is_js_config_path(path: &Path) -> bool {
matches!(
path.extension().and_then(OsStr::to_str),
Some("js" | "mjs" | "cjs" | "ts" | "cts" | "mts")
)
}

#[cfg(test)]
mod test {
use std::path::PathBuf;
use std::path::{Path, PathBuf};

use oxc_linter::ExternalPluginStore;

use super::ConfigLoader;
use super::{ConfigLoader, is_js_config_path};

#[test]
fn test_config_path_with_parent_references() {
Expand Down Expand Up @@ -682,4 +689,15 @@ mod test {
assert!(result.is_ok(), "Expected default config when no config found");
std::fs::remove_dir_all(&temp_dir).expect("Failed to cleanup temporary test directory");
}

#[test]
fn test_is_js_config_path() {
assert!(is_js_config_path(Path::new("my-config.js")));
assert!(is_js_config_path(Path::new("my-config.cjs")));
assert!(is_js_config_path(Path::new("my-config.mjs")));
assert!(is_js_config_path(Path::new("my-config.ts")));
assert!(is_js_config_path(Path::new("my-config.cts")));
assert!(is_js_config_path(Path::new("my-config.mts")));
assert!(!is_js_config_path(Path::new("oxlint.config.json")));
}
}
4 changes: 2 additions & 2 deletions apps/oxlint/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ impl CliRunner {
)
)
}
ConfigLoadError::TypeScriptConfigFileFoundButJsRuntimeNotAvailable => {
"Error: TypeScript config files (oxlint.config.ts) found but JS runtime not available.\n\
ConfigLoadError::JsConfigFileFoundButJsRuntimeNotAvailable => {
"Error: JavaScript/TypeScript config files found but JS runtime not available.\n\
This is an experimental feature that requires running oxlint via Node.js.\n\
Please use JSON config files (.oxlintrc.json) instead, or run oxlint via the npm package.\n".to_string()
}
Expand Down
2 changes: 1 addition & 1 deletion apps/oxlint/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ pub type JsDestroyWorkspaceCb = ThreadsafeFunction<
/// JS callback to load JavaScript config files.
#[napi]
pub type JsLoadJsConfigsCb = ThreadsafeFunction<
// Arguments: Vec of absolute paths to oxlint.config.ts files
// Arguments: Vec of absolute paths to JavaScript/TypeScript config files
Vec<String>,
// Return value: JSON string containing success/failure result
Promise<String>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
debugger;
if (x == 1) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from "#oxlint";

export default defineConfig({
rules: {
"no-debugger": "error",
eqeqeq: "warn",
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"args": ["-c", "my-oxlint-config.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Exit code
1

# stdout
```
x eslint(no-debugger): `debugger` statement is not allowed
,-[files/test.js:1:1]
1 | debugger;
: ^^^^^^^^^
2 | if (x == 1) {
`----
help: Remove the debugger statement

! eslint(eqeqeq): Expected === and instead saw ==
,-[files/test.js:2:7]
1 | debugger;
2 | if (x == 1) {
: ^^
3 | }
`----
help: Prefer === operator

Found 1 warning and 1 error.
Finished in Xms on 1 file with 94 rules using X threads.
```

# stderr
```
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
debugger;
if (x == 1) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from "#oxlint";

export default defineConfig({
// @ts-expect-error - we are testing invalid CLI args, so we need to ignore the type error here
x: {
"no-debugger": "error",
eqeqeq: "warn",
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"args": ["-c", "my-oxlint-config.ts"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Exit code
1

# stdout
```
Failed to parse oxlint configuration file.

x Failed to parse config from <fixture>/my-oxlint-config.ts
note: unknown field `x`, expected one of `$schema`, `plugins`, `jsPlugins`, `categories`, `rules`, `settings`, `env`, `globals`, `overrides`, `ignorePatterns`, `extends`
```

# stderr
```
```
3 changes: 2 additions & 1 deletion tasks/website_linter/src/snapshots/cli.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ search: false
## Basic Configuration
- **`-c`**, **`--config`**=_`<./.oxlintrc.json>`_ &mdash;
Oxlint configuration file
* only `.json` extension is supported
* `.json` config files are supported in all runtimes
* JavaScript/TypeScript config files are experimental and require running via Node.js
* you can use comments in configuration files.
* tries to be compatible with ESLint v8's format

Expand Down
4 changes: 3 additions & 1 deletion tasks/website_linter/src/snapshots/cli_terminal.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ Usage: [-c=<./.oxlintrc.json>] [PATH]...

Basic Configuration
-c, --config=<./.oxlintrc.json> Oxlint configuration file
* only `.json` extension is supported
* `.json` config files are supported in all runtimes
* JavaScript/TypeScript config files are experimental and require
running via Node.js
* you can use comments in configuration files.
* tries to be compatible with ESLint v8's format
--tsconfig=<./tsconfig.json> TypeScript `tsconfig.json` path for reading path alias and
Expand Down
Loading