Skip to content

feat: passthrough fallback for vpr/vp i on low Node (#1909 #1916)#1921

Open
l246804 wants to merge 19 commits into
voidzero-dev:mainfrom
l246804:feat/low-node-passthrough
Open

feat: passthrough fallback for vpr/vp i on low Node (#1909 #1916)#1921
l246804 wants to merge 19 commits into
voidzero-dev:mainfrom
l246804:feat/low-node-passthrough

Conversation

@l246804

@l246804 l246804 commented Jun 23, 2026

Copy link
Copy Markdown

Summary

Closes #1909 (low-Node passthrough). Refs #1916.

Changes

Crate Change
vite_shared Add MIN_SUPPORTED_NODE = "20.19.0" constant + is_node_below_min() helper
vite_install Add PackageManagerBuilder::detect_only() — builds PM without writing devEngines
vite_pm_cli Extract dispatch_with_pm() + 12 *_with_pm handler variants for external PM injection
vite_global_cli Add passthrough module (is_eligible, should_passthrough, execute) + precheck in run_command_with_options

How it works

  1. In run_command_with_options, before the command match, check if the command is eligible (Run / PackageManager) and the project's resolved Node < 20.19.0.
  2. If yes: print a one-line notice, resolve the PM via detect_only (no devEngines pin), and dispatch via dispatch_with_pm (PM commands) or run_command (Run commands).
  3. If no: original path unchanged.

Test plan

  • cargo test -p vite_shared --lib → 92 passed
  • cargo test -p vite_install --lib → 552 passed
  • cargo test -p vite_pm_cli --lib → 12 passed
  • cargo test -p vite_global_cli --bin vp passthrough → 6 passed
  • cargo clippy on all 4 crates → clean
  • Manual: .node-version=14.15.0 + vpr/vp i passthrough, no devEngines written
  • pnpm bootstrap-cli && pnpm test && git status (requires cmake)

leihaohao added 15 commits June 23, 2026 12:08
…ch in passthrough

Task 1 follow-up: the initial commit (9e7ddfd) omitted the node-semver
workspace dependency in vite_shared/Cargo.toml and used `let Some(...)` to
match node_semver::Version::parse, which returns Result (not Option).
- Add node-semver = { workspace = true } to vite_shared dependencies
- Use let Ok(...) for both Version::parse calls (actual + min)
- Plan doc updated to match (node_semver returns Result, not Option)

Verified: RUSTC_BOOTSTRAP=1 cargo test -p vite_shared --lib passthrough -> 4 passed
- 12 run_* functions each get a run_*_with_pm variant that accepts an
  external &PackageManager and calls pm.run_*_command directly (no build).
- Original run_* becomes a thin wrapper: build + delegate to *_with_pm.
- dispatch_with_pm copies dispatch's match body but calls *_with_pm variants.
- build_for_dispatch picks the right build function per command.
- dispatch now delegates: build_for_dispatch + dispatch_with_pm.
- Add tempfile dev-dep for the new unit test.

Verified: RUSTC_BOOTSTRAP=1 cargo test -p vite_pm_cli --lib -> 12 passed.
…+ dispatch_with_pm

- execute() resolves the project's package manager via detect_only (no
  devEngines pin), then dispatches:
  - Commands::PackageManager: delegates to vite_pm_cli::dispatch_with_pm,
    reusing existing resolve_* parameter generation (zero drift).
  - Commands::Run: builds <pm> run <script> args manually and runs via
    run_command (nr-style), since resolve_install_command auto-prepends
    'install'.
- map_pm_error maps UnrecognizedPackageManager to a user-friendly message.
- build_passthrough_envs prepends the node runtime bin to PATH.
- Uses pm.bin_name directly (not resolve_install_command which prepends
  'install') to correctly handle run <script>.

Verified: RUSTC_BOOTSTRAP=1 cargo test -p vite_global_cli --bin vp passthrough
-> 4 passed.
Task 4a (Critical): Fix Dlx npx fallback regression on original dispatch
path. When vp dlx runs without a package.json, the original code fell back
to npx. The refactored dispatch propagated PackageJsonNotFound through
build_for_dispatch, making the fallback dead code. Fix: special-case Dlx
in dispatch to call handlers::run_dlx directly (preserving the fallback).

Task 4b (Medium): Add missing tests for build_passthrough_envs and
map_pm_error (plan requires them). Remove dead code merge_envs (never
called — resolve_install_command doesn't exist, so Run arm uses pm.bin_name
directly). Add #[allow(dead_code)] on execute (was lost during placeholder
replacement). Add AbsolutePathBuf import in test module.

Verified:
- RUSTC_BOOTSTRAP=1 cargo test -p vite_pm_cli --lib -> 12 passed
- RUSTC_BOOTSTRAP=1 cargo test -p vite_global_cli --bin vp passthrough -> 6 passed
- RUSTC_BOOTSTRAP=1 cargo clippy -p vite_pm_cli -- -D warnings -> clean
- RUSTC_BOOTSTRAP=1 cargo clippy -p vite_global_cli --bin vp -- -D warnings -A clippy::result_large_err -> clean
@netlify

netlify Bot commented Jun 23, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview ready!

Name Link
🔨 Latest commit 5cfcafa
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a3b44e753c2f300087db107
😎 Deploy Preview https://deploy-preview-1921--viteplus-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@liangmiQwQ liangmiQwQ self-requested a review June 23, 2026 13:46

@liangmiQwQ liangmiQwQ left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

plans/2026-06-23-low-node-passthrough.md‎ and specs/2026-06-23-low-node-passthrough-design.md‎ seem left by mistake?

Could you please also clean up outdated comments and dead code like Wired in Task X?

#[allow(dead_code)] // wired up in Task 4
fn print_passthrough_notice(node_version: &str, min: &str) {
// Reuse shared output style; keep to a single concise line.
vite_shared::output::info(&format!(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
vite_shared::output::info(&format!(
vite_shared::output::warn(&format!(

Is it better to show a warning?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I haven't tested this PR locally yet; it was mistakenly submitted by AI yesterday.

@liangmiQwQ

Copy link
Copy Markdown
Collaborator

I tested locally, and I found it only works for vp run command but not for vpr command. We'd better handle passthrough fallback for shims as well.

@codex

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@liangmiQwQ

Copy link
Copy Markdown
Collaborator

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d290e8af8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

} else {
// npm, no explicit version: use Node-bundled npm.
(
node_bin_prefix.to_absolute_path_buf(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the runtime install dir for bundled npm

When low-Node passthrough resolves to npm without an explicit packageManager, node_bin_prefix is already the directory containing npm, but it is stored as install_dir. Package-manager commands later call PackageManager::get_bin_prefix(), which appends another /bin, so vp i in this advertised npm fallback sets PATH to <node>/bin/bin and cannot find the bundled npm. Store the runtime install directory here or avoid appending bin for this case.

Useful? React with 👍 / 👎.

// name, PATH = node runtime bin (for the pm shim) + pm's bin dir.
let mut full_args = vec!["run".to_string()];
full_args.extend(args.iter().cloned());
let envs = build_passthrough_envs(&runtime_bin);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Put the detected package manager on PATH for vpr

For low-Node projects with an explicit packageManager such as pnpm@... or yarn@..., detect_only downloads the package manager into the Vite+ cache, but this env only prepends the Node runtime bin. run_command resolves bin_name using this PATH, so vp run <script>/vpr <script> fails with CannotFindBinaryPath unless that package manager also happens to be globally installed. Include pm.get_bin_prefix() in the passthrough env as well.

Useful? React with 👍 / 👎.

Comment on lines +95 to +96
Ok(vite_pm_cli::dispatch::dispatch_with_pm(cwd, pm_command.clone(), &pm)
.await?)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prepend the project runtime before dispatching PM shims

For explicit pnpm/yarn/npm package managers in low-Node passthrough, dispatch_with_pm runs generated JS shims with a PATH that starts at the package-manager bin dir, but ensure_project_runtime only returns a runtime and does not mutate the process PATH. In a standalone vp install with no system node on PATH (or with a different system node), vp i/vp add cannot run the shim with the resolved project runtime. Prepend runtime_bin before dispatching or pass an env that contains both dirs.

Useful? React with 👍 / 👎.

Comment on lines +1142 to +1143
let resolution = resolve_node_version(cwd, true).await?;
Ok(resolution.map(|r| r.version.to_string()))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve version ranges before testing the low-Node cutoff

This returns the raw version source, so supported inputs like .node-version containing 18/18.20 or engines.node: "<20" are passed directly to is_node_below_min, fail semver parsing, and skip passthrough. Those projects then continue to the old JS delegation path even though the runtime resolver would choose a Node version below 20.19.0. Compare against the resolved exact Node version instead of the raw source string.

Useful? React with 👍 / 👎.

leihaohao and others added 4 commits June 24, 2026 10:04
The vpr shim goes through shim::dispatch → commands::vpr::execute_vpr →
delegate::execute, which bypasses run_command_with_options where the
passthrough precheck lives. This meant vpr on low-Node projects still
crashed with the styleText ESM error.

Add the same passthrough precheck logic to execute_vpr: resolve the
project's Node version, check should_passthrough, and if eligible call
passthrough::execute before delegating to the JS CLI.

Fixes the issue reported in PR voidzero-dev#1921 review where vpr didn't work with
passthrough.

Verified: RUSTC_BOOTSTRAP=1 cargo test -p vite_global_cli --bin vp passthrough -> 6 passed.
These files were used during development but shouldn't be committed to
the repo. They contain internal implementation notes and planning docs
that aren't part of the public API or documentation.
Replace 'wired up in Task X' comments with descriptive text explaining
where each function is called from (run_command_with_options, execute_vpr,
execute). Makes the code self-documenting without referencing internal
task numbers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: vite-plus@0.2.1 crashes on Node < 20.12 with "node:util has no export named styleText" before engine check

2 participants