@@ -22,6 +22,7 @@ pub(crate) async fn execute_check(
2222 fix : bool ,
2323 no_fmt : bool ,
2424 no_lint : bool ,
25+ no_error_on_unmatched_pattern : bool ,
2526 paths : Vec < String > ,
2627 envs : & Arc < FxHashMap < Arc < OsStr > , Arc < OsStr > > > ,
2728 cwd : & AbsolutePathBuf ,
@@ -37,14 +38,20 @@ pub(crate) async fn execute_check(
3738
3839 let mut status = ExitStatus :: SUCCESS ;
3940 let has_paths = !paths. is_empty ( ) ;
41+ // In --fix mode with file paths (the lint-staged use case), implicitly suppress
42+ // "no matching files" errors. This is also available as an explicit flag for
43+ // non-fix use cases.
44+ let suppress_unmatched = no_error_on_unmatched_pattern || ( fix && has_paths) ;
4045 let mut fmt_fix_started: Option < Instant > = None ;
4146 let mut deferred_lint_pass: Option < ( String , String ) > = None ;
4247 let resolved_vite_config = resolver. resolve_universal_vite_config ( ) . await ?;
4348
4449 if !no_fmt {
4550 let mut args = if fix { vec ! [ ] } else { vec ! [ "--check" . to_string( ) ] } ;
46- if has_paths {
51+ if suppress_unmatched {
4752 args. push ( "--no-error-on-unmatched-pattern" . to_string ( ) ) ;
53+ }
54+ if has_paths {
4855 args. extend ( paths. iter ( ) . cloned ( ) ) ;
4956 }
5057 let fmt_start = Instant :: now ( ) ;
@@ -87,11 +94,17 @@ pub(crate) async fn execute_check(
8794 ) ) ;
8895 }
8996 None => {
90- print_error_block (
91- "Formatting could not start" ,
92- & combined_output,
93- "Formatting failed before analysis started" ,
94- ) ;
97+ // oxfmt handles --no-error-on-unmatched-pattern natively and
98+ // exits 0 when no files match, so we only need to guard
99+ // against the edge case where output is unparsable but the
100+ // process still succeeded.
101+ if !( suppress_unmatched && status == ExitStatus :: SUCCESS ) {
102+ print_error_block (
103+ "Formatting could not start" ,
104+ & combined_output,
105+ "Formatting failed before analysis started" ,
106+ ) ;
107+ }
95108 }
96109 }
97110 }
@@ -127,6 +140,9 @@ pub(crate) async fn execute_check(
127140 // parser think linting never started. Force the default reporter here so the
128141 // captured output is stable across local and CI environments.
129142 args. push ( "--format=default" . to_string ( ) ) ;
143+ if suppress_unmatched {
144+ args. push ( "--no-error-on-unmatched-pattern" . to_string ( ) ) ;
145+ }
130146 if has_paths {
131147 args. extend ( paths. iter ( ) . cloned ( ) ) ;
132148 }
@@ -177,11 +193,17 @@ pub(crate) async fn execute_check(
177193 ) ) ;
178194 }
179195 None => {
180- output:: error ( "Linting could not start" ) ;
181- if !combined_output. trim ( ) . is_empty ( ) {
182- print_stdout_block ( & combined_output) ;
196+ // oxlint handles --no-error-on-unmatched-pattern natively and
197+ // exits 0 when no files match, so we only need to guard
198+ // against the edge case where output is unparsable but the
199+ // process still succeeded.
200+ if !( suppress_unmatched && status == ExitStatus :: SUCCESS ) {
201+ output:: error ( "Linting could not start" ) ;
202+ if !combined_output. trim ( ) . is_empty ( ) {
203+ print_stdout_block ( & combined_output) ;
204+ }
205+ print_summary_line ( "Linting failed before analysis started" ) ;
183206 }
184- print_summary_line ( "Linting failed before analysis started" ) ;
185207 }
186208 }
187209 if status != ExitStatus :: SUCCESS {
@@ -193,8 +215,10 @@ pub(crate) async fn execute_check(
193215 // (e.g. the curly rule adding braces to if-statements)
194216 if fix && !no_fmt && !no_lint {
195217 let mut args = Vec :: new ( ) ;
196- if has_paths {
218+ if suppress_unmatched {
197219 args. push ( "--no-error-on-unmatched-pattern" . to_string ( ) ) ;
220+ }
221+ if has_paths {
198222 args. extend ( paths. into_iter ( ) ) ;
199223 }
200224 let captured = resolve_and_capture_output (
0 commit comments