Repository navigation
fix(vpx): handle --experimental-config-file and Windows Ctrl+C - #3167
Conversation
CLI artifact sizes (
|
| Artifact | Format | Base | PR | Change |
|---|---|---|---|---|
packages/cli/dist |
Directory total | 2.24 MiB | 2.24 MiB | +588 B (+0.03%) |
packages/core/dist |
Directory total | 4.08 MiB | 4.08 MiB | 0 B (0.00%) |
| Combined package dist | Directory total | 6.32 MiB | 6.32 MiB | +588 B (+0.01%) |
vp (Linux x64) |
Binary | 8.20 MiB | 8.20 MiB | 0 B (0.00%) |
vp (Linux x64) |
gzip -9 | 3.74 MiB | 3.74 MiB | +95 B (+0.00%) |
| NAPI (Linux x64) | Binary | 35.36 MiB | 35.36 MiB | 0 B (0.00%) |
| NAPI (Linux x64) | gzip -9 | 14.07 MiB | 14.07 MiB | +2 B (+0.00%) |
vp (macOS ARM64) |
Binary | 5.36 MiB | 5.36 MiB | +16 B (+0.00%) |
vp (macOS ARM64) |
gzip -9 | 3.19 MiB | 3.19 MiB | +604 B (+0.02%) |
| NAPI (macOS ARM64) | Binary | 42.17 MiB | 42.17 MiB | 0 B (0.00%) |
| NAPI (macOS ARM64) | gzip -9 | 18.22 MiB | 18.22 MiB | +3 B (+0.00%) |
vp (Windows x64) |
Binary | 6.07 MiB | 6.07 MiB | 0 B (0.00%) |
vp (Windows x64) |
gzip -9 | 2.81 MiB | 2.81 MiB | +188 B (+0.01%) |
| NAPI (Windows x64) | Binary | 31.41 MiB | 31.41 MiB | 0 B (0.00%) |
| NAPI (Windows x64) | gzip -9 | 12.78 MiB | 12.78 MiB | +62 B (+0.00%) |
| Trampoline (Windows x64) | Binary | 13.50 KiB | 13.50 KiB | 0 B (0.00%) |
| Trampoline (Windows x64) | gzip -9 | 7.09 KiB | 7.09 KiB | 0 B (0.00%) |
| Installer (Windows x64) | Binary | 4.66 MiB | 4.66 MiB | 0 B (0.00%) |
| Installer (Windows x64) | gzip -9 | 2.23 MiB | 2.23 MiB | 0 B (0.00%) |
--experimental-config-file as taking no value--experimental-config-file as taking no value, and wait for Ctrl+C cleanup on Windows
Preview binary sizes (
|
| Binary | Latest release | Preview | Change |
|---|---|---|---|
vp (macOS ARM64) |
7.62 MiB | 4.82 MiB | -2.80 MiB (-36.75%) |
| NAPI (macOS ARM64) | 41.49 MiB | 42.14 MiB | +663.30 KiB (+1.56%) |
vp (macOS x64) |
9.29 MiB | 6.07 MiB | -3.22 MiB (-34.67%) |
| NAPI (macOS x64) | 46.44 MiB | 47.23 MiB | +810.63 KiB (+1.70%) |
vp (Linux ARM64 GNU) |
8.48 MiB | 6.06 MiB | -2.42 MiB (-28.56%) |
| NAPI (Linux ARM64 GNU) | 30.66 MiB | 31.37 MiB | +725.63 KiB (+2.31%) |
vp (Linux ARM64 musl) |
8.53 MiB | 6.13 MiB | -2.40 MiB (-28.15%) |
| NAPI (Linux ARM64 musl) | 29.89 MiB | 30.59 MiB | +717.13 KiB (+2.34%) |
vp (Linux x64 GNU) |
10.21 MiB | 7.27 MiB | -2.94 MiB (-28.80%) |
| NAPI (Linux x64 GNU) | 34.53 MiB | 35.35 MiB | +841.56 KiB (+2.38%) |
vp (Linux x64 musl) |
10.21 MiB | 7.34 MiB | -2.87 MiB (-28.11%) |
| NAPI (Linux x64 musl) | 33.50 MiB | 34.33 MiB | +845.38 KiB (+2.46%) |
vp (Windows x64) |
8.29 MiB | 5.31 MiB | -2.98 MiB (-35.91%) |
| NAPI (Windows x64) | 30.25 MiB | 31.06 MiB | +832.00 KiB (+2.69%) |
| Trampoline (Windows x64) | 13.00 KiB | 13.00 KiB | 0 B (0.00%) |
| Installer (Windows x64) | 3.64 MiB | 3.67 MiB | +31.50 KiB (+0.85%) |
vp (Windows ARM64) |
6.94 MiB | 4.67 MiB | -2.27 MiB (-32.65%) |
| NAPI (Windows ARM64) | 26.83 MiB | 27.49 MiB | +684.00 KiB (+2.49%) |
| Trampoline (Windows ARM64) | 13.00 KiB | 13.00 KiB | 0 B (0.00%) |
| Installer (Windows ARM64) | 3.01 MiB | 3.04 MiB | +27.00 KiB (+0.88%) |
gzip -9 sizes
| Binary | Latest release | Preview | Change |
|---|---|---|---|
vp (macOS ARM64) |
3.94 MiB | 2.88 MiB | -1.06 MiB (-26.90%) |
| NAPI (macOS ARM64) | 17.97 MiB | 18.20 MiB | +239.82 KiB (+1.30%) |
vp (macOS x64) |
4.28 MiB | 3.04 MiB | -1.23 MiB (-28.84%) |
| NAPI (macOS x64) | 19.14 MiB | 19.39 MiB | +260.92 KiB (+1.33%) |
vp (Linux ARM64 GNU) |
4.26 MiB | 3.32 MiB | -967.59 KiB (-22.16%) |
| NAPI (Linux ARM64 GNU) | 13.00 MiB | 13.26 MiB | +262.10 KiB (+1.97%) |
vp (Linux ARM64 musl) |
4.30 MiB | 3.35 MiB | -981.76 KiB (-22.27%) |
| NAPI (Linux ARM64 musl) | 12.71 MiB | 12.96 MiB | +260.62 KiB (+2.00%) |
vp (Linux x64 GNU) |
4.50 MiB | 3.37 MiB | -1.13 MiB (-25.10%) |
| NAPI (Linux x64 GNU) | 13.81 MiB | 14.07 MiB | +270.12 KiB (+1.91%) |
vp (Linux x64 musl) |
4.51 MiB | 3.40 MiB | -1.11 MiB (-24.62%) |
| NAPI (Linux x64 musl) | 13.35 MiB | 13.61 MiB | +268.67 KiB (+1.97%) |
vp (Windows x64) |
3.66 MiB | 2.48 MiB | -1.18 MiB (-32.20%) |
| NAPI (Windows x64) | 12.37 MiB | 12.64 MiB | +267.37 KiB (+2.11%) |
| Trampoline (Windows x64) | 7.14 KiB | 7.14 KiB | 0 B (0.00%) |
| Installer (Windows x64) | 1.78 MiB | 1.80 MiB | +14.14 KiB (+0.77%) |
vp (Windows ARM64) |
3.46 MiB | 2.38 MiB | -1.08 MiB (-31.35%) |
| NAPI (Windows ARM64) | 11.75 MiB | 12.00 MiB | +261.39 KiB (+2.17%) |
| Trampoline (Windows ARM64) | 6.93 KiB | 6.93 KiB | -2 B (-0.03%) |
| Installer (Windows ARM64) | 1.71 MiB | 1.72 MiB | +11.50 KiB (+0.66%) |
Raw sizes are uncompressed file sizes. gzip sizes recompress each binary at level 9; they are not package or download archive sizes.
The baseline uses published release files, including NAPI packages from npm at the same version. Build versions and toolchains can affect the difference.
Registry bridge build (
|
| Package | Version |
|---|---|
vite-plus |
0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6 |
@voidzero-dev/vite-plus-core |
0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6 |
create-vite-plus |
0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6 |
Install the Vite+ CLI built from this commit, then migrate a project:
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/voidzero-dev/vite-plus/fa1f275ad58a7dea77fd735238059b8ec5f7acd6/packages/cli/install.sh | VP_PR_VERSION=3167 VP_LEGACY_INSTALLER_URL=https://raw.githubusercontent.com/voidzero-dev/vite-plus/fa1f275ad58a7dea77fd735238059b8ec5f7acd6/packages/cli/install-legacy.sh bash# Windows (PowerShell)
$env:VP_PR_VERSION="3167"; $env:VP_LEGACY_INSTALLER_URL="https://raw.githubusercontent.com/voidzero-dev/vite-plus/fa1f275ad58a7dea77fd735238059b8ec5f7acd6/packages/cli/install-legacy.ps1"; irm https://raw.githubusercontent.com/voidzero-dev/vite-plus/fa1f275ad58a7dea77fd735238059b8ec5f7acd6/packages/cli/install.ps1 | iexOr download the standalone Windows installer built from this commit:
| Architecture | Installer |
|---|---|
| x64 | vp-setup-x86_64-pc-windows-msvc.exe |
| Arm64 | vp-setup-aarch64-pc-windows-msvc.exe |
GitHub requires you to sign in and downloads each installer as a ZIP artifact. Extract vp-setup.exe, then run it against this preview build:
.\vp-setup.exe --version "0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6" --registry "https://registry-bridge.viteplus.dev/"After installing, upgrade the current project's vite-plus to this test build with:
vp migrateOr create a new project from this commit without installing the CLI:
export npm_config_registry=https://registry-bridge.viteplus.dev/ pnpm_config_registry=https://registry-bridge.viteplus.dev/
pnpm create vite-plus@0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6 # or: npm create vite-plus@0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6$env:npm_config_registry="https://registry-bridge.viteplus.dev/"; $env:pnpm_config_registry="https://registry-bridge.viteplus.dev/"
pnpm create vite-plus@0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6 # or: npm create vite-plus@0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6pnpm 11+ reads pnpm_config_registry, npm and older pnpm read npm_config_registry. The new project pins this build, so keep the registry set for later installs, or configure it as below.
Or point your package manager at the bridge registry https://registry-bridge.viteplus.dev/:
| Package manager | Registry config |
|---|---|
| npm / pnpm / Bun | .npmrc: registry=https://registry-bridge.viteplus.dev/ |
| Yarn (v2+) | .yarnrc.yml: npmRegistryServer: "https://registry-bridge.viteplus.dev/" |
Then pin the build (vite aliases to vite-plus-core; pnpm can use a catalog, npm an overrides entry):
{
"devDependencies": {
"vite-plus": "0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6",
"vite": "npm:@voidzero-dev/vite-plus-core@0.0.0-commit.fa1f275ad58a7dea77fd735238059b8ec5f7acd6"
}
}
🐳 Docker preview imageBuilt from this PR's registry bridge build:
# remove any stale local copy from a previous run, then pull fresh
docker rmi ghcr.io/voidzero-dev/vite-plus:pr-3167 2>/dev/null; docker pull ghcr.io/voidzero-dev/vite-plus:pr-3167Quick check: docker run --rm ghcr.io/voidzero-dev/vite-plus:pr-3167 vp --versionSee docs/guide/docker.md for usage. |
--experimental-config-file as taking no value, and wait for Ctrl+C cleanup on Windows--experimental-config-file without = on every Node.js, and wait for Ctrl+C cleanup on Windows
Node.js 24.19, 26.1, and later read a bare `--experimental-config-file` as `node.config.json`. Script detection took the next argument as its value, so it misplaced the end of the Node.js options: `--watch` after the script started the watcher, and `--watch`, a watcher option, or `--tsconfig` after the flag was taken as the config file. Both CLIs now read the flag without a value, as those releases do; the `=` form names a config file on every release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Unix, `vp` ignores SIGINT while it waits for a child, so a Ctrl+C reaches the child and `vp` exits once the child has cleaned up. On Windows, `vp.exe` had no handler, so the console killed it with STATUS_CONTROL_C_EXIT while the child was still cleaning up, and the `vpx.exe` trampoline and `vp run` reported that early exit. `vp_command::ConsoleCtrlGuard` handles Ctrl+C and Ctrl+Break while `spawn_tool` and `execute_with_terminal_guard` wait, as the trampoline does. A handler routine, unlike a NULL handler, is not inherited, so the child still gets the event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A script cleans up for 300 ms after SIGINT. The global `vpx` and `vp run` must exit after it, with its code; on Windows, `vp.exe` used to exit at once with STATUS_CONTROL_C_EXIT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reading a bare `--experimental-config-file` as taking no value broke the separate-path form that Node.js 22.x, 24.18 and earlier, and 26.0 take: `custom.json` was taken for the script, so `--tsconfig` after it was left to the watcher, where a TS_NODE_PROJECT from an env file overrode it, and `--watch --include` after it reached Node.js. Detection now takes the next argument as the config file when it ends in `.json`, the same on every release: a `.json` file cannot be the script either way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows, the snapshot runner starts steps with Ctrl+C ignored, which children inherit, and only vite-task clears it, so the script under a direct `vpx` never got Ctrl+C and the step timed out. The `vp run` case passes on Windows and covers the global vpx there as its task. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `execute_with_terminal_guard` picks its guard per platform around one spawn and wait, and `ConsoleCtrlGuard` no longer tracks whether it was installed: removing a routine that was not added is a no-op. - `run_command`, which runs `vp install` and the `vpx` dlx fallback, waits out the child's Ctrl+C cleanup on Windows as well. - The separate config path under `--watch` is a `verify-watch.mjs` scenario instead of a third driver script; the direct `vpx` Ctrl+C case, which only ran on Unix, where the `vp run` case covers the same path, is gone; and the bare config flag case, whose detection has no Windows code, skips the Node.js 26.12 download on Windows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3e70c81 to
d66511f
Compare
--experimental-config-file without = on every Node.js, and wait for Ctrl+C cleanup on Windows--experimental-config-file and Windows Ctrl+C
Summary
Two follow-ups to #3164.
--experimental-config-filewithout=Node.js 24.19, 26.1, and later read a bare
--experimental-config-fileasnode.config.jsonand take no value; older releases take the next argument as the config file. vpx's script detection always took the next argument, so on newer Node.js it misplaced the end of the Node.js options and took--watchand--tsconfigfrom the wrong place.vpx --experimental-config-file main.ts --watch[]main.tsonce, and the script gets["--watch"]vpx --experimental-config-file --watch --include extra.json main.tsbad option: --include--include extra.jsonvpx --watch --experimental-config-file --tsconfig cli.json --env-file=.env main.tsTS_NODE_PROJECTin.envnamescli.jsonBoth CLIs now take the argument after a bare
--experimental-config-fileas its value only when it ends in.json, the same on every Node.js release: older releases take a separate config path, and a.jsonfile cannot be the script on newer ones. So the separate-path form keeps working on 22.x, 24.18 and earlier, and 26.0, wherevpx --watch --experimental-config-file custom.json --tsconfig cli.json --env-file=.env main.tsusescli.jsonandvpx --experimental-config-file custom.json --watch --include extra.json main.tsruns the watcher. On those releases, a separate config path that does not end in.jsonneeds the=form, which names a config file on every release.Windows: wait for the child's Ctrl+C cleanup
On Windows, the global
vpxandvp runexited as soon as Ctrl+C arrived, withSTATUS_CONTROL_C_EXIT(0xC000013A), about 300 ms before the script finished itsSIGINTcleanup; the project-local bin waited. A caller waiting on the CLI's PID could not tell when the script had stopped. Now they wait for the script and exit with its code, as the project-local bin does.On Unix,
vpignores SIGINT while it waits for a child.vp.exenow does the same withvp_command::ConsoleCtrlGuard, a console control handler that handles Ctrl+C and Ctrl+Break whilespawn_tool,run_command(vp install, and thevpxfallback tovp dlx), andexecute_with_terminal_guardwait, as thevpx.exetrampoline already does. A handler routine, unlike a NULL handler, is not inherited, so the child still gets the event. vite-task's runner already handles Ctrl+C, sovp.exewas the only process in thevp runchain that died early.Also filed: oxc-project/oxc-node#878, where an explicit
.jsentry runs a sibling.tsfile, reported against the same preview.🤖 Generated with Claude Code