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
6 changes: 3 additions & 3 deletions apps/oxfmt/src-js/cli/js_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function loadJsConfig(path: string): Promise<object | null> {
fileUrl.searchParams.set("cache", Date.now().toString());
const { default: config } = await import(fileUrl.href);

if (config === undefined) throw new Error(`Configuration file has no default export: ${path}`);
if (config === undefined) throw new Error("Configuration file has no default export.");

// Vite config: extract `.fmt` field
if (pathBasename(path) === VITE_CONFIG_NAME) {
Expand All @@ -39,14 +39,14 @@ export async function loadJsConfig(path: string): Promise<object | null> {

if (!isObject(fmtConfig)) {
throw new Error(
`The \`${VITE_OXFMT_CONFIG_FIELD}\` field in the default export must be an object: ${path}`,
`The \`${VITE_OXFMT_CONFIG_FIELD}\` field in the default export must be an object.`,
);
}
return fmtConfig;
}

if (!isObject(config)) {
throw new Error(`Configuration file must have a default export that is an object: ${path}`);
throw new Error("Configuration file must have a default export that is an object.");
}

return config;
Expand Down
30 changes: 18 additions & 12 deletions apps/oxfmt/src/core/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use editorconfig_parser::{
};
use fast_glob::glob_match;
use serde_json::Value;
#[cfg(feature = "napi")]
use tracing::debug;
use tracing::instrument;

use oxc_formatter::FormatOptions;
Expand Down Expand Up @@ -301,20 +303,24 @@ impl ConfigResolver {
// For `vite.config.ts`
#[cfg(feature = "napi")]
if is_vite_plus_config(&path) {
if let Some(raw_config) = load_js_config(
match load_js_config(
js_config_loader
.expect("JS config loader must be set when `napi` feature is enabled"),
&path,
)? {
let editorconfig = load_editorconfig(cwd, editorconfig_path)?;
return Ok(Self::new(
raw_config,
path.parent().map(Path::to_path_buf),
editorconfig,
));
) {
// Load successful and `.fmt` field found -> Use it as config
Ok(Some(raw_config)) => {
let editorconfig = load_editorconfig(cwd, editorconfig_path)?;
let config_dir = path.parent().map(Path::to_path_buf);
return Ok(Self::new(raw_config, config_dir, editorconfig));
}
// Load successful but no `.fmt` field
// -> Skip and continue searching, otherwise `load_config_at()` would treat as an error
Ok(None) => debug!("No `.fmt` field in {}, skipping", path.display()),
// Load failed (e.g., syntax error, missing dependencies)
// -> Skip and continue searching, as the config file is likely not intended for Oxfmt
Err(err) => debug!("Failed to load {}: {err}, skipping", path.display()),
}
// `load_js_config()` returns `None` if `.fmt` is missing.
// Skip it and continue, otherwise `load_config_at()` would treat as an error.
continue;
}

Expand Down Expand Up @@ -492,9 +498,9 @@ fn load_js_config(
js_config_loader: &JsConfigLoaderCb,
path: &Path,
) -> Result<Option<Value>, String> {
let value = js_config_loader(path.to_string_lossy().into_owned()).map_err(|_| {
let value = js_config_loader(path.to_string_lossy().into_owned()).map_err(|err| {
format!(
"{}\nEnsure the file has a valid default export of a JSON-serializable configuration object.",
"{}\n{err}\nEnsure the file has a valid default export of a JSON-serializable configuration object.",
path.display()
)
})?;
Expand Down
3 changes: 1 addition & 2 deletions apps/oxfmt/src/core/js_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pub fn create_js_config_loader(cb: JsLoadJsConfigCb) -> JsConfigLoaderCb {
tokio::runtime::Handle::current()
.block_on(async move { cb.call_async(path).await?.into_future().await })
});

res.map_err(|_| "failed to load".to_string())
res.map_err(|e| e.reason.clone())
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ exit code: 1
--- STDERR ---------
Failed to load configuration file.
<cwd>/oxfmt.config.ts
Error: Configuration file must have a default export that is an object.
Ensure the file has a valid default export of a JSON-serializable configuration object.
--------------------"
`;
Expand All @@ -41,6 +42,7 @@ exit code: 1
--- STDERR ---------
Failed to load configuration file.
<cwd>/oxfmt.config.ts
Error: Configuration file has no default export.
Ensure the file has a valid default export of a JSON-serializable configuration object.
--------------------"
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ Finished in <variable>ms on 1 files using 1 threads.
--------------------"
`;
exports[`vite_config > skip: parent config is found when vite.config.ts fails to load 1`] = `
"--------------------
arguments: --check test.ts
working directory: vite_config/fixtures/error_finds_parent/child
exit code: 1
--- STDOUT ---------
Checking formatting...
test.ts (<variable>ms)
Format issues found in above 1 files. Run without \`--check\` to fix.
Finished in <variable>ms on 1 files using 1 threads.
--- STDERR ---------
--------------------"
`;
exports[`vite_config > skip: parent config is found when vite.config.ts without fmt is skipped 1`] = `
"--------------------
arguments: --check test.ts
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"semi": false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const a = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "non-existent-module";

export default {
fmt: {
semi: true,
},
};
9 changes: 9 additions & 0 deletions apps/oxfmt/test/cli/vite_config/vite_config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ describe("vite_config", () => {
expect(snapshot).toMatchSnapshot();
});

it("skip: parent config is found when vite.config.ts fails to load", async () => {
// child/ has vite.config.ts that imports a non-existent module → load error → skipped
// parent has .oxfmtrc.json with semi: false
// So `const a = 1;` (with semicolon) should be flagged as mismatch
const cwd = join(fixturesDir, "error_finds_parent", "child");
const snapshot = await runAndSnapshot(cwd, [["--check", "test.ts"]]);
expect(snapshot).toMatchSnapshot();
});

it("skip: parent config is found when vite.config.ts without fmt is skipped", async () => {
// child/ has vite.config.ts without .fmt → skipped
// parent has .oxfmtrc.json with semi: false
Expand Down
Loading