@@ -241,7 +241,7 @@ async fn execute_add_command(
241241 global : bool ,
242242 allow_build : Option < & str > ,
243243 pass_through_args : Option < & [ String ] > ,
244- ) -> Result < ExecutionSummary , Error > {
244+ ) -> Result < ExitStatus , Error > {
245245 let save_dependency_type = if save_dev {
246246 Some ( SaveDependencyType :: Dev )
247247 } else if save_peer {
@@ -342,6 +342,37 @@ pub struct ResolvedUniversalViteConfig {
342342 pub fmt : Option < FmtConfig > ,
343343}
344344
345+ #[ cfg( unix) ]
346+ fn fix_stdio_streams ( ) {
347+ // libuv may mark stdin/stdout/stderr as close-on-exec, which interferes with Rust's subprocess spawning.
348+ // As a workaround, we clear the FD_CLOEXEC flag on these file descriptors to prevent them from being closed when spawning child processes.
349+ //
350+ // For details see https://github.com/libuv/libuv/issues/2062
351+ // Fixed by reference from https://github.com/electron/electron/pull/15555
352+
353+ use std:: os:: unix:: io:: RawFd ;
354+
355+ use nix:: libc;
356+
357+ unsafe {
358+ // Helper function to clear FD_CLOEXEC flag on a file descriptor
359+ let clear_cloexec = |fd : RawFd | {
360+ // Get current file descriptor flags
361+ let flags = libc:: fcntl ( fd, libc:: F_GETFD ) ;
362+ if flags >= 0 {
363+ // Clear the FD_CLOEXEC flag
364+ let new_flags = flags & !libc:: FD_CLOEXEC ;
365+ libc:: fcntl ( fd, libc:: F_SETFD , new_flags) ;
366+ }
367+ } ;
368+
369+ // Clear FD_CLOEXEC on stdin, stdout, stderr
370+ clear_cloexec ( libc:: STDIN_FILENO ) ;
371+ clear_cloexec ( libc:: STDOUT_FILENO ) ;
372+ clear_cloexec ( libc:: STDERR_FILENO ) ;
373+ }
374+ }
375+
345376/// Main entry point for vite-plus task execution.
346377///
347378/// # Execution Flow
@@ -409,6 +440,9 @@ pub async fn main<
409440 > ,
410441 > ,
411442) -> Result < std:: process:: ExitStatus , Error > {
443+ #[ cfg( unix) ]
444+ fix_stdio_streams ( ) ;
445+
412446 // Auto-install dependencies if needed, but skip for install command itself, or if `VITE_DISABLE_AUTO_INSTALL=1` is set.
413447 if !matches ! ( args. commands, Commands :: Install { .. } )
414448 && std:: env:: var_os ( "VITE_DISABLE_AUTO_INSTALL" ) != Some ( "1" . into ( ) )
@@ -534,7 +568,7 @@ pub async fn main<
534568 allow_build,
535569 pass_through_args,
536570 } => {
537- execute_add_command (
571+ let exit_status = execute_add_command (
538572 cwd,
539573 packages,
540574 * save_prod,
@@ -551,7 +585,8 @@ pub async fn main<
551585 allow_build. as_deref ( ) ,
552586 pass_through_args. as_deref ( ) ,
553587 )
554- . await ?
588+ . await ?;
589+ return Ok ( exit_status) ;
555590 }
556591 Commands :: Install { args } => {
557592 // Check if args contain packages - if yes, redirect to Add command
@@ -573,7 +608,7 @@ pub async fn main<
573608 pass_through_args,
574609 } ) = parse_install_as_add ( args)
575610 {
576- execute_add_command (
611+ let exit_status = execute_add_command (
577612 cwd,
578613 & packages,
579614 save_prod,
@@ -590,7 +625,8 @@ pub async fn main<
590625 allow_build. as_deref ( ) ,
591626 pass_through_args. as_deref ( ) ,
592627 )
593- . await ?
628+ . await ?;
629+ return Ok ( exit_status) ;
594630 } else {
595631 install:: InstallCommand :: builder ( cwd) . build ( ) . execute ( args) . await ?
596632 }
0 commit comments