fix(run): prepend node_modules/.bin to script PATH unconditionally#283
Conversation
bin_path() gated each ancestor's node_modules/.bin PATH segment on an is_dir() check computed once before the child spawns, so a .bin absent at spawn was dropped from the child's PATH entirely. A script that creates node_modules/.bin/<tool> mid-run and then invokes <tool> by bare name (the install-then-invoke pattern) could not find it. npm and pnpm both prepend node_modules/.bin unconditionally, without stat'ing; a missing PATH entry is harmless. Drop the existence check; keep the ancestor-walk cap and the workspace_root break. Closes #281
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — drops the pre-spawn is_dir() gate in bin_path so each ancestor node_modules/.bin is prepended to a script's PATH unconditionally, fixing install-then-invoke scripts (#281) that create .bin mid-run.
- Unconditional
.binprepend inbin_path— removes theis_dir()check; the walk-up now pushes everynode_modules/.binentry, preserving the 16-level cap and theworkspace_rootbreak. Matches npm/pnpm, which prepend without stat'ing; non-existent PATH entries are inert. - Unit test
bin_path_includes_missing_dir— asserts a non-existent<root>/node_modules/.binstill appears in the returned PATH. - Unix e2e
run_script_finds_bin_created_mid_run— a script that writes+chmodsnode_modules/.bin/mytooland then invokes it by bare name must succeed.
Verified both consumers of bin_path (cli.rs:3499, cli.rs:4864) merely concatenate the result into the child PATH, so the extra non-existent entries are harmless. find_bin is untouched. Clean, well-scoped fix that faithfully matches the referenced npm/pnpm behavior.
ℹ️ Nitpicks
crates/nub-core/src/workspace/scripts.rs— thefor _ in 0..16loop now pushes on iteration 0 before anybreak, sodirsis never empty and theif dirs.is_empty()branch (~L322) is now dead. Harmless; can be simplified to always take the join path in a follow-up.
Claude Opus | 𝕏
There was a problem hiding this comment.
Pull request overview
Fixes nub run script PATH construction so node_modules/.bin entries are prepended even if the directory doesn’t exist at spawn time, matching npm/pnpm behavior and fixing the install-then-invoke pattern from #281.
Changes:
- Remove the
is_dir()gating inbin_path()so ancestornode_modules/.binsegments are always prepended (still capped at 16 levels; still breaks atworkspace_root). - Add a unit regression test ensuring
bin_path()includes a non-existent.bindirectory. - Add a Unix-only e2e regression test proving a script can create
node_modules/.bin/<tool>mid-run and then invoke it by bare name successfully.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| crates/nub-core/src/workspace/scripts.rs | Make bin_path() prepend node_modules/.bin unconditionally; add unit regression test for missing .bin inclusion. |
| crates/nub-cli/tests/workspace_run.rs | Add Unix e2e regression test covering mid-run creation of .bin and subsequent bare-name invocation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Shipped in v0.3.0: https://github.com/nubjs/nub/releases/tag/v0.3.0 |

bin_path()gated each ancestor'snode_modules/.binPATH segment on anis_dir()check computed before spawn, so a.binabsent at spawn was dropped — including one a script creates mid-run (install-then-invoke). npm and pnpm prepend unconditionally without stat'ing; a missing PATH entry is inert. Drop the check; keep the 16-level cap andworkspace_rootbreak.find_bin()untouched.Tests:
bin_path_includes_missing_dir(unit) andrun_script_finds_bin_created_mid_run(Unix e2e).Closes #281
https://claude.ai/code/session_01Xuh9u8b93eMNu53fTQTWX7