Skip to content

Commit 5b3a50f

Browse files
liangmiQwQfengmk2
andauthored
feat(cli): check managed global outdated packages (#1659)
Implemented the outdated part of #1656 by routing `vp outdated -g` through Vite+'s managed global package metadata instead of delegating to the underlying `npm outdated -g` store. This PR includes a refactor for global package commands. This is intentional, taking into account that commands like `outdated` and `install` have many shared utils, putting all of them inside `env` module will reduce maintainability. Also modify `vp update -g` to reuse the utilities added in this PR. 🤖 Generated with Codex --------- Co-authored-by: MK (fengmk2) <fengmk2@gmail.com>
1 parent 9bd2a4e commit 5b3a50f

24 files changed

Lines changed: 667 additions & 329 deletions

File tree

crates/vite_global_cli/src/cli.rs

Lines changed: 54 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,13 @@ use vite_pm_cli::PackageManagerCommand;
1313
use vite_shared::output;
1414

1515
use crate::{
16-
commands::{
17-
self,
18-
env::{global_install, package_metadata::PackageMetadata},
19-
},
16+
commands::{self, env::package_metadata::PackageMetadata, global},
2017
error::Error,
2118
help,
2219
};
2320

2421
const DEFAULT_GLOBAL_INSTALL_CONCURRENCY: usize = 5;
22+
const DEFAULT_GLOBAL_VIEW_CONCURRENCY: usize = 3 * DEFAULT_GLOBAL_INSTALL_CONCURRENCY;
2523

2624
#[derive(Clone, Copy, Debug)]
2725
pub struct RenderOptions {
@@ -552,7 +550,7 @@ fn run_tasks_completions(current: &OsStr) -> Vec<clap_complete::CompletionCandid
552550
/// Handle a parsed package-manager command.
553551
///
554552
/// `Install`/`Add`/`Update`/`Remove` invoked with `-g`/`--global` are routed
555-
/// through the vite-plus-managed Node.js install store (`commands::env`).
553+
/// through the vite-plus-managed Node.js install store (`commands::global`).
556554
/// Everything else is forwarded to `vite_pm_cli::dispatch`, which executes
557555
/// the underlying package manager (pnpm/npm/yarn/bun).
558556
async fn run_package_manager_command(
@@ -581,13 +579,30 @@ async fn run_package_manager_command(
581579
managed_update(packages, concurrency).await
582580
}
583581

582+
PackageManagerCommand::Outdated {
583+
global: true,
584+
ref packages,
585+
long,
586+
format,
587+
concurrency,
588+
..
589+
} => {
590+
global::outdated::execute(
591+
packages,
592+
long,
593+
format,
594+
concurrency.unwrap_or(DEFAULT_GLOBAL_VIEW_CONCURRENCY),
595+
)
596+
.await
597+
}
598+
584599
// `pm list -g` lists vite-plus-managed globals, not the underlying PM's.
585600
PackageManagerCommand::Pm(vite_pm_cli::cli::PmCommands::List {
586601
global: true,
587602
json,
588603
ref pattern,
589604
..
590-
}) => crate::commands::env::packages::execute(json, pattern.as_deref()).await,
605+
}) => global::packages::execute(json, pattern.as_deref()).await,
591606

592607
cmd => {
593608
commands::prepend_js_runtime_to_path_env(&cwd).await?;
@@ -602,7 +617,7 @@ async fn managed_install(
602617
force: bool,
603618
concurrency: Option<usize>,
604619
) -> Result<ExitStatus, Error> {
605-
if let Err((package_name, error)) = crate::commands::env::global_install::install(
620+
if let Err((package_name, error)) = global::install::install(
606621
packages,
607622
node,
608623
force,
@@ -623,106 +638,68 @@ async fn managed_install(
623638

624639
async fn managed_uninstall(packages: &[String], dry_run: bool) -> Result<ExitStatus, Error> {
625640
for package in packages {
626-
if let Err(e) = crate::commands::env::global_install::uninstall(package, dry_run).await {
641+
if let Err(e) = global::install::uninstall(package, dry_run).await {
627642
vite_shared::output::raw_stderr(&format!("Failed to uninstall {package}: {e}"));
628643
return Ok(exit_status(1));
629644
}
630645
}
631646
Ok(ExitStatus::default())
632647
}
633648

634-
fn is_global_package_up_to_date(installed_version: &str, registry_version: &str) -> bool {
635-
installed_version.trim() == registry_version.trim()
636-
}
637-
638649
async fn managed_update(
639650
packages: &[String],
640651
concurrency: Option<usize>,
641652
) -> Result<ExitStatus, Error> {
642-
let all_packages = if packages.is_empty() {
653+
let concurrency = concurrency.unwrap_or(DEFAULT_GLOBAL_INSTALL_CONCURRENCY);
654+
let mut to_update: Vec<String> = Vec::new();
655+
656+
let packages = if packages.is_empty() {
643657
let all = PackageMetadata::list_all().await?;
644658
if all.is_empty() {
645659
vite_shared::output::raw("No global packages installed.");
646660
return Ok(ExitStatus::default());
647661
}
648-
Some(all)
649-
} else {
650-
None
651-
};
652662

653-
let mut to_update: Vec<String> = Vec::new();
654-
let mut skipped = 0usize;
655-
656-
if let Some(all) = all_packages {
657-
for metadata in all {
658-
match global_install::latest_package_version(&metadata.name).await {
659-
Ok(latest_version)
660-
if is_global_package_up_to_date(&metadata.version, &latest_version) =>
661-
{
662-
vite_shared::output::raw(&format!(
663-
"{} is already up to date (v{}).",
664-
metadata.name, metadata.version
665-
));
666-
skipped += 1;
667-
}
668-
Ok(_) => to_update.push(metadata.name.clone()),
669-
Err(e) => {
670-
vite_shared::output::raw_stderr(&format!(
671-
"Could not check latest version for {}: {e}; updating anyway.",
672-
metadata.name
673-
));
674-
to_update.push(metadata.name.clone());
675-
}
676-
}
677-
}
663+
None
678664
} else {
665+
let mut managed_specs = Vec::new();
666+
679667
for package in packages {
680-
if global_install::is_local_package_spec(package) {
668+
// Always update local packages
669+
if global::is_local_package_spec(package) {
681670
to_update.push(package.clone());
682671
continue;
683672
}
684673

685-
let (package_name, _) = global_install::parse_package_spec(package);
686-
if let Some(metadata) = PackageMetadata::load(&package_name).await? {
687-
match global_install::latest_package_version(package).await {
688-
Ok(latest_version)
689-
if is_global_package_up_to_date(&metadata.version, &latest_version) =>
690-
{
691-
vite_shared::output::raw(&format!(
692-
"{} is already up to date (v{}).",
693-
package_name, metadata.version
694-
));
695-
skipped += 1;
696-
continue;
697-
}
698-
Ok(_) => {}
699-
Err(e) => {
700-
vite_shared::output::raw_stderr(&format!(
701-
"Could not check latest version for {package}: {e}; updating anyway."
702-
));
703-
}
704-
}
674+
let (package_name, _) = global::parse_package_spec(package);
675+
if PackageMetadata::load(&package_name).await?.is_some() {
676+
managed_specs.push(package.clone());
677+
} else {
678+
to_update.push(package.clone());
705679
}
706-
to_update.push(package.clone());
707680
}
708-
}
681+
682+
Some(managed_specs)
683+
};
684+
to_update.extend(
685+
global::outdated::get_outdated_packages(
686+
&packages.unwrap_or(Vec::new()),
687+
concurrency * 3,
688+
true,
689+
)
690+
.await?
691+
.into_iter()
692+
.map(|package| package.spec.unwrap_or(package.name)),
693+
);
709694

710695
if to_update.is_empty() {
711-
if skipped > 0 {
712-
vite_shared::output::raw("All global packages are up to date.");
713-
}
696+
vite_shared::output::raw("All global packages are up to date.");
714697
return Ok(ExitStatus::default());
715698
}
716699

717700
// Call reinstall logic
718-
if let Err((package_name, error)) = global_install::install(
719-
&to_update,
720-
None,
721-
false,
722-
concurrency.unwrap_or(DEFAULT_GLOBAL_INSTALL_CONCURRENCY),
723-
true,
724-
)
725-
.await
701+
if let Err((package_name, error)) =
702+
global::install::install(&to_update, None, false, concurrency, true).await
726703
{
727704
output::error(&format!(
728705
"Failed to update {}: {error}",
@@ -975,20 +952,10 @@ pub fn try_parse_args_from_with_options(
975952
#[cfg(test)]
976953
mod tests {
977954
use super::{
978-
has_flag_before_terminator, is_global_package_up_to_date, should_force_global_delegate,
955+
has_flag_before_terminator, should_force_global_delegate,
979956
should_suppress_header_for_subcommand,
980957
};
981958

982-
#[test]
983-
fn skips_global_update_when_registry_and_node_versions_match() {
984-
assert!(is_global_package_up_to_date("5.9.3", "5.9.3"));
985-
}
986-
987-
#[test]
988-
fn updates_global_package_when_registry_version_differs_from_installed_version() {
989-
assert!(!is_global_package_up_to_date("5.9.2", "5.9.3"));
990-
}
991-
992959
#[test]
993960
fn detects_flag_before_option_terminator() {
994961
assert!(has_flag_before_terminator(

crates/vite_global_cli/src/commands/env/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ mod current;
99
mod default;
1010
mod doctor;
1111
mod exec;
12-
pub mod global_install;
1312
mod list;
1413
mod list_remote;
1514
mod off;
1615
mod on;
1716
pub mod package_metadata;
18-
pub mod packages;
1917
mod pin;
2018
mod setup;
2119
mod unpin;
@@ -24,6 +22,8 @@ mod which;
2422

2523
use std::process::ExitStatus;
2624

25+
#[cfg(windows)]
26+
pub(crate) use setup::{cleanup_legacy_windows_shim, get_trampoline_path, remove_or_rename_to_old};
2727
use vite_path::AbsolutePathBuf;
2828

2929
use crate::{

0 commit comments

Comments
 (0)