11use directories:: BaseDirs ;
22use vite_path:: { AbsolutePathBuf , current_dir} ;
3+ use which:: which;
34
45use crate :: EnvConfig ;
56
@@ -8,7 +9,9 @@ const VITE_PLUS_HOME_DIR: &str = ".vite-plus";
89
910/// Get the vite-plus home directory.
1011///
11- /// Uses `EnvConfig::get().vite_plus_home` if set, otherwise defaults to `~/.vite-plus`.
12+ /// Uses `EnvConfig::get().vite_plus_home` if set,
13+ /// or the `vp` executable's grandparent directory,
14+ /// otherwise defaults to `~/.vite-plus`.
1215/// Falls back to `$CWD/.vite-plus` if the home directory cannot be determined.
1316pub fn get_vp_home ( ) -> std:: io:: Result < AbsolutePathBuf > {
1417 let config = EnvConfig :: get ( ) ;
@@ -18,6 +21,16 @@ pub fn get_vp_home() -> std::io::Result<AbsolutePathBuf> {
1821 return Ok ( path) ;
1922 }
2023
24+ // Get from `vp` executable file's grandparent directory (~/.vite-plus/bin/vp)
25+ // For the case where `VP_HOME` is missing
26+ if let Ok ( path) = which ( "vp" )
27+ && let Some ( parent) = path. parent ( )
28+ && let Some ( grandparent) = parent. parent ( )
29+ && grandparent. ends_with ( VITE_PLUS_HOME_DIR )
30+ {
31+ return Ok ( AbsolutePathBuf :: new ( grandparent. to_path_buf ( ) ) . unwrap ( ) ) ;
32+ }
33+
2134 // Default to ~/.vite-plus
2235 match BaseDirs :: new ( ) {
2336 Some ( dirs) => {
@@ -49,4 +62,80 @@ mod tests {
4962 assert_eq ! ( home. as_path( ) , temp_dir. as_path( ) ) ;
5063 } ) ;
5164 }
65+
66+ #[ test]
67+ #[ serial_test:: serial]
68+ fn test_get_vp_home_without_vp_home_infers_from_vp_on_path ( ) {
69+ use std:: {
70+ ffi:: { OsStr , OsString } ,
71+ path:: PathBuf ,
72+ } ;
73+
74+ let temp_dir = PathBuf :: from (
75+ std:: env:: temp_dir ( ) . join ( format ! ( "vp-test-vp-path-{}" , std:: process:: id( ) ) ) ,
76+ ) ;
77+ let vite_plus_home = temp_dir. join ( ".vite-plus" ) ;
78+ let bin_dir = vite_plus_home. join ( "bin" ) ;
79+ std:: fs:: create_dir_all ( & bin_dir) . unwrap ( ) ;
80+
81+ #[ cfg( windows) ]
82+ let vp_path = bin_dir. join ( "vp.exe" ) ;
83+ #[ cfg( not( windows) ) ]
84+ let vp_path = bin_dir. join ( "vp" ) ;
85+
86+ #[ cfg( windows) ]
87+ std:: fs:: write ( & vp_path, b"MZ" ) . unwrap ( ) ;
88+ #[ cfg( not( windows) ) ]
89+ {
90+ std:: fs:: write ( & vp_path, "#!/bin/sh\n echo 'fake vp'" ) . unwrap ( ) ;
91+ use std:: os:: unix:: fs:: PermissionsExt ;
92+ let mut perms = std:: fs:: metadata ( & vp_path) . unwrap ( ) . permissions ( ) ;
93+ perms. set_mode ( 0o755 ) ;
94+ std:: fs:: set_permissions ( & vp_path, perms) . unwrap ( ) ;
95+ }
96+
97+ struct EnvVarGuard {
98+ name : & ' static str ,
99+ original : Option < OsString > ,
100+ }
101+
102+ impl EnvVarGuard {
103+ fn set ( name : & ' static str , value : impl AsRef < OsStr > ) -> Self {
104+ let guard = Self { name, original : std:: env:: var_os ( name) } ;
105+ // SAFETY: this serial test owns process environment mutations and restores them on drop.
106+ unsafe { std:: env:: set_var ( name, value) } ;
107+ guard
108+ }
109+
110+ fn remove ( name : & ' static str ) -> Self {
111+ let guard = Self { name, original : std:: env:: var_os ( name) } ;
112+ // SAFETY: this serial test owns process environment mutations and restores them on drop.
113+ unsafe { std:: env:: remove_var ( name) } ;
114+ guard
115+ }
116+ }
117+
118+ impl Drop for EnvVarGuard {
119+ fn drop ( & mut self ) {
120+ // SAFETY: restore the environment snapshot captured by this serial test.
121+ unsafe {
122+ match & self . original {
123+ Some ( value) => std:: env:: set_var ( self . name , value) ,
124+ None => std:: env:: remove_var ( self . name ) ,
125+ }
126+ }
127+ }
128+ }
129+
130+ let path = std:: env:: join_paths ( [ bin_dir. as_os_str ( ) ] ) . unwrap ( ) ;
131+ let _path_guard = EnvVarGuard :: set ( "PATH" , path) ;
132+ let _vp_home_guard = EnvVarGuard :: remove ( crate :: env_vars:: VP_HOME ) ;
133+
134+ EnvConfig :: test_scope ( EnvConfig :: for_test ( ) , || {
135+ let home = get_vp_home ( ) . unwrap ( ) ;
136+ assert_eq ! ( home. as_path( ) , vite_plus_home. as_path( ) ) ;
137+ } ) ;
138+
139+ let _ = std:: fs:: remove_dir_all ( & temp_dir) ;
140+ }
52141}
0 commit comments