fix(linker): point bin shim NODE_PATH at the hidden modules dir#613
Conversation
Two stacked bugs caused `astro check` (and any tool that loads a
transitive via `require('typescript')` from a shimmed bin) to fail
on an aube install while it worked on pnpm:
1. `preferSymlinkedExecutables` resolved to `true` whenever unset,
so POSIX `.bin/<name>` was always a bare symlink. A symlink can't
export env vars, which silently made `extendNodePath=true` a
no-op on every default install. pnpm's actual default flips
based on the linker: symlink under `node-linker=hoisted`,
shim under `isolated`. aube now mirrors that — unset defaults to
`false` for the isolated linker.
2. Even with a shim, `NODE_PATH` was set to `$basedir/..` —
`<project>/node_modules/` only — so Node's resolver couldn't see
auto-installed peers hoisted to `.aube/node_modules/`. The shim
now emits a two-entry NODE_PATH covering the project's top-level
`node_modules/` and the virtual store's hidden modules dir,
matching the load-bearing entries of pnpm's own shim.
Repro: an Astro project with `@astrojs/check` as a direct devDep
(and `typescript` only auto-installed as the package's peer) ran
`astro check` and hit `requires the following dependency to be
installed: typescript`, even though `typescript` was on disk at
`node_modules/.aube/typescript@.../node_modules/typescript`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Greptile SummaryFixes two compounding bugs that caused shimmed bins (e.g.
Confidence Score: 5/5Safe to merge — shim and default changes are well-guarded and tested end-to-end. The No files require special attention. Important Files Changed
Reviews (2): Last reviewed commit: "fix(linker): use `;` on Windows + skip h..." | Re-trigger Greptile |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d0a1f1a. Configure here.
Address PR review feedback and the bats-serial CI failures: - Windows: NODE_PATH is parsed by Node.js, which always splits on `;` (`path.delimiter`) regardless of which shell launched the bin. The prior change used `:` for the ps1 and Git-Bash `.sh` shims because their paths use `/`, but that conflated the path separator with the list separator — Node would have read the multi-entry value as one invalid path and dropped the hidden-modules entry. All three Windows shim variants now join with `;`. - `install` and `rebuild`: gate `hidden_modules_dir` on `node_linker != Hoisted`. Under the hoisted layout the directory doesn't exist; appending it to NODE_PATH would just stuff a spurious non-existent entry into every shim. `add.rs` (global install, hoisted-shaped) already did this; the same guard now applies to project installs and rebuilds. - bats: update the suite to match the new defaults. The default `.bin/<name>` is now a shim under the isolated linker rather than a symlink, with a two-entry NODE_PATH. Replace the assertion shape in `workspace.bats` for the workspace-bin test so it works for both shim and symlink outputs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Pushed 9362ecd addressing the review feedback and the bats-serial CI failures: Greptile (both files): gated Cursor Bugbot (Windows separator): real bug — bats: updated Verified: This comment was generated by Claude. |

Summary
Two stacked bugs caused
astro check(and any tool that loads a transitive viarequire('typescript')from a shimmed bin) to fail on an aube install while it worked on pnpm:preferSymlinkedExecutablesdefaulted to symlink under the isolated linker. A symlink can't export env vars, silently makingextendNodePath=truea no-op on every default install. pnpm's actual default flips based on the linker (symlink fornode-linker=hoisted, shim forisolated); aube now mirrors that.NODE_PATHonly covered the project's top-levelnode_modules/. Auto-installed peers hoisted to.aube/node_modules/were invisible to Node's resolver when launched from a shim. The shim now emits a two-entry NODE_PATH covering both directories, matching the load-bearing entries of pnpm's own shim.Repro
Astro project with
@astrojs/checkas a direct devDep andtypescriptonly auto-installed as the package's peer:typescript@5.9.3was on disk atnode_modules/.aube/typescript@5.9.3/node_modules/typescript, but the shim'sNODE_PATHpointed at the rootnode_modules/which doesn't contain transitives.After the fix:
The same shim now reads:
Test plan
cargo test -p aube-linker --lib— 77 pass (newcreate_bin_shim_appends_hidden_modules_to_node_pathcovers the two-entry NODE_PATH)cargo test -p aube --bin aube— 463 passcargo test --workspace --lib— all greencargo clippy --workspace --all-targets -- -D warnings— cleancargo fmt --all -- --check— clean/tmp/cmp-aube-with-pnpm-lock(Astro + Vite + @astrojs/check with existing pnpm-lock.yaml): aube install +astro checksucceeds, matching pnpm behavior🤖 Generated with Claude Code
Note
Medium Risk
Changes default POSIX
.binentry type (symlink → shim) under the isolated linker and altersNODE_PATHconstruction across platforms, which may affect how executables resolve dependencies and how tools observe.binfiles.Overview
Fixes isolated installs where shimmed CLIs couldn’t resolve transitives/auto-installed peers by expanding shim
NODE_PATHto include both the top-levelnode_modulesand the hidden virtual-store modules dir (.aube/node_modules).On POSIX,
preferSymlinkedExecutablesnow defaults to shims under the isolated linker (to makeextendNodePatheffective) while keeping the symlink default undernodeLinker=hoisted; global installs explicitly pass no hidden-modules path. Windows shims are updated to build multi-entryNODE_PATHcorrectly (always using;as the delimiter), and tests/docs are updated to reflect the new defaults and NODE_PATH contents.Reviewed by Cursor Bugbot for commit 9362ecd. Bugbot is set up for automated code reviews on this repo. Configure here.