From 3a9d43b09958177540368694fc09428a360f209e Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Wed, 7 Jan 2026 08:11:46 +0000 Subject: [PATCH] fix(oxfmt): Ignore explicit positional path which is ignored by directory (#17732) Fixes #17729 --- apps/oxfmt/src/cli/walk.rs | 21 ++++++--- ...no_error_on_unmatched_pattern.test.ts.snap | 43 ++++++++++++++++++- .../.oxfmtrc.json | 3 ++ .../.prettierignore | 1 + .../custom.ignore | 1 + .../formatted/good.js | 1 + .../ignored-by-config/bad.js | 1 + .../ignored-by-ignore-path/bad.js | 1 + .../ignored-by-prettierignore/bad.js | 1 + .../no_error_on_unmatched_pattern.test.ts | 29 ++++++++++++- 10 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.oxfmtrc.json create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.prettierignore create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/custom.ignore create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/formatted/good.js create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-config/bad.js create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-ignore-path/bad.js create mode 100644 apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-prettierignore/bad.js diff --git a/apps/oxfmt/src/cli/walk.rs b/apps/oxfmt/src/cli/walk.rs index b0281341a19d9..9dc91994955b6 100644 --- a/apps/oxfmt/src/cli/walk.rs +++ b/apps/oxfmt/src/cli/walk.rs @@ -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`. @@ -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; } @@ -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; } diff --git a/apps/oxfmt/test/__snapshots__/no_error_on_unmatched_pattern.test.ts.snap b/apps/oxfmt/test/__snapshots__/no_error_on_unmatched_pattern.test.ts.snap index 33203a98a0fe1..81f22babd6e38 100644 --- a/apps/oxfmt/test/__snapshots__/no_error_on_unmatched_pattern.test.ts.snap +++ b/apps/oxfmt/test/__snapshots__/no_error_on_unmatched_pattern.test.ts.snap @@ -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... @@ -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... @@ -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. +--------------------" +`; diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.oxfmtrc.json b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.oxfmtrc.json new file mode 100644 index 0000000000000..58bea5baf7574 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "ignorePatterns": ["ignored-by-config/"] +} diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.prettierignore b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.prettierignore new file mode 100644 index 0000000000000..56f0ff3720c56 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/.prettierignore @@ -0,0 +1 @@ +ignored-by-prettierignore/ diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/custom.ignore b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/custom.ignore new file mode 100644 index 0000000000000..4553a51680580 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/custom.ignore @@ -0,0 +1 @@ +ignored-by-ignore-path/ diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/formatted/good.js b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/formatted/good.js new file mode 100644 index 0000000000000..943c458c79e20 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/formatted/good.js @@ -0,0 +1 @@ +const x = 1; diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-config/bad.js b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-config/bad.js new file mode 100644 index 0000000000000..8c979fcd5f944 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-config/bad.js @@ -0,0 +1 @@ +class A{ diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-ignore-path/bad.js b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-ignore-path/bad.js new file mode 100644 index 0000000000000..8c979fcd5f944 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-ignore-path/bad.js @@ -0,0 +1 @@ +class A{ diff --git a/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-prettierignore/bad.js b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-prettierignore/bad.js new file mode 100644 index 0000000000000..8c979fcd5f944 --- /dev/null +++ b/apps/oxfmt/test/fixtures/no_error_on_unmatched_pattern/ignored-by-prettierignore/bad.js @@ -0,0 +1 @@ +class A{ diff --git a/apps/oxfmt/test/no_error_on_unmatched_pattern.test.ts b/apps/oxfmt/test/no_error_on_unmatched_pattern.test.ts index fb17dcb725088..a6e49ad382591 100644 --- a/apps/oxfmt/test/no_error_on_unmatched_pattern.test.ts +++ b/apps/oxfmt/test/no_error_on_unmatched_pattern.test.ts @@ -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"], @@ -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(); + }); });