@@ -31,39 +31,6 @@ use vp_setup::{VP_BINARY_NAME, install, integrity, platform, registry};
3131use vp_shared:: VpDirs ;
3232use vt_path:: AbsolutePathBuf ;
3333
34- #[ derive( Debug ) ]
35- struct AbandonedSplitData {
36- data : AbsolutePathBuf ,
37- parent_to_remove : Option < std:: path:: PathBuf > ,
38- }
39-
40- impl AbandonedSplitData {
41- async fn capture ( data : AbsolutePathBuf ) -> Option < Self > {
42- if tokio:: fs:: try_exists ( & data) . await . unwrap_or ( true ) {
43- return None ;
44- }
45-
46- let parent_to_remove = if let Some ( parent) = data. as_path ( ) . parent ( )
47- && !tokio:: fs:: try_exists ( parent) . await . unwrap_or ( true )
48- {
49- Some ( parent. to_path_buf ( ) )
50- } else {
51- None
52- } ;
53- Some ( Self { data, parent_to_remove } )
54- }
55-
56- async fn remove ( self ) {
57- let _ = tokio:: fs:: remove_dir_all ( & self . data ) . await ;
58- if let Some ( parent) = self . parent_to_remove {
59- // Remove only the parent that did not exist before probing. A
60- // concurrent file or directory makes this non-recursive removal
61- // fail and preserves the parent.
62- let _ = tokio:: fs:: remove_dir ( parent) . await ;
63- }
64- }
65- }
66-
6734/// Restrict DLL search to system32 only to prevent DLL hijacking
6835/// when the installer is run from a Downloads folder.
6936#[ cfg( windows) ]
@@ -185,10 +152,7 @@ async fn run(mut opts: cli::Options, dirs: VpDirs) -> i32 {
185152 }
186153
187154 let code = match do_install ( & opts, & dirs) . await {
188- Ok ( effective_dirs) => {
189- // do_install uses the monolithic root for a pre-split payload.
190- // Report the directories that it used.
191- let ( data_dir_display, bin_dir_display) = dir_displays ( & effective_dirs) ;
155+ Ok ( ( ) ) => {
192156 print_success ( & opts, & data_dir_display, & bin_dir_display) ;
193157 0
194158 }
@@ -207,23 +171,17 @@ async fn run(mut opts: cli::Options, dirs: VpDirs) -> i32 {
207171 code
208172}
209173
210- /// Install the resolved version and return the directories that the installer
211- /// used. The installer uses the monolithic root for a pre-split payload.
174+ /// Install the resolved version.
212175#[ allow( clippy:: print_stdout) ]
213- async fn do_install (
214- opts : & cli:: Options ,
215- dirs : & VpDirs ,
216- ) -> Result < VpDirs , Box < dyn std:: error:: Error > > {
217- let mut dirs = dirs. clone ( ) ;
176+ async fn do_install ( opts : & cli:: Options , dirs : & VpDirs ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
218177 let platform_suffix = platform:: detect_platform_suffix ( ) ?;
219178 if !opts. quiet {
220179 print_info ( & format ! ( "detected platform: {platform_suffix}" ) ) ;
221180 }
222181
223182 // Check local version first to potentially skip HTTP requests.
224183 // This operation is read-only. Create the install root only after the
225- // downloaded payload confirms the layout. Thus, a pre-split fallback does
226- // not leave empty split directories.
184+ // installer resolves and validates the target version.
227185 let current_version = install:: read_current_version ( & dirs. data ) . await ;
228186
229187 let version_or_tag = opts. version . as_deref ( ) . unwrap_or ( & opts. tag ) ;
@@ -235,6 +193,12 @@ async fn do_install(
235193 }
236194 let target_version =
237195 registry:: resolve_version_string ( version_or_tag, opts. registry . as_deref ( ) ) . await ?;
196+ if !vp_setup:: supports_split_layout ( & target_version) {
197+ return Err ( format ! (
198+ "vite-plus {target_version} is not supported by vp-setup. Install vite-plus 0.3.0 or later."
199+ )
200+ . into ( ) ) ;
201+ }
238202
239203 // Same version only if the binary is intact — a corrupted install needs a full reinstall.
240204 // `is_install_dir_for_version` also matches `{version}+force.*` dirs left by a forced
@@ -278,41 +242,6 @@ async fn do_install(
278242 }
279243 integrity:: verify_integrity ( & platform_data, & resolved. platform_integrity ) ?;
280244
281- // A pre-split release resolves every path from VP_HOME. Its default is
282- // ~/.vite-plus. Its environment setup, shims, and trampolines cannot
283- // use split roots. Use that monolithic root when the payload cannot
284- // report split category roots.
285- let legacy = VpDirs :: legacy_single_root ( & vp_shared:: EnvConfig :: get ( ) . user_home ) ;
286- let abandoned_split_data = if legacy. data == dirs. data {
287- // Pre-split and split-aware payloads use the same monolithic root
288- // here. Skip the probe because it extracts and starts the payload.
289- None
290- } else {
291- let split_data_cleanup = AbandonedSplitData :: capture ( dirs. data . clone ( ) ) . await ;
292- if let Some ( probed) = install:: probe_payload_dirs ( & platform_data) . await {
293- // Use the payload's resolution, as install.sh and install.ps1 do.
294- // This keeps the written layout equal to the resolved layout.
295- dirs = VpDirs :: from_resolved_parts (
296- probed. bin ,
297- probed. data ,
298- probed. cache ,
299- probed. config ,
300- probed. state ,
301- probed. layout ,
302- ) ;
303- None
304- } else {
305- if !opts. quiet {
306- print_info ( & format ! (
307- "vite-plus {target_version} does not support the split directory layout. Vite+ will install it in {}." ,
308- legacy. data. as_path( ) . display( )
309- ) ) ;
310- }
311- dirs = legacy;
312- split_data_cleanup
313- }
314- } ;
315-
316245 let install_dir = & dirs. data ;
317246 let version_dir = install_dir. join ( & target_version) ;
318247 tokio:: fs:: create_dir_all ( & version_dir) . await ?;
@@ -332,13 +261,6 @@ async fn do_install(
332261 let _ = tokio:: fs:: remove_dir_all ( & version_dir) . await ;
333262 }
334263
335- // The managed node and pnpm use paths from the process EnvConfig. The
336- // installer pinned this configuration before the payload selected the
337- // monolithic root. Remove the split data root and its empty application
338- // parent if this run created them.
339- if let Some ( split_data) = abandoned_split_data {
340- split_data. remove ( ) . await ;
341- }
342264 result?;
343265 }
344266
@@ -371,7 +293,7 @@ async fn do_install(
371293 }
372294 }
373295
374- Ok ( dirs )
296+ Ok ( ( ) )
375297}
376298
377299/// Auto-detect whether the Node.js version manager should be enabled.
@@ -489,6 +411,10 @@ async fn install_new_version(
489411 if !tokio:: fs:: try_exists ( & binary_path) . await . unwrap_or ( false ) {
490412 return Err ( "Binary not found after extraction. The download may be corrupted." . into ( ) ) ;
491413 }
414+ #[ cfg( windows) ]
415+ if !tokio:: fs:: try_exists ( version_dir. join ( "bin" ) . join ( "vp-shim.exe" ) ) . await . unwrap_or ( false ) {
416+ return Err ( "Trampoline not found after extraction. The download may be corrupted." . into ( ) ) ;
417+ }
492418
493419 install:: generate_wrapper_package_json ( version_dir, version) . await ?;
494420
@@ -545,17 +471,8 @@ async fn setup_bin_shims(dirs: &VpDirs) -> Result<(), Box<dyn std::error::Error>
545471 let shim_src = dirs. data . join ( "current" ) . join ( "bin" ) . join ( "vp-shim.exe" ) ;
546472 let shim_dst = bin_dir. join ( "vp.exe" ) ;
547473
548- // Prefer vp-shim.exe (trampoline); fall back to vp.exe for pre-trampoline releases
549- let src = if tokio:: fs:: try_exists ( & shim_src) . await . unwrap_or ( false ) {
550- shim_src
551- } else {
552- dirs. data . join ( "current" ) . join ( "bin" ) . join ( "vp.exe" )
553- } ;
554-
555- if tokio:: fs:: try_exists ( & src) . await . unwrap_or ( false ) {
556- replace_windows_exe ( & src, & shim_dst, & bin_dir) . await ?;
557- dirs. write_shim_pointer ( "vp" ) ?;
558- }
474+ replace_windows_exe ( & shim_src, & shim_dst, & bin_dir) . await ?;
475+ dirs. write_shim_pointer ( "vp" ) ?;
559476
560477 // Best-effort cleanup of old shim files
561478 if let Ok ( mut entries) = tokio:: fs:: read_dir ( & bin_dir) . await {
@@ -968,32 +885,4 @@ mod tests {
968885 }
969886 } ) ;
970887 }
971-
972- #[ tokio:: test]
973- async fn abandoned_split_data_cleanup_removes_new_empty_parent ( ) {
974- let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
975- let parent = tmp. path ( ) . join ( "vite-plus" ) ;
976- let data = AbsolutePathBuf :: new ( parent. join ( "data" ) ) . unwrap ( ) ;
977- let cleanup = AbandonedSplitData :: capture ( data. clone ( ) ) . await . unwrap ( ) ;
978-
979- tokio:: fs:: create_dir_all ( & data) . await . unwrap ( ) ;
980- cleanup. remove ( ) . await ;
981-
982- assert ! ( !parent. exists( ) ) ;
983- }
984-
985- #[ tokio:: test]
986- async fn abandoned_split_data_cleanup_preserves_existing_parent ( ) {
987- let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
988- let parent = tmp. path ( ) . join ( "vite-plus" ) ;
989- std:: fs:: create_dir ( & parent) . unwrap ( ) ;
990- let data = AbsolutePathBuf :: new ( parent. join ( "data" ) ) . unwrap ( ) ;
991- let cleanup = AbandonedSplitData :: capture ( data. clone ( ) ) . await . unwrap ( ) ;
992-
993- tokio:: fs:: create_dir_all ( & data) . await . unwrap ( ) ;
994- cleanup. remove ( ) . await ;
995-
996- assert ! ( parent. is_dir( ) ) ;
997- assert ! ( !data. as_path( ) . exists( ) ) ;
998- }
999888}
0 commit comments