Skip to content

fix: Preserve npm workspace importers#443

Merged
jdx merged 4 commits intomainfrom
codex/workspace-recursive-install
May 1, 2026
Merged

fix: Preserve npm workspace importers#443
jdx merged 4 commits intomainfrom
codex/workspace-recursive-install

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented May 1, 2026

Summary

  • parse npm package-lock workspace target entries as importers so workspace package dependency graphs survive reads
  • write npm workspace package entries, root workspace links, and per-workspace node_modules trees
  • add npm workspace importer parse/write regression coverage

Test plan

  • cargo fmt --check
  • cargo test -p aube-lockfile workspace_importers
  • cargo test -p aube-lockfile
  • cargo build -p aube
  • /Users/jdx/src/aube/.worktrees/codex-workspace-recursive-install/target/debug/aube install -r in jdx/mise-versions
  • /Users/jdx/src/aube/.worktrees/codex-workspace-recursive-install/target/debug/aube ci -r in jdx/mise-versions

Note: the patched aube install/ci path gets mise-versions past the original missing web workspace dependencies / astro binary failure. The subsequent web build exposes the existing Astro/Tailwind configuration mismatch, which is separate from the package-lock workspace importer bug.


Note

Medium Risk
Touches npm package-lock.json parsing and serialization logic for workspaces, which can affect dependency graph shape and emitted lockfile contents across installs. Changes are localized but may cause lockfile diffs or incorrect hoisting/link records if edge cases are missed.

Overview
Preserves npm workspaces as first-class importers when parsing package-lock.json by capturing each workspace target path (e.g. web/, packages/app/) and recording its prod/dev/optional direct deps (including their original specifiers) in graph.importers.

Extends the npm writer to emit workspace package entries and their root node_modules/<workspace> link: true records, and to build per-workspace node_modules trees while skipping redundant workspace-nested packages already hoisted at the root. Updates reachability/flag computation to consider roots from all importers, and adds regression tests for workspace importer parse/write and duplicate-hoist avoidance.

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

@jdx jdx changed the title Preserve npm workspace importers fix: Preserve npm workspace importers May 1, 2026
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 1, 2026

Greptile Summary

This PR fixes npm workspace handling by parsing workspace target path entries (e.g. web, packages/app) as separate graph importers and updating the writer to emit the corresponding node_modules/<name> link: true records and per-workspace hoist trees. Reachability computation now considers all importers rather than only the root, ensuring dev/optional flags are correct for workspace-only dependencies.

Confidence Score: 5/5

Safe to merge; logic is correct and well-tested with parse, write, and dedup round-trip regression tests.

No P0 or P1 issues found. The one P2 comment is a minor performance concern in workspace_package_for_importer. Core logic — link-target importer parsing, redundant-top pruning, cross-importer reachability, and the non_link_roots filter — all check out against the existing parse infrastructure.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube-lockfile/src/npm.rs Adds npm workspace importer parse/write support: parses workspace target paths as graph importers, emits workspace package entries + root link records, builds per-workspace hoist trees with redundancy pruning, and expands reachability to all importers. Three regression tests cover parse, write, and dedup round-trips.

Fix All in Claude Code

Reviews (4): Last reviewed commit: "fix(lockfile): skip workspace dups alrea..." | Re-trigger Greptile

Comment thread crates/aube-lockfile/src/npm.rs Outdated
Comment thread crates/aube-lockfile/src/npm.rs
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Benchmark changes

Versions:

  • aube: 1.5.1 -> 1.5.2
  • pnpm: 11.0.2 -> 11.0.3

Public ratios: warm installs vs Bun 4x -> 10x; warm installs vs pnpm 5x -> 15x.

Benchmark aube bun pnpm
Fresh install (warm cache) 1021ms -> 188ms (-82%) 4134ms -> 1842ms (-55%) 4717ms -> 2877ms (-39%)
CI install (warm cache, GVS disabled) 2920ms -> 805ms (-72%) 3396ms -> 1878ms (-45%) 4864ms -> 2499ms (-49%)
CI install (cold cache, GVS disabled) 10801ms -> 3697ms (-66%) 10012ms -> 4137ms (-59%) 9722ms -> 4992ms (-49%)

be154a5 vs 73a6617 | aube/bun/pnpm | 3 scenarios | 3 runs | 500mbit/50ms | generated by Codex.

non_link_roots looked up each dep in the canonical map via
canonical_key_from_dep_path, but for a workspace link dep the dep_path
is `<name>@link:<path>`, which doesn't match the canonical map's
`name@version` keys. The lookup always returned None, the filter passed
every dep through, and the function was a no-op. Output stayed correct
only because build_hoist_tree independently skips unknown canonical
keys — a fragile coincidence.

Filter on the dep_path tail's `link:` prefix instead, which matches
the format directly. Also drop the stale write-doc bullet about
workspace importers (this is the change that landed them).

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

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 658b168. Configure here.

Comment thread crates/aube-lockfile/src/npm.rs
…efix

The previous fix checked `dep_path_tail(...).starts_with("link:")`, but
`LocalSource::dep_path()` produces `<name>@link+<hash>` (plus sign, not
colon). Real link dep_paths from the parser never matched. The
write-test masked this by hand-constructing `link:web` literals instead
of calling `LocalSource::Link::dep_path()`.

Look the dep up in `graph.packages` (keyed by the same dep_path the
DirectDep carries) and check `local_source` directly — robust to any
future change in the dep_path string format. Test now generates the
dep_path the same way the parser does.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread crates/aube-lockfile/src/npm.rs
Per-workspace hoist trees were built in isolation and merged via
or_insert, so when the root tree already placed `node_modules/<name>`
the workspace still emitted `<importer>/node_modules/<name>` for the
same canonical version. Node's upward `node_modules` walk from
`<importer>/...` resolves the root copy, so the workspace-nested
entry is dead weight — and real `npm install` omits it, so emitting
it produces a diff on every round-trip.

Compute the per-workspace tree, drop top-level segments whose canonical
key already lives at `node_modules/<name>` in placed, and drop their
entire subtree (Node never walks into a `node_modules/<name>` dir that
doesn't exist, so descendants would orphan).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 8bd5012 into main May 1, 2026
18 checks passed
@jdx jdx deleted the codex/workspace-recursive-install branch May 1, 2026 15:29
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