feat(shim): improve npm global install experience#708
Merged
Conversation
✅ Deploy Preview for viteplus-staging canceled.
|
Member
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
c1234de to
9449ff0
Compare
npm install -g in shim
VP-217 vp env: active Node bin not added to PATH
Problem
User impact
Expected
Context
|
fengmk2
commented
Mar 6, 2026
fengmk2
commented
Mar 6, 2026
fengmk2
commented
Mar 6, 2026
fengmk2
commented
Mar 6, 2026
hyf0
approved these changes
Mar 6, 2026
1871f6d to
3ab3849
Compare
When Vite+ manages Node via shims, `npm install -g <pkg>` installs binaries into the managed Node's bin dir which is not on PATH. The binaries are silently unreachable. After `npm install -g` completes successfully, the shim now checks whether installed binaries are reachable from PATH. If not: - Interactive mode: prompts user to create a link in ~/.vite-plus/bin/ - Non-interactive mode: creates links automatically - Always prints a tip suggesting `vp install -g` instead Uses spawn+wait instead of exec for `npm install -g` specifically so post-install checks can run. All other npm commands continue using exec for zero overhead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-binary reachability check with PATH-scope check. The previous implementation only checked if binaries existed in ~/.vite-plus/bin/, which missed cases where users set a custom npm prefix (e.g., NPM_CONFIG_PREFIX) whose bin dir is already on PATH. Now uses `npm config get prefix` to determine the actual npm global prefix, derives the bin dir from it, and checks if it's on the user's original PATH. If yes, binaries are reachable and no shim/hint is needed. If not, creates shims and shows the tip as before. Also adds snap tests for custom npm prefix scenarios (both on-PATH and off-PATH).
Use `output::info` and `output::error` from `vite_shared::output` instead
of ad-hoc `eprintln!("vp: ...")` calls in `create_bin_link` and
`check_npm_global_install_result`. This aligns with the project's logging
standards (`info:`, `warn:`, `error:` prefixes).
Also adds `output.status.success()` check to `npm config get prefix`
fallback to avoid using error output as a prefix path.
npm's @npmcli/redact matches hyphenated UUID patterns (e.g., `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) and treats any config value containing them as a secret, causing `npm config get prefix` to fail with "The prefix option is protected". Removing hyphens from the UUID avoids this pattern match. This also removes the NPM_CONFIG_PREFIX env var workaround in `get_npm_global_prefix`, since `npm config get prefix` now works correctly without UUID-like segments in the path.
- Pipe npm stdout/stderr instead of inheriting, so install noise is hidden on success and only shown on failure for debugging - Add --no-fund flag to suppress npm funding messages - Replace raw println! with output::raw/output::warn for consistent CLI output formatting - Remove unnecessary two-space prefix from all install/uninstall messages - Update RFC examples to match new output format
When `npm install -g` encounters a binary that's already managed by `vp install -g` (detected via BinConfig), warn the user instead of silently skipping. When `npm uninstall -g` runs, skip removal of vp-managed shims to prevent breaking managed installations. Also adds npm global uninstall support (parse, collect bins before uninstall, remove links after) and cleans up CLI output formatting.
- Unify `parse_npm_global_install` and `parse_npm_global_uninstall` into a shared `parse_npm_global_command` with parameterized subcommands - Merge identical `NpmGlobalInstall`/`NpmGlobalUninstall` into `NpmGlobalCommand` - Extract `read_npm_package_json` helper for the duplicated package.json reading with npm-prefix-then-node-dir fallback - Extract `collect_bin_names_from_npm` to share bin collection logic between install and uninstall flows Net: -48 lines with no behavior change.
- Add BinSource enum (Vp/Npm) to BinConfig with backward-compatible serde default, so npm-created links are distinguishable from vp-managed shims and user-owned binaries - Record BinConfig with source:"npm" when create_bin_link() succeeds - Only remove links with source:"npm" during npm uninstall -g cleanup, protecting user-owned binaries and vp-managed shims - Add sync methods (load_sync/save_sync/delete_sync) to BinConfig using direct error-kind matching instead of exists() pre-checks - Capture --prefix/--prefix=value from npm CLI args and use it for package.json lookups and bin dir resolution - Detect Windows drive-letter paths (C:\...) as local package specs - Add snap tests for preexisting binary protection and --prefix support
…pm uninstall Fix two defects in npm global install/uninstall shim handling: 1. resolve_npm_prefix() now resolves relative --prefix values (e.g., ./custom) against cwd instead of silently falling back to the default npm prefix. Uses cwd.join(prefix) which handles both relative and absolute paths correctly. 2. remove_npm_global_uninstall_links() now checks both source == Npm AND package ownership before removing a link. This prevents uninstalling package A from removing a bin link that was overwritten by package B. collect_bin_names_from_npm() returns (bin_name, package_name) pairs, and check_npm_global_install_result() updates BinConfig ownership when a force-install overwrites an existing npm link.
- Use output::error instead of eprintln! in spawn_tool() (exec.rs) - Save node_version in BinConfig::new_npm() so npm-installed packages track which Node.js version was used - Remove "Uninstalling..." log from vp remove -g (fast enough to skip) - Update snap tests for the above changes
- Use to_absolute_path_buf() instead of to_path_buf() for AbsolutePath - Dereference vite_str::Str before passing to std::fs::write
…utput" This reverts commit 3f14602.
Add `after` step to uninstall the npm global package so subsequent runs don't see "changed" instead of "added" from residual state.
7e902fa to
dfd8ffd
Compare
When `npm install -g pkg-a pkg-b` and both packages declare the same binary name, missing_bins gets duplicate entries. The second create_bin_link call fails (EEXIST) so BinConfig is never saved for the last package, leaving stale ownership. Dedup by bin_name keeping the last entry to match npm's "last writer wins" behavior.
2e83449 to
449380c
Compare
- Extract `exit_code_from_status()` in exec.rs to return 128+signal on Unix when a child is killed by a signal, instead of always returning 1 - Make the `exists()` early-return in `remove_npm_global_uninstall_links` Unix-only, since on Windows shim files are regular files that always exist regardless of target validity - Clean up stale .cmd files on Windows during repair
449380c to
1824df7
Compare
cpojer
approved these changes
Mar 6, 2026
…d snap test Narrow CI to only run the failing snap test case and add debug eprintln! calls at every decision point in check_npm_global_install_result to identify why the "Skipped" message is not being output.
fengmk2
commented
Mar 7, 2026
The snap test only captures stdout, so eprintln debug output was invisible. Switch all debug logs to println so they appear in the snap test output.
Log the full dispatch context before check_npm_global_install_result: parse result, args, exit code, home dir, npm_prefix, and node_dir.
- Remove all debug println! from dispatch.rs - Bump NAPI cache key v3→v4 to force binary rebuild on CI - Restore CI to run full test suite with git diff --exit-code - Mark vp ls -g output as ignoreOutput (pnpm presence varies) - Normalize npm tree characters (ASCII `` `-- `` → Unicode └──)
… install - Add tracing::debug! at every decision point in dispatch and check_npm_global_install_result - Set VITE_LOG=trace in CI snap test env to capture tracing output - Run only npm-global-install-hint snap test to isolate the issue
fengmk2
commented
Mar 7, 2026
When pnpm runs via the vp shim, vp sets VITE_PLUS_TOOL_RECURSION=1 before exec to prevent infinite shim loops. This env var leaked into snap test subprocesses (matched by the VITE_* passthrough pattern), causing every npm/node command to bypass managed shim dispatch and fall through to system binaries — hiding the "Linked" messages. Also reverts all debugging artifacts (tracing, --verbose, VITE_LOG, cache key bump) and removes the overly broad npm tree character normalization that corrupted cowsay ASCII art.
The exec unit tests spawned `sh` without an absolute path, which fails on CI macOS runners where PATH may not include /bin during cargo test. Use `/bin/sh` on Unix and `cmd` on Windows instead.
c2c8c84 to
0e6559d
Compare
fengmk2
commented
Mar 7, 2026
…nstall `is_local_path()` only checked for `./`, `../`, `/`, and Windows drive paths. Bare `.` and `..` are valid npm local path specs but were treated as package names, so `npm install -g .` failed to create a fallback link.
- Add `output::raw_inline()` for no-newline stdout output - Replace `print!` prompt in dispatch.rs with `output::raw_inline()` - Replace `eprintln!` in exec_unix with `output::error()` - Use HashSet instead of Vec for O(n) dedup in `dedup_missing_bins()`
Member
Author
This was referenced Mar 7, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
npm install -g: When Vite+ manages Node via shims,npm install -g <pkg>installs binaries into the managed Node's bin dir which is not on PATH. After install, the shim now checks whether installed binaries are reachable and offers to create links in~/.vite-plus/bin/, plus a tip suggestingvp install -ginstead.vp install -g: Pipe npm stdout/stderr so install noise is hidden on success, only shown on failure for debugging. Add--no-fundflag.println!withoutput::raw/output::warn, remove unnecessary two-space prefix from install/uninstall messages.Test plan
cargo test -p vite_global_cli— unit tests passcargo clippy -p vite_global_cli— no new warningspnpm bootstrap-cli && vp install -g cowsay— verify clean outputcloses VP-217