@@ -7,7 +7,7 @@ use vite_path::{AbsolutePathBuf, current_dir};
77use vite_shared:: { env_vars, output} ;
88
99use super :: config:: { self , ShimMode , get_bin_dir, get_vp_home, load_config, resolve_version} ;
10- use crate :: error:: Error ;
10+ use crate :: { error:: Error , shim } ;
1111
1212/// IDE-relevant profile files that GUI-launched applications can see.
1313/// GUI apps don't run through an interactive shell, so only login/environment
@@ -114,7 +114,7 @@ pub async fn execute(cwd: AbsolutePathBuf) -> Result<ExitStatus, Error> {
114114
115115 // Section: Configuration
116116 print_section ( "Configuration" ) ;
117- check_shim_mode ( ) . await ;
117+ let ( shim_mode , system_node_path ) = check_shim_mode ( ) . await ;
118118
119119 // Check env sourcing: IDE-relevant profiles first, then all shell profiles
120120 #[ cfg( not( windows) ) ]
@@ -128,7 +128,7 @@ pub async fn execute(cwd: AbsolutePathBuf) -> Result<ExitStatus, Error> {
128128
129129 // Section: Version Resolution
130130 print_section ( "Version Resolution" ) ;
131- check_current_resolution ( & cwd) . await ;
131+ check_current_resolution ( & cwd, shim_mode , system_node_path ) . await ;
132132
133133 // Section: Conflicts (conditional)
134134 check_conflicts ( ) ;
@@ -247,43 +247,48 @@ fn shim_filename(tool: &str) -> String {
247247 }
248248}
249249
250- /// Check and display shim mode.
251- async fn check_shim_mode ( ) {
250+ /// Check and display shim mode. Returns the mode and any found system node path.
251+ async fn check_shim_mode ( ) -> ( ShimMode , Option < AbsolutePathBuf > ) {
252252 let config = match load_config ( ) . await {
253253 Ok ( c) => c,
254254 Err ( e) => {
255255 print_check (
256256 & output:: WARN_SIGN . yellow ( ) . to_string ( ) ,
257- "Shim mode" ,
257+ "Node.js mode" ,
258258 & format ! ( "config error: {e}" ) . yellow ( ) . to_string ( ) ,
259259 ) ;
260- return ;
260+ return ( ShimMode :: default ( ) , None ) ;
261261 }
262262 } ;
263263
264+ let mut system_node_path = None ;
265+
264266 match config. shim_mode {
265267 ShimMode :: Managed => {
266- print_check ( & output:: CHECK . green ( ) . to_string ( ) , "Shim mode" , "managed" ) ;
268+ print_check ( & output:: CHECK . green ( ) . to_string ( ) , "Node.js mode" , "managed" ) ;
267269 }
268270 ShimMode :: SystemFirst => {
269271 print_check (
270272 & output:: CHECK . green ( ) . to_string ( ) ,
271- "Shim mode" ,
273+ "Node.js mode" ,
272274 & "system-first" . bright_blue ( ) . to_string ( ) ,
273275 ) ;
274276
275277 // Check if system Node.js is available
276- if let Some ( system_node) = find_system_node ( ) {
277- print_check ( " " , "System Node.js" , & system_node. display ( ) . to_string ( ) ) ;
278+ if let Some ( system_node) = shim:: find_system_tool ( "node" ) {
279+ print_check ( " " , "System Node.js" , & system_node. as_path ( ) . display ( ) . to_string ( ) ) ;
280+ system_node_path = Some ( system_node) ;
278281 } else {
279282 print_check (
280283 & output:: WARN_SIGN . yellow ( ) . to_string ( ) ,
281284 "System Node.js" ,
282- & "not found (will use managed)" . yellow ( ) . to_string ( ) ,
285+ & "not found (will fall back to managed)" . yellow ( ) . to_string ( ) ,
283286 ) ;
284287 }
285288 }
286289 }
290+
291+ ( config. shim_mode , system_node_path)
287292}
288293
289294/// Check profile files for env sourcing and classify where it was found.
@@ -338,36 +343,6 @@ fn check_env_sourcing() -> EnvSourcingStatus {
338343 EnvSourcingStatus :: NotFound
339344}
340345
341- /// Find system Node.js, skipping vite-plus bin directory and any
342- /// directories listed in `VP_BYPASS`.
343- fn find_system_node ( ) -> Option < std:: path:: PathBuf > {
344- let bin_dir = get_bin_dir ( ) . ok ( ) ;
345- let path_var = std:: env:: var_os ( "PATH" ) ?;
346-
347- // Parse VP_BYPASS as a PATH-style list of additional directories to skip
348- let bypass_paths: Vec < std:: path:: PathBuf > = std:: env:: var_os ( env_vars:: VP_BYPASS )
349- . map ( |v| std:: env:: split_paths ( & v) . collect ( ) )
350- . unwrap_or_default ( ) ;
351-
352- // Filter PATH to exclude our bin directory and any bypass directories
353- let filtered_paths: Vec < _ > = std:: env:: split_paths ( & path_var)
354- . filter ( |p| {
355- if let Some ( ref bin) = bin_dir {
356- if p == bin. as_path ( ) {
357- return false ;
358- }
359- }
360- !bypass_paths. iter ( ) . any ( |bp| p == bp)
361- } )
362- . collect ( ) ;
363-
364- let filtered_path = std:: env:: join_paths ( filtered_paths) . ok ( ) ?;
365-
366- // Use vite_command::resolve_bin with filtered PATH - stops at first match
367- let cwd = current_dir ( ) . ok ( ) ?;
368- vite_command:: resolve_bin ( "node" , Some ( & filtered_path) , & cwd) . ok ( ) . map ( |p| p. into_path_buf ( ) )
369- }
370-
371346/// Check for active session override via VP_NODE_VERSION or session file.
372347fn check_session_override ( ) {
373348 if let Ok ( version) = std:: env:: var ( config:: VERSION_ENV_VAR ) {
@@ -613,9 +588,35 @@ fn print_ide_setup_guidance(bin_dir: &vite_path::AbsolutePath) {
613588}
614589
615590/// Check current directory version resolution.
616- async fn check_current_resolution ( cwd : & AbsolutePathBuf ) {
591+ async fn check_current_resolution (
592+ cwd : & AbsolutePathBuf ,
593+ shim_mode : ShimMode ,
594+ system_node_path : Option < AbsolutePathBuf > ,
595+ ) {
617596 print_check ( " " , "Directory" , & cwd. as_path ( ) . display ( ) . to_string ( ) ) ;
618597
598+ // In system-first mode, show system Node.js info instead of managed resolution
599+ if shim_mode == ShimMode :: SystemFirst {
600+ if let Some ( system_node) = system_node_path {
601+ let version = get_node_version ( & system_node) . await ;
602+ print_check ( " " , "Source" , "system PATH" ) ;
603+ print_check ( " " , "Version" , & version. bright_green ( ) . to_string ( ) ) ;
604+ print_check (
605+ & output:: CHECK . green ( ) . to_string ( ) ,
606+ "Node binary" ,
607+ & system_node. as_path ( ) . display ( ) . to_string ( ) ,
608+ ) ;
609+ } else {
610+ print_check (
611+ & output:: WARN_SIGN . yellow ( ) . to_string ( ) ,
612+ "System Node.js" ,
613+ & "not found in PATH" . yellow ( ) . to_string ( ) ,
614+ ) ;
615+ print_hint ( "Install Node.js or run 'vp env on' to use managed Node.js." ) ;
616+ }
617+ return ;
618+ }
619+
619620 match resolve_version ( cwd) . await {
620621 Ok ( resolution) => {
621622 let source_display = resolution
@@ -658,6 +659,16 @@ async fn check_current_resolution(cwd: &AbsolutePathBuf) {
658659 }
659660}
660661
662+ /// Get the version string from a Node.js binary.
663+ async fn get_node_version ( node_path : & vite_path:: AbsolutePath ) -> String {
664+ match tokio:: process:: Command :: new ( node_path. as_path ( ) ) . arg ( "--version" ) . output ( ) . await {
665+ Ok ( output) if output. status . success ( ) => {
666+ String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( )
667+ }
668+ _ => "unknown" . to_string ( ) ,
669+ }
670+ }
671+
661672/// Check for conflicts with other version managers.
662673fn check_conflicts ( ) {
663674 let mut conflicts = Vec :: new ( ) ;
@@ -806,9 +817,12 @@ mod tests {
806817 std:: env:: set_var ( env_vars:: VP_BYPASS , dir_a. as_os_str ( ) ) ;
807818 }
808819
809- let result = find_system_node ( ) ;
820+ let result = shim :: find_system_tool ( "node" ) ;
810821 assert ! ( result. is_some( ) , "Should find node in non-bypassed directory" ) ;
811- assert ! ( result. unwrap( ) . starts_with( & dir_b) , "Should find node in dir_b, not dir_a" ) ;
822+ assert ! (
823+ result. unwrap( ) . as_path( ) . starts_with( & dir_b) ,
824+ "Should find node in dir_b, not dir_a"
825+ ) ;
812826 }
813827
814828 #[ test]
@@ -826,7 +840,7 @@ mod tests {
826840 std:: env:: set_var ( env_vars:: VP_BYPASS , dir_a. as_os_str ( ) ) ;
827841 }
828842
829- let result = find_system_node ( ) ;
843+ let result = shim :: find_system_tool ( "node" ) ;
830844 assert ! ( result. is_none( ) , "Should return None when all paths are bypassed" ) ;
831845 }
832846
0 commit comments