@@ -220,47 +220,22 @@ async fn do_install(
220220 let version_dir = install_dir. join ( & target_version) ;
221221 tokio:: fs:: create_dir_all ( & version_dir) . await ?;
222222
223- if !opts. quiet {
224- print_info ( "extracting binary..." ) ;
225- }
226- install:: extract_platform_package ( & platform_data, & version_dir) . await ?;
227-
228- let binary_path = version_dir. join ( "bin" ) . join ( VP_BINARY_NAME ) ;
229- if !tokio:: fs:: try_exists ( & binary_path) . await . unwrap_or ( false ) {
230- return Err ( "Binary not found after extraction. The download may be corrupted." . into ( ) ) ;
231- }
232-
233- install:: generate_wrapper_package_json ( & version_dir, & target_version) . await ?;
234-
235- if !opts. quiet {
236- print_info ( "installing dependencies (this may take a moment)..." ) ;
237- }
238- install:: install_production_deps (
223+ let result = install_new_version (
224+ opts,
225+ & platform_data,
239226 & version_dir,
240- opts . registry . as_deref ( ) ,
241- opts . yes ,
227+ install_dir ,
228+ & target_version ,
242229 & resolved. version ,
230+ current_version. is_some ( ) ,
243231 )
244- . await ?;
245-
246- let previous_version = if current_version. is_some ( ) {
247- install:: save_previous_version ( install_dir) . await ?
248- } else {
249- None
250- } ;
251- install:: swap_current_link ( install_dir, & target_version) . await ?;
232+ . await ;
252233
253- // Cleanup with both new and previous versions protected (matches vp upgrade)
254- let mut protected = vec ! [ target_version. as_str( ) ] ;
255- if let Some ( ref prev) = previous_version {
256- protected. push ( prev. as_str ( ) ) ;
257- }
258- if let Err ( e) =
259- install:: cleanup_old_versions ( install_dir, vite_setup:: MAX_VERSIONS_KEEP , & protected)
260- . await
261- {
262- print_warn ( & format ! ( "Old version cleanup failed (non-fatal): {e}" ) ) ;
234+ // On failure, clean up the partial version directory (matches vp upgrade behavior)
235+ if result. is_err ( ) {
236+ let _ = tokio:: fs:: remove_dir_all ( & version_dir) . await ;
263237 }
238+ result?;
264239 }
265240
266241 // --- Post-activation setup (always runs, even for same-version repair) ---
@@ -347,6 +322,58 @@ fn auto_detect_node_manager(install_dir: &vite_path::AbsolutePath, interactive:
347322 interactive
348323}
349324
325+ /// Extract, install deps, and activate a new version. Separated so the caller
326+ /// can clean up the version directory on failure.
327+ async fn install_new_version (
328+ opts : & cli:: Options ,
329+ platform_data : & [ u8 ] ,
330+ version_dir : & AbsolutePathBuf ,
331+ install_dir : & AbsolutePathBuf ,
332+ target_version : & str ,
333+ resolved_version : & str ,
334+ has_previous : bool ,
335+ ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
336+ if !opts. quiet {
337+ print_info ( "extracting binary..." ) ;
338+ }
339+ install:: extract_platform_package ( platform_data, version_dir) . await ?;
340+
341+ let binary_path = version_dir. join ( "bin" ) . join ( VP_BINARY_NAME ) ;
342+ if !tokio:: fs:: try_exists ( & binary_path) . await . unwrap_or ( false ) {
343+ return Err ( "Binary not found after extraction. The download may be corrupted." . into ( ) ) ;
344+ }
345+
346+ install:: generate_wrapper_package_json ( version_dir, target_version) . await ?;
347+
348+ if !opts. quiet {
349+ print_info ( "installing dependencies (this may take a moment)..." ) ;
350+ }
351+ install:: install_production_deps (
352+ version_dir,
353+ opts. registry . as_deref ( ) ,
354+ opts. yes ,
355+ resolved_version,
356+ )
357+ . await ?;
358+
359+ let previous_version =
360+ if has_previous { install:: save_previous_version ( install_dir) . await ? } else { None } ;
361+ install:: swap_current_link ( install_dir, target_version) . await ?;
362+
363+ // Cleanup with both new and previous versions protected (matches vp upgrade)
364+ let mut protected = vec ! [ target_version] ;
365+ if let Some ( ref prev) = previous_version {
366+ protected. push ( prev. as_str ( ) ) ;
367+ }
368+ if let Err ( e) =
369+ install:: cleanup_old_versions ( install_dir, vite_setup:: MAX_VERSIONS_KEEP , & protected) . await
370+ {
371+ print_warn ( & format ! ( "Old version cleanup failed (non-fatal): {e}" ) ) ;
372+ }
373+
374+ Ok ( ( ) )
375+ }
376+
350377/// Windows locks running `.exe` files — rename the old one out of the way before copying.
351378#[ cfg( windows) ]
352379async fn replace_windows_exe (
0 commit comments