Skip to content

Commit f773b89

Browse files
authored
Adds a spinner when performing a lazy install (#275)
Most Yarn commands now silently run installs under the hood when they detect the project isn't in sync compared to the last modification time of package.json. Only problem is that it's a little _too_ silent and it may look like the command is unexpectedly hanging. To address this we now display a spinner if the install takes more than 200ms. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches the async lazy-install control flow with a new `tokio::select!` timing branch and threaded terminal output, which could affect install completion timing or terminal behavior in edge cases. > > **Overview** > Improves UX for `lazy_install` by showing a spinner message ("Installing dependencies...") **only when running in a TTY** and the install hasn’t finished within ~200ms; non-terminal contexts still run silently. > > Refactors `lazy_install` to pin the install future, race it against a short sleep via `tokio::select!`, start/stop a `start_progress` spinner around the remaining work, and updates the progress thread to flush immediately after hiding the cursor to make terminal state changes visible promptly. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 328144f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 93452b3 commit f773b89

2 files changed

Lines changed: 37 additions & 7 deletions

File tree

packages/zpm-utils/src/progress.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,13 @@ where
7575
let mut frame_idx: usize
7676
= 0;
7777

78-
let mut stdout
79-
= std::io::stdout().lock();
78+
{
79+
let mut stdout
80+
= std::io::stdout().lock();
8081

81-
stdout.write_all(b"\x1b[?25l").ok();
82+
stdout.write_all(b"\x1b[?25l").ok();
83+
stdout.flush().ok();
84+
}
8285

8386
loop {
8487
match stop_rx.recv_timeout(Duration::from_millis(50)) {

packages/zpm/src/project.rs

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::{collections::{BTreeMap, BTreeSet, HashSet}, io::ErrorKind, sync::Arc, time::UNIX_EPOCH};
1+
use std::{collections::{BTreeMap, BTreeSet, HashSet}, io::ErrorKind, sync::Arc, time::{Duration, UNIX_EPOCH}};
22

33
use globset::{GlobBuilder, GlobSetBuilder};
44
use zpm_config::{Configuration, ConfigurationContext};
55
use zpm_macro_enum::zpm_enum;
66
use zpm_parsers::JsonDocument;
77
use zpm_primitives::{Descriptor, Ident, Locator, Range, Reference, WorkspaceIdentReference, WorkspaceMagicRange, WorkspacePathReference};
88
use zpm_tasks::{parse as parse_taskfile, ResolvedTasks, TaskId};
9-
use zpm_utils::{Glob, LastModifiedAt, Path, ToFileString, ToHumanString};
9+
use zpm_utils::{Glob, LastModifiedAt, Path, ToFileString, ToHumanString, is_terminal, start_progress};
1010
use serde::Deserialize;
1111
use zpm_formats::zip::ZipSupport;
1212

@@ -764,7 +764,7 @@ impl Project {
764764
}
765765
}
766766

767-
self.run_install(RunInstallOptions {
767+
let install = self.run_install(RunInstallOptions {
768768
check_checksums: false,
769769
check_resolutions: false,
770770
enforced_resolutions: BTreeMap::new(),
@@ -774,7 +774,34 @@ impl Project {
774774
mode: None,
775775
roots: None,
776776
..Default::default()
777-
}).await?;
777+
});
778+
779+
tokio::pin!(install);
780+
781+
if !is_terminal() {
782+
install.await?;
783+
return Ok(());
784+
}
785+
786+
tokio::select! {
787+
result = &mut install => {
788+
result?;
789+
return Ok(());
790+
},
791+
792+
_ = tokio::time::sleep(Duration::from_millis(200)) => {
793+
}
794+
}
795+
796+
let mut progress_handle
797+
= start_progress(|_| "Installing dependencies...".to_string());
798+
799+
let install_result
800+
= install.await;
801+
802+
progress_handle.stop();
803+
804+
install_result?;
778805

779806
Ok(())
780807
}

0 commit comments

Comments
 (0)