@@ -243,13 +243,15 @@ pub fn find_local_binary(cwd: &AbsolutePath, cmd: &str) -> Option<AbsolutePathBu
243243 let mut current = cwd;
244244 loop {
245245 let bin_dir = current. join ( "node_modules" ) . join ( ".bin" ) ;
246- let bin_path = bin_dir. join ( cmd) ;
247246
248- if bin_path. as_path ( ) . exists ( ) {
249- return Some ( bin_path) ;
247+ #[ cfg( not( windows) ) ]
248+ {
249+ let bin_path = bin_dir. join ( cmd) ;
250+ if bin_path. as_path ( ) . exists ( ) {
251+ return Some ( bin_path) ;
252+ }
250253 }
251254
252- // On Windows, check for .cmd extension
253255 #[ cfg( windows) ]
254256 {
255257 let cmd_path = bin_dir. join ( format ! ( "{cmd}.cmd" ) ) ;
@@ -536,11 +538,18 @@ mod tests {
536538 let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
537539 let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
538540
539- // Create node_modules/.bin/eslint
541+ // Create node_modules/.bin/eslint and eslint.cmd
540542 let bin_dir = temp_path. join ( "node_modules" ) . join ( ".bin" ) ;
541543 std:: fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
544+ // Unix script
545+ std:: fs:: write ( bin_dir. join ( "eslint" ) , "#!/bin/sh\n " ) . unwrap ( ) ;
546+ #[ cfg( windows) ]
547+ std:: fs:: write ( bin_dir. join ( "eslint.cmd" ) , "@eslint %*\r \n " ) . unwrap ( ) ;
548+
549+ #[ cfg( not( windows) ) ]
542550 let eslint_path = bin_dir. join ( "eslint" ) ;
543- std:: fs:: write ( & eslint_path, "#!/bin/sh\n " ) . unwrap ( ) ;
551+ #[ cfg( windows) ]
552+ let eslint_path = bin_dir. join ( "eslint.cmd" ) ;
544553
545554 let result = find_local_binary ( & temp_path, "eslint" ) ;
546555 assert ! ( result. is_some( ) ) ;
@@ -552,11 +561,17 @@ mod tests {
552561 let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
553562 let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
554563
555- // Create node_modules/.bin/eslint at root
564+ // Create node_modules/.bin/eslint and eslint.cmd at root
556565 let bin_dir = temp_path. join ( "node_modules" ) . join ( ".bin" ) ;
557566 std:: fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
567+ std:: fs:: write ( bin_dir. join ( "eslint" ) , "#!/bin/sh\n " ) . unwrap ( ) ;
568+ #[ cfg( windows) ]
569+ std:: fs:: write ( bin_dir. join ( "eslint.cmd" ) , "@eslint %*\r \n " ) . unwrap ( ) ;
570+
571+ #[ cfg( not( windows) ) ]
558572 let eslint_path = bin_dir. join ( "eslint" ) ;
559- std:: fs:: write ( & eslint_path, "#!/bin/sh\n " ) . unwrap ( ) ;
573+ #[ cfg( windows) ]
574+ let eslint_path = bin_dir. join ( "eslint.cmd" ) ;
560575
561576 // Create nested directory
562577 let nested_dir = temp_path. join ( "packages" ) . join ( "app" ) ;
@@ -586,19 +601,58 @@ mod tests {
586601 let root_bin = temp_path. join ( "node_modules" ) . join ( ".bin" ) ;
587602 std:: fs:: create_dir_all ( & root_bin) . unwrap ( ) ;
588603 std:: fs:: write ( root_bin. join ( "eslint" ) , "root" ) . unwrap ( ) ;
604+ #[ cfg( windows) ]
605+ std:: fs:: write ( root_bin. join ( "eslint.cmd" ) , "@eslint %*\r \n " ) . unwrap ( ) ;
589606
590607 // Create eslint in nested package
591608 let nested = temp_path. join ( "packages" ) . join ( "app" ) ;
592609 let nested_bin = nested. join ( "node_modules" ) . join ( ".bin" ) ;
593610 std:: fs:: create_dir_all ( & nested_bin) . unwrap ( ) ;
594611 std:: fs:: write ( nested_bin. join ( "eslint" ) , "nested" ) . unwrap ( ) ;
612+ #[ cfg( windows) ]
613+ std:: fs:: write ( nested_bin. join ( "eslint.cmd" ) , "@eslint %*\r \n " ) . unwrap ( ) ;
614+
615+ #[ cfg( not( windows) ) ]
616+ let nested_bin = nested_bin. join ( "eslint" ) ;
617+ #[ cfg( windows) ]
618+ let nested_bin = nested_bin. join ( "eslint.cmd" ) ;
595619
596620 let nested_abs = AbsolutePathBuf :: new ( nested. as_path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
597621 let result = find_local_binary ( & nested_abs, "eslint" ) ;
598622 assert ! ( result. is_some( ) ) ;
599623 // Should find the nested one first
600624 let found = result. unwrap ( ) ;
601- assert_eq ! ( found. as_path( ) , nested_bin. join( "eslint" ) . as_path( ) ) ;
625+ assert_eq ! ( found. as_path( ) , nested_bin. as_path( ) ) ;
626+ }
627+
628+ #[ cfg( windows) ]
629+ #[ test]
630+ fn test_find_local_binary_windows_prefers_cmd_over_extensionless ( ) {
631+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
632+ let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
633+
634+ let bin_dir = temp_path. join ( "node_modules" ) . join ( ".bin" ) ;
635+ std:: fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
636+ std:: fs:: write ( bin_dir. join ( "playwright" ) , "#!/bin/sh\n " ) . unwrap ( ) ;
637+ std:: fs:: write ( bin_dir. join ( "playwright.cmd" ) , "@playwright %*\r \n " ) . unwrap ( ) ;
638+
639+ let result = find_local_binary ( & temp_path, "playwright" ) ;
640+ assert ! ( result. is_some( ) ) ;
641+ assert_eq ! ( result. unwrap( ) . as_path( ) , bin_dir. join( "playwright.cmd" ) . as_path( ) ) ;
642+ }
643+
644+ #[ cfg( windows) ]
645+ #[ test]
646+ fn test_find_local_binary_windows_ignores_extensionless_only ( ) {
647+ let temp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
648+ let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
649+
650+ let bin_dir = temp_path. join ( "node_modules" ) . join ( ".bin" ) ;
651+ std:: fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
652+ std:: fs:: write ( bin_dir. join ( "playwright" ) , "#!/bin/sh\n " ) . unwrap ( ) ;
653+
654+ let result = find_local_binary ( & temp_path, "playwright" ) ;
655+ assert ! ( result. is_none( ) ) ;
602656 }
603657
604658 // =========================================================================
0 commit comments