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
21 changes: 15 additions & 6 deletions apps/oxfmt/src/cli/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,16 @@ impl Walk {
}

//
// Filter paths by formatter ignores
// Filter positional paths by formatter ignores
//
// NOTE: Base paths passed to `WalkBuilder` are not filtered by `filter_entry()`,
// Base paths passed to `WalkBuilder` are not filtered by `filter_entry()`,
// so we need to filter them here before passing to the walker.
// This is needed for cases like `husky`, may specify ignored paths as staged files.
// NOTE: Git ignored paths are not filtered here.
// But it's OK because in cases like `husky`, they are never staged.
let target_paths: Vec<_> = target_paths
.into_iter()
.filter(|path| !is_ignored(&matchers, path, path.is_dir()))
.filter(|path| !is_ignored(&matchers, path, path.is_dir(), true))
.collect();

// If no target paths remain after filtering, return `None`.
Expand Down Expand Up @@ -170,7 +171,7 @@ impl Walk {
}

// Check ignore files, patterns
if is_ignored(&matchers, entry.path(), is_dir) {
if is_ignored(&matchers, entry.path(), is_dir, false) {
return false;
}

Expand Down Expand Up @@ -224,9 +225,17 @@ impl Walk {

/// Check if a path should be ignored by any of the matchers.
/// A path is ignored if any matcher says it's ignored (and not whitelisted in that same matcher).
fn is_ignored(matchers: &[Gitignore], path: &Path, is_dir: bool) -> bool {
///
/// When `check_ancestors: true`, also checks if any parent directory is ignored.
/// This is more expensive, but necessary when paths (to be ignored) are passed directly via CLI arguments.
/// For normal walking, walk is done in a top-down manner, so only the current path needs to be checked.
fn is_ignored(matchers: &[Gitignore], path: &Path, is_dir: bool, check_ancestors: bool) -> bool {
for matcher in matchers {
let matched = matcher.matched(path, is_dir);
let matched = if check_ancestors {
matcher.matched_path_or_any_parents(path, is_dir)
} else {
matcher.matched(path, is_dir)
};
if matched.is_ignore() && !matched.is_whitelist() {
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`no_error_on_unmatched_pattern > should handle --no-error-on-unmatched-pattern flag 1`] = `
"--------------------
arguments: --check --no-error-on-unmatched-pattern __non__existent__file.js
working directory: fixtures
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 0
--- STDOUT ---------
Checking formatting...
Expand All @@ -14,7 +14,7 @@ No files found matching the given patterns.
--------------------
--------------------
arguments: --check __non__existent__file.js
working directory: fixtures
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 2
--- STDOUT ---------
Checking formatting...
Expand All @@ -23,3 +23,42 @@ Checking formatting...
Expected at least one target file
--------------------"
`;

exports[`no_error_on_unmatched_pattern > should ignore ignored paths even when explicitly passed 1`] = `
"--------------------
arguments: --check --no-error-on-unmatched-pattern ignored-by-config/bad.js
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 0
--- STDOUT ---------

--- STDERR ---------
No files found matching the given patterns.
--------------------
--------------------
arguments: --check --no-error-on-unmatched-pattern ignored-by-config
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 0
--- STDOUT ---------

--- STDERR ---------
No files found matching the given patterns.
--------------------
--------------------
arguments: --check --no-error-on-unmatched-pattern ignored-by-prettierignore/bad.js
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 0
--- STDOUT ---------

--- STDERR ---------
No files found matching the given patterns.
--------------------
--------------------
arguments: --check --no-error-on-unmatched-pattern --ignore-path custom.ignore ignored-by-ignore-path/bad.js
working directory: fixtures/no_error_on_unmatched_pattern
exit code: 0
--- STDOUT ---------

--- STDERR ---------
No files found matching the given patterns.
--------------------"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ignorePatterns": ["ignored-by-config/"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignored-by-prettierignore/
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ignored-by-ignore-path/
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 @@
class A{
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class A{
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class A{
29 changes: 27 additions & 2 deletions apps/oxfmt/test/no_error_on_unmatched_pattern.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { describe, expect, it } from "vitest";
import { join } from "node:path";
import { runAndSnapshot } from "./utils";

const fixturesDir = join(__dirname, "fixtures");
const fixturesDir = join(__dirname, "fixtures", "no_error_on_unmatched_pattern");

describe("no_error_on_unmatched_pattern", () => {
it("should handle --no-error-on-unmatched-pattern flag", async () => {
const cwd = fixturesDir;
const cwd = join(fixturesDir);
const testCases = [
["--check", "--no-error-on-unmatched-pattern", "__non__existent__file.js"],
["--check", "__non__existent__file.js"],
Expand All @@ -15,4 +15,29 @@ describe("no_error_on_unmatched_pattern", () => {
const snapshot = await runAndSnapshot(cwd, testCases);
expect(snapshot).toMatchSnapshot();
});

// When a file path inside an ignored directory is passed directly via CLI, it should still be ignored.
// This is important for tools like lint-staged that pass explicit file paths.
it("should ignore ignored paths even when explicitly passed", async () => {
const cwd = join(fixturesDir);
const testCases = [
// Explicit file path - should still be ignored
["--check", "--no-error-on-unmatched-pattern", "ignored-by-config/bad.js"],
// Explicit directory path - should still be ignored
["--check", "--no-error-on-unmatched-pattern", "ignored-by-config"],
// Explicit file path - should be ignored
["--check", "--no-error-on-unmatched-pattern", "ignored-by-prettierignore/bad.js"],
// Explicit file path with custom ignore file - should be ignored
[
"--check",
"--no-error-on-unmatched-pattern",
"--ignore-path",
"custom.ignore",
"ignored-by-ignore-path/bad.js",
],
];

const snapshot = await runAndSnapshot(cwd, testCases);
expect(snapshot).toMatchSnapshot();
});
});
Loading