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
9 changes: 7 additions & 2 deletions apps/oxfmt/src/cli/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{

use ignore::gitignore::{Gitignore, GitignoreBuilder};

use crate::core::FormatFileStrategy;
use crate::core::{FormatFileStrategy, utils::normalize_relative_path};

pub struct Walk {
inner: ignore::WalkParallel,
Expand Down Expand Up @@ -232,6 +232,11 @@ impl Walk {
fn is_ignored(matchers: &[Gitignore], path: &Path, is_dir: bool, check_ancestors: bool) -> bool {
for matcher in matchers {
let matched = if check_ancestors {
// `matched_path_or_any_parents()` panics if path is not under matcher's root.
// Skip this matcher if the path is outside its scope.
if !path.starts_with(matcher.path()) {
continue;
}
matcher.matched_path_or_any_parents(path, is_dir)
} else {
matcher.matched(path, is_dir)
Expand All @@ -248,7 +253,7 @@ fn load_ignore_paths(cwd: &Path, ignore_paths: &[PathBuf]) -> Result<Vec<PathBuf
if !ignore_paths.is_empty() {
let mut result = Vec::with_capacity(ignore_paths.len());
for path in ignore_paths {
let path = if path.is_absolute() { path.clone() } else { cwd.join(path) };
let path = normalize_relative_path(cwd, path);
if !path.exists() {
return Err(format!("{}: File not found", path.display()));
}
Expand Down
6 changes: 1 addition & 5 deletions apps/oxfmt/src/core/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ use super::{FormatFileStrategy, utils};
pub fn resolve_oxfmtrc_path(cwd: &Path, config_path: Option<&Path>) -> Option<PathBuf> {
// If `--config` is explicitly specified, use that path
if let Some(config_path) = config_path {
return Some(if config_path.is_absolute() {
config_path.to_path_buf()
} else {
cwd.join(config_path)
});
return Some(utils::normalize_relative_path(cwd, config_path));
}

// If `--config` is not specified, search the nearest config file from cwd upwards
Expand Down
16 changes: 15 additions & 1 deletion apps/oxfmt/src/core/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::{fs, io, io::Write, path::Path};
use std::{
fs, io,
io::Write,
path::{Path, PathBuf},
};

/// To debug `oxc_formatter`:
/// `OXC_LOG=oxc_formatter oxfmt`
Expand Down Expand Up @@ -52,3 +56,13 @@ pub fn print_and_flush(writer: &mut dyn Write, message: &str) {
writer.write_all(message.as_bytes()).or_else(check_for_writer_error).unwrap();
writer.flush().unwrap();
}

/// Normalize a relative path by stripping `./` prefix and joining with `cwd`.
/// This ensures consistent path format and avoids issues with relative paths.
pub fn normalize_relative_path(cwd: &Path, path: &Path) -> PathBuf {
if path.is_absolute() {
return path.to_path_buf();
}

if let Ok(stripped) = path.strip_prefix("./") { cwd.join(stripped) } else { cwd.join(path) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`config_in_subdirectory > should not panic when config with \`ignorePatterns\` is in subdirectory 1`] = `
"--------------------
arguments: --check -c ./subdir/.oxfmtrc.json ./subdir
working directory: config_in_subdirectory/fixtures
exit code: 0
--- STDOUT ---------
Checking formatting...

All matched files use the correct format.
Finished in <variable>ms on 1 files using 1 threads.
--- STDERR ---------

--------------------"
`;

exports[`config_in_subdirectory > should not panic when target path is absolute and outside matcher root 1`] = `
"--------------------
arguments: --check -c ./subdir/.oxfmtrc.json <cwd>/outside/file.js
working directory: config_in_subdirectory/fixtures
exit code: 0
--- STDOUT ---------
Checking formatting...

All matched files use the correct format.
Finished in <variable>ms on 1 files using 1 threads.
--- STDERR ---------

--------------------"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { describe, expect, it } from "vitest";
import { join } from "node:path";
import { runAndSnapshot } from "../utils";

const fixturesDir = join(import.meta.dirname, "fixtures");

describe("config_in_subdirectory", () => {
it("should not panic when config with `ignorePatterns` is in subdirectory", async () => {
// Simulates running from project root:
// ```
// oxfmt -c ./subdir/.oxfmtrc.json ./subdir
// ```
// Where the config has `ignorePatterns`, causing matcher root to be `./subdir/`
const snapshot = await runAndSnapshot(fixturesDir, [
["--check", "-c", "./subdir/.oxfmtrc.json", "./subdir"],
]);
expect(snapshot).toMatchSnapshot();
});

it("should not panic when target path is absolute and outside matcher root", async () => {
// fixtures/
// subdir/ <- matcher root (has `.oxfmtrc.json` with `ignorePatterns`)
// outside/file.js <- target path (outside of matcher root)
const snapshot = await runAndSnapshot(fixturesDir, [
// Target path is outside of matcher root - should not panic
["--check", "-c", "./subdir/.oxfmtrc.json", join(fixturesDir, "outside", "file.js")],
]);
expect(snapshot).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const x = 1;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignorePatterns": ["ignored/"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class X {
Loading