Skip to content

fix(lockfile): hoist npm workspace links to root importer deps#374

Merged
jdx merged 1 commit intomainfrom
fix/npm-workspace-root-symlinks
Apr 29, 2026
Merged

fix(lockfile): hoist npm workspace links to root importer deps#374
jdx merged 1 commit intomainfrom
fix/npm-workspace-root-symlinks

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented Apr 29, 2026

Summary

  • npm symlinks every workspace member into the root node_modules/ regardless of whether the root package.json declares it as a dep — recorded in package-lock.json as node_modules/<name>: { link: true, resolved: "<rel>" }. Aube was seeding the root importer only from dependencies/devDependencies/optionalDependencies, so undeclared workspace projects never got a top-level node_modules/<ws-pkg> symlink and Angular CLI / Nx / similar tooling that resolves workspace libraries from the repo root broke when migrating npm-managed monorepos to aube.
  • Surface every top-level link entry (plain or scoped, no nested /node_modules/ segment) as a direct dep of the root importer in the npm-lockfile parser. Existing already_added dedupe preserves explicit dependencies: { "@scope/x": "file:packages/x" } declarations; entries are appended sorted by name for deterministic output.

Reported in #345 (comment) (siemens/element).

Test plan

  • cargo test -p aube-lockfile (new test test_parse_workspace_links_undeclared_in_root_deps + existing test_parse_workspace_links to confirm no double-add when the workspace IS in root deps)
  • cargo test workspace-wide
  • Manual smoke: minimal repro repo with workspaces: ["projects/foo", "projects/bar"], neither declared in root dependencies, package-lock.json with node_modules/@scope/foo + node_modules/@scope/bar link entries → aube install produces node_modules/@scope/foo and node_modules/@scope/bar symlinks pointing at ../../projects/<name>

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it changes how npm lockfiles are interpreted to seed root direct dependencies, which can affect install layout and hoisting behavior for workspace/linked packages.

Overview
Ensures npm-managed workspaces that are not declared in the root package.json deps still get their top-level node_modules/<workspace> symlinks recreated by aube install.

The npm lockfile parser now scans package-lock.json packages for top-level node_modules/* entries with link: true, de-dupes against already-declared direct deps, sorts them deterministically, and appends them to the root importer’s direct deps. Adds a regression test covering undeclared workspace members being surfaced as root direct deps and round-tripping as LocalSource::Link.

Reviewed by Cursor Bugbot for commit c98805f. Bugbot is set up for automated code reviews on this repo. Configure here.

npm symlinks every workspace member into the root `node_modules/`
regardless of whether the root `package.json` declares it as a dep.
The lockfile records each as `node_modules/<name>: { link: true,
resolved: "<rel>" }` independent of the root manifest. Aube was
seeding the root importer's direct deps from `dependencies` /
`devDependencies` / `optionalDependencies` only, so workspace
projects that weren't explicitly listed in the root manifest never
got a top-level `node_modules/<ws-pkg>` symlink — Angular CLI / Nx
/ many monorepo build tools that resolve workspace libraries from
the repo root silently broke after migrating from npm to aube.

Surface every top-level link entry in `node_modules/` (plain or
scoped, no nested `/node_modules/` segment) as a direct dep of the
root importer when the npm parser builds the graph. The existing
`already_added` dedupe keeps explicit `dependencies: { "@scope/x":
"file:packages/x" }` declarations from doubling up. Sorted by name
for stable output.

Reported in #345 (comment).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 29, 2026

Greptile Summary

This PR fixes a silent breakage for npm monorepos where workspace members are not listed in the root package.json dependencies: npm records them as node_modules/<name>: { link: true } entries in the lockfile, but aube's importer-seeding loop was driven solely by the dependencies/devDependencies/optionalDependencies keys, so the root symlinks were never created. The fix scans all link: true, top-level node_modules/ entries after the explicit-dep pass and appends any that are not already in direct, sorted for deterministic output.

Confidence Score: 5/5

Safe to merge — the change is narrow, correct, and backed by two well-targeted tests.

No logical, security, or correctness issues found. The scoped-package segment check handles all edge cases, already_added deduplication prevents double-adds, and the existing test_parse_workspace_links assertion (importer.len() == 1) confirms the no-double-add invariant still holds under the new code.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube-lockfile/src/npm.rs Adds a post-processing step to hoist top-level workspace link entries from the lockfile into the root importer's direct-dep list, with deduplication via already_added and deterministic ordering. Two focused tests cover both the undeclared and declared (no-double-add) cases.

Reviews (1): Last reviewed commit: "fix(lockfile): hoist npm workspace links..." | Re-trigger Greptile

@jdx jdx merged commit 7dc3692 into main Apr 29, 2026
19 checks passed
@jdx jdx deleted the fix/npm-workspace-root-symlinks branch April 29, 2026 13:20
@greptile-apps greptile-apps Bot mentioned this pull request Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant