Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Exec in place instead of spawning and waiting in the CLI's wrapper processes

オープン
#3,106 コメント 0 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@fengmk2 がすでに取り組んでいます。

2026年10月18日 から。

評価

この issue はまだ評価されていません。

説明

Follow-up to #2936, which made the project-local vpx bin replace itself with the script's Node.js through process.execve() on Unix instead of spawning it and waiting. The global CLI's shims already exec in place (exec_tool in crates/vp_global_cli/src/shim/exec.rs). Several other paths still spawn a child only to wait for it and pass its exit status on.

Why

  • One PID and real signals. The wrappers ignore SIGINT so that Ctrl+C reaches the child through the process group, and none of them forwards SIGTERM or SIGHUP. A tool that holds the PID of vp and sends it a signal (kill <pid>, a process manager, a test harness) stops the wrapper, or nothing, while the real process keeps running. SIGUSR1 opens a Node.js wrapper's inspector instead of the tool's, and kills the Rust vp wrapper while the tool keeps running.
  • Exact exit status. A child killed by a signal comes back as a plain exit code (exit_code_from_status, exit_status_from, process.exit(n) in bin.ts), so callers cannot tell SIGINT from exit 130.
  • Fewer idle processes. vp dev through the global CLI runs three processes: vp (Rust) waits for node dist/bin.js, which waits for the Vite Node.js process. An idle Node.js wrapper holds about 50 MB for as long as the dev server runs.

Startup time does not change, since each wrapper still starts before it execs.

Candidates

Strongest first. "After the child" is what the parent still does once the child exits, which an exec must move earlier or keep on the spawn path.

# Command Where After the child
1 global vpr → node <vite-plus>/dist/bin.js run … js_executor.rs run_js_entry → vp_command::execute_with_terminal_guard nothing (shim mode skips the upgrade notice)
2 global vpx pkg@version / -p / -c → package manager dlx vpx.rs → vp_pm_cli resolution/runner.rs run_command → vp_command::run_command nothing
3 vp env exec --node <v> [--npm <v>] <command> commands/env/exec.rs:235-252 the upgrade notice; the shim branch of the same command already execs
4 vp sync-versions with the packaged sidecar commands/sync_versions.rs:32-35 nothing
5 vp exec <command> for one package, without --report-summary binding/src/exec/workspace.rs:254 nothing in that case
6 vp dev, preview, build, test, pack, doc, lint (not --init) binding/src/cli/execution.rs:36-57 nothing; the --init and fmt paths filter output and stay as they are
7 package-manager passthrough in the local CLI (vp install, add, dlx, …, and the local vpx pkg@version) binding/src/cli/mod.rs → vp_pm_cli runner the toolchain hint after vp why only
8 global CLI delegation to JS (vp dev, build, test, lint, fmt, check, pack, run, …) cli.rs → commands/delegate.rs → js_executor.rs the cached upgrade notice (main.rs:597-599)
9 global package-manager passthrough (vp install, add, why, …) cli.rs → vp_pm_cli runner the vp why hint and the upgrade notice

Rows 2, 7, and 9 share vp_pm_cli's runner and vp_command::run_command, so an exec variant there covers them together. Rows 5–7 run inside the Node.js process of the local CLI. For row 8, the upgrade notice has to print before the exec, keep the spawn path when a notice is due, or be dropped as the shim branch already does.

Not candidates: npm install -g / uninstall -g (they link bins afterwards), vp fmt and vp check (they filter or capture output), vp run / vp cache (task graph, many children), multi-package and --parallel vp exec, the installer (it owns the temp directory with the binary), and every call that captures output.

Lessons from #2936

  • Blocking stdio. Node.js sets O_NONBLOCK on a pipe once it opens it as stdio, and process.execve() passes that on (nodejs/node#66824), so the new program's writes fail with EAGAIN. bin.ts already restores blocking stdio; anything that execs from Node.js must do so first.
  • Signal dispositions. Node.js ignores SIGPIPE and SIGXFSZ, and an ignored signal stays ignored across exec. std's CommandExt::exec restores SIGPIPE but not SIGXFSZ. vpx-bin.ts restores both by adding and removing a listener.
  • A failed exec. process.execve() aborts Node.js on failure before 26.12 instead of throwing. vpx-bin.ts execs binaries through /bin/sh -c 'exec "$0" "$@"' for that reason. A Rust exec returns an error the caller can still report.
  • IPC. Exec closes an IPC channel, so a process started with one (NODE_CHANNEL_FD, process.send) keeps spawning.
  • Windows has no exec, so every path keeps its spawn there.
  • Rust side. tokio::process::Command::as_std_mut().exec() keeps the configured env, cwd, and pre_exec hooks (such as fix_stdio_streams), so the existing command builders can be reused.

Suggested order

  1. Add a Unix exec variant to vp_command next to run_command and execute_with_terminal_guard, and use it for rows 1–4.
  2. Use it for the local CLI's paths (rows 5–7), skipping exec when the process has an IPC channel and restoring SIGXFSZ.
  3. Decide the upgrade notice, then switch rows 8 and 9.

Separately, vp run does not forward SIGTERM to its tasks, which is out of scope here because it manages many children.

主要言語
Rust
スター
6.3k
フォーク
283
平均マージ
1日 16時間
マージ済み PR(30日)
214

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

voidzero-dev/vite-plus のほかの issue

voidzero-dev/vite-plus の issue をすべて見る

似ている issue

Rust の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。