Skip to content

cli: handle -v/--version in the node argv0 shim - #36128

Open
robobun wants to merge 4 commits into
mainfrom
farm/6fde7ab4/node-shim-version
Open

cli: handle -v/--version in the node argv0 shim#36128
robobun wants to merge 4 commits into
mainfrom
farm/6fde7ab4/node-shim-version

Conversation

@robobun

@robobun robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

What

node -v and node --version now print v<process.version> and exit 0 when bun is invoked through its node argv0 shim (the /tmp/bun-node-<rev>/node symlink that bun run/--bun put on PATH).

Repro

With no real node on PATH:

cat > package.json <<'JSON'
{"scripts":{"probe":"node -e 'console.log(process.version)'; node --version; echo rc1=$?; node -v >/dev/null 2>&1; echo rc2=$?"}}
JSON
PATH=/usr/bin:/bin bun run probe

Before:

v26.3.0
error: Missing script to execute. Bun's provided 'node' cli wrapper does not support a repl.
rc1=1
rc2=1

After:

v26.3.0
v26.3.0
rc1=0
rc2=0

Cause

RunAsNodeCommand parses argv with RUN_TABLE, which has no -v/--version entry (those live in AUTO_ONLY_PARAMS). So:

  • --version was an unknown long flag, silently dropped (node-mode clears WARN_ON_UNRECOGNIZED_FLAG), leaving ctx.positionals empty and hitting exec_as_if_node_missing_script()error: Missing script to execute (rc 1).
  • -v was an unknown short flag, which clap's short-flag chainer hard-rejects (streaming.rs::chainging) → error: Invalid Argument '-v' plus the full bun run usage screen (rc 1).

This broke engine-version preflights (node --version in prepare/postinstall/CI scripts, node-gyp's version probe) on machines without a real node installed, while node -e/-p/file.js through the same shim worked fine.

Fix

Scan argv in exec_run_as_node before init() runs clap: if -v/--version appears before the first positional (or --), print v<REPORTED_NODEJS_VERSION>\n and exit 0. node app.js --version is unaffected; the flag still reaches the script.

Tests

Added to test/cli/run/as-node.test.ts:

  • node -v / node --version print process.version with exit 0
  • the shim's output matches node -e 'console.log(process.version)'
  • node script.js --version still passes --version through to the script
USE_SYSTEM_BUN=1 bun test test/cli/run/as-node.test.ts -t version   # 3 fail, 1 pass
bun bd test test/cli/run/as-node.test.ts                            # 15 pass

[review] gate passed · iteration 0 · 2 files touched

fails on main (without fix)
ASAN without fix: 3 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/cli/run/as-node.test.ts
bun test v1.4.0 (fea90e29b)

test/cli/run/as-node.test.ts:
(pass) fake node cli > the node cli actually works [411.52ms]
(pass) fake node cli > doesnt resolve bins [390.23ms]
(pass) fake node cli > doesnt resolve scripts [401.47ms]
(pass) fake node cli > can run a script named run.js [396.59ms]
(pass) fake node cli > entrypoint file extension picking > picks tsx over any other ext [407.89ms]
(pass) fake node cli > entrypoint file extension picking > picks jsx over ts [392.87ms]
(pass) fake node cli > entrypoint file extension picking > picks mts over ts [405.56ms]
(pass) fake node cli > entrypoint file extension picking > picks ts over js/cjs/etc [399.19ms]
(pass) fake node cli > node -e  [394.95ms]
 96 |       const result = Bun.spawnSync([bunExe(), "--bun", "node", flag], {
 97 |         cwd: temp,
 98 |         env: { ...bunEnv, NODE_ENV: undefined },
 99 |         stdin: Buffer.alloc(0),
100 |       });
101 |       expect(result.stderr.toString()).toBe("");
                                             ^
... (truncated)

release without fix: 3 FAILED
bun test v1.4.0-canary.1 (1498d7b77)

test/cli/run/as-node.test.ts:
(pass) fake node cli > the node cli actually works [21.91ms]
(pass) fake node cli > doesnt resolve bins [18.35ms]
(pass) fake node cli > doesnt resolve scripts [17.33ms]
(pass) fake node cli > can run a script named run.js [16.93ms]
(pass) fake node cli > entrypoint file extension picking > picks tsx over any other ext [17.38ms]
(pass) fake node cli > entrypoint file extension picking > picks jsx over ts [16.90ms]
(pass) fake node cli > entrypoint file extension picking > picks mts over ts [17.95ms]
(pass) fake node cli > entrypoint file extension picking > picks ts over js/cjs/etc [16.89ms]
(pass) fake node cli > node -e  [13.61ms]
 96 |       const result = Bun.spawnSync([bunExe(), "--bun", "node", flag], {
 97 |         cwd: temp,
 98 |         env: { ...bunEnv, NODE_ENV: undefined },
 99 |         stdin: Buffer.alloc(0),
100 |       });
101 |       expect(result.stderr.toString()).toBe("");
                                             ^
error: expect(received).toBe(expected)

- ""
+ "error: Invalid Argument '-v'
+ error: "node" exited with code 1
+ "

- Expected  - 1
+ Received  + 3

      at <a
... (truncated)
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/cli/run/as-node.test.ts
bun test v1.4.0 (fea90e29b)

test/cli/run/as-node.test.ts:
(pass) fake node cli > the node cli actually works [404.00ms]
(pass) fake node cli > doesnt resolve bins [376.78ms]
(pass) fake node cli > doesnt resolve scripts [388.46ms]
(pass) fake node cli > can run a script named run.js [397.33ms]
(pass) fake node cli > entrypoint file extension picking > picks tsx over any other ext [403.36ms]
(pass) fake node cli > entrypoint file extension picking > picks jsx over ts [397.51ms]
(pass) fake node cli > entrypoint file extension picking > picks mts over ts [382.50ms]
(pass) fake node cli > entrypoint file extension picking > picks ts over js/cjs/etc [410.34ms]
(pass) fake node cli > node -e  [395.49ms]
(pass) fake node cli > -v / --version > node -v prints process.version [256.81ms]
(pass) fake node cli > -v / --version > node --version prints process.version [232.85ms]
(pass) fake node cli > -v / --version > node --version matches node -e 'console.log(process.version)' [856.08ms]
(pass) fake node cli > -v / -
... (truncated)

release with fix: all passed
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 748ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/6] gen generated_host_exports.rs
generated_host_exports.rs: 94 exports (host=3, lazy=10, generic=81, rust=0); 240 extern-C blocks audited
[1/6] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)

  nightly-2026-07-20-x86_64-unknown-linux-gnu unchanged - rustc 1.99.0-nightly (9f36de775 2026-07-19)

�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�[92m   Compiling�[0m bun_ptr v0.0.0 (/workspace/bun/src/ptr)
�[1m�[92m   Compiling�[0m bun_boringssl_sys v0.0.0 (/workspace/bun/src/boringssl_sys)
�[1m�[92m   Compiling�[0m bun_safety v0.0.0 (/workspace/bun/src/safety)
�[1m�[92m   Compiling�[0m bun_zlib_sys v0.0.0 (/workspace/bun/src/zlib_sys)
�[1m�[92m   Compiling�[0m bun_cares_sys v0.0.0 (/workspace/bun/src/cares_sys)
�[1m�[92m   Compiling�[0m bun_zstd v0.0.0 (/workspace/bun/src/zstd)
�[1m�[92m   Compiling�[0m bun_picohttp v0.0.0 (/workspace/bun/src/picohttp)
�[1m�[92m   Compiling�[0m bun_output v
... (truncated)
diff hotspot
src/runtime/cli/mod.rs       | 20 ++++++++++++++++++++
 test/cli/run/as-node.test.ts | 40 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+)

gate history · 1 passed · 0 rejected · iteration 0

evidence per changed file
file                          reads  edits  tests
src/runtime/cli/mod.rs           11      6      0
test/cli/run/as-node.test.ts      1      2      0

When bun is invoked as 'node' (via the /tmp/bun-node-<rev>/node symlink
that 'bun run' puts on PATH), 'node -v' and 'node --version' now print
'v<process.version>' and exit 0, matching Node.js.

Previously RunAsNodeCommand used RUN_TABLE, which has no -v/--version
entry: '--version' was silently dropped (empty positionals -> 'Missing
script to execute'), and '-v' hit clap's short-flag chainer which
hard-errors on unknown shorts. This broke engine-version preflights
(node-gyp, prepare/postinstall scripts) on machines without a real
node installed.

The flag is only honoured before the first positional or '--', so
'node app.js --version' still passes '--version' through to the
script.
@robobun

robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 12:04 PM PT - Jul 27th, 2026

@robobun, your commit fea90e2 has 1 failures in Build #83498 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 36128

That installs a local version of the PR into your bun-36128 executable, so you can run:

bun-36128 --bun

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

The node-mode CLI handles leading -v and --version flags before initialization, prints the reported Node.js version, exits successfully, and preserves version-like arguments passed after a script. Tests cover output, stderr, exit status, and argument forwarding.

Node version flag handling

Layer / File(s) Summary
Version flag detection
src/runtime/cli/mod.rs
exec_run_as_node scans leading arguments for -v and --version, stopping at -- or positional script arguments.
Version output and validation
src/runtime/cli/mod.rs, test/cli/run/as-node.test.ts
The new helper writes the reported Node.js version and exits with status 0; tests validate output, stderr, flag equivalence, and script argument forwarding.

Suggested reviewers: cirospaciari

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding -v/--version handling to the node argv0 shim.
Description check ✅ Passed It explains the change and includes reproduction, cause, fix, and tests, covering the template’s purpose and verification needs.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/runtime/cli/mod.rs`:
- Around line 1532-1537: Update the argument scan around bun::argv() so
value-taking Node options, including --require, consume their following token
before classifying positional arguments. Preserve handling of --version and --,
continue scanning to process later flags such as --version, and add a regression
test for --require preload.js --version.

In `@test/cli/run/as-node.test.ts`:
- Around line 90-121: Extend the “node script.js --version passes the flag
through to the script” test around fakeNodeRun to cover the explicit -- option
terminator: invoke node with --, index.js, and each of --version and -v, and
assert the script receives the corresponding flag in process.argv.slice(2).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ce58083-0249-4a58-9a32-c410a21ef572

📥 Commits

Reviewing files that changed from the base of the PR and between 4eb6f99 and 102a266.

📒 Files selected for processing (2)
  • src/runtime/cli/mod.rs
  • test/cli/run/as-node.test.ts

Comment thread src/runtime/cli/mod.rs
Comment thread test/cli/run/as-node.test.ts
Comment thread src/runtime/cli/mod.rs Outdated
Comment thread src/runtime/cli/mod.rs
Comment thread src/runtime/cli/mod.rs
Comment thread src/runtime/cli/mod.rs Outdated
@robobun

robobun commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

CI status for fea90e2: test/cli/run/as-node.test.ts (the test added here) is green on every lane.

The failures in build 83498 are all darwin-only and unrelated to this CLI argv change:

This diff only touches exec_run_as_node argv handling and adds a print_node_version_and_exit helper; none of the HTTP, fetch, or require-cache code paths are reachable from it. Ready for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants