Skip to content

fix(pnpm-collector): find nested deps in pnpm hoisted workspaces - #10234

Merged
mmaietta merged 6 commits into
masterfrom
fix/pnpm-hoisted-workspace-appdir
Sep 25, 2026
Merged

mmaietta merged 6 commits into
masterfrom
fix/pnpm-hoisted-workspace-appdir

Conversation

@claude

@claude claude Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Requested by Mike Maietta · Slack thread

In a pnpm hoisted (nodeLinker: hoisted) workspace the app package usually has no node_modules of its own, so the pnpm collector detected the layout as isolated and never searched the workspace root. It then packaged out-of-range copies of nested deps, for example the root readable-stream@3.6.2 in place of lazystream's nested readable-stream@2.3.8, which is the same symptom as #10228. The collector now finds the workspace root by walking up to the nearest pnpm-workspace.yaml and uses it both for hoisted detection and for the in-range lookup. This follows up on #10228, and the same fix is being added to the v26 backport #10230. It touches the same files as #10232, so whichever PR merges second may need a small rebase. The packaging integration tests couldn't run locally because the Electron download is blocked, so they rely on CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF


Generated by Claude Code

…he app is a hoisted workspace package

With `nodeLinker: hoisted`, pnpm installs a workspace package's dependencies
into the workspace root's node_modules, and the app dir has no node_modules
of its own unless it needs a conflicting version. The collector runs with
rootDir = the app dir, so:

- `isHoisted` scanned `<app>/node_modules`, found nothing, and reported the
  flat default, disabling the downward search; and
- even when hoisted, the downward BFS started at the missing
  `<app>/node_modules`, never reaching
  `<root>/node_modules/lazystream/node_modules/readable-stream`.

The override fallback then accepted the root readable-stream@3.6.2 for
lazystream's ^2 dependency (#10228's symptom, still present on master for
workspace apps). Detect the layout from the workspace root when the app dir
has no packages, and add a range-satisfying search from the workspace root
after the app-dir search.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF
… changeset

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF
@changeset-bot

changeset-bot Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e7ee366

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
app-builder-lib Patch
dmg-builder Patch
electron-builder-squirrel-windows Patch
electron-builder Patch
electron-forge-maker-appimage Patch
electron-forge-maker-nsis-web Patch
electron-forge-maker-nsis Patch
electron-forge-maker-snap Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@claude

claude Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

CI note: Test e2e Auto Updater Linux (appimage, true) failed for infrastructure reasons, not because of this PR (job 107710412913)

The job failed while building the Docker test image, before any test ran. It stopped at the first RUN apt-get update in test/src/updater/dockerfile-appimage because the Ubuntu mirror returned a file whose hash didn't match its index:

E: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/jammy-updates/main/binary-amd64/Packages.gz  Hash Sum mismatch
   Last modification reported: Thu, 24 Sep 2026 04:16:25 +0000
   Release file created at: Thu, 24 Sep 2026 12:35:36 +0000
E: Failed to fetch http://archive.ubuntu.com/ubuntu/dists/jammy-updates/universe/binary-amd64/Packages.gz
ERROR: failed to solve: process "/bin/sh -c apt-get update && ..." did not complete successfully: exit code: 100

Why it isn't caused by this PR:

There's no code fix to make. The failed job needs a re-run once the run finishes; GitHub won't re-run a single job while the run is in progress.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF
…space

Replace the mocked node_modules trees in pnpmHoistedTest with packTester
tests in HoistedNodeModuleTest that run a real `pnpm install` (pnpm pinned
to 11.26.0 via `packageManager`) and pack the app:

- hoisted workspace (#10228): app is a workspace package with no own
  node_modules, `nodeLinker: hoisted` in pnpm-workspace.yaml
- hoisted single-package project
- isolated (.pnpm store) project
- isolated project with a `link:` dependency
- project without dependencies

Each asserts the layout the collector detects (isHoisted) on the real
install and the exact packaged node_modules tree, including lazystream's
nested readable-stream@2.3.8 next to the root readable-stream@3.6.2.
Only the ModuleManager skipDownwardSearch unit test, which a real install
cannot observe, stays mocked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF
…out tests

HoistedNodeModuleTest resolves to HoistedNodeModuleTest.js.win.snap on
win32, which was missing the five new "pnpm v11 ..." entries, so CI
(which never writes snapshots) failed them as mismatched on Windows.
The entries are the same `{ linux: [] }` dir-target artifact list as the
POSIX baseline.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Evsxm8m75QrJA3XqkuaXNF
@mmaietta
mmaietta marked this pull request as ready for review September 25, 2026 17:12

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I reviewed this PR and didn't find any bugs. Because it changes core pnpm dependency-resolution/packaging logic (workspace-root discovery, hoisted-layout detection, and the fallback search order used to pick nested dependency versions), a human look would still be worthwhile.

What was reviewed: the new workspaceRoot/layoutRoots walk-up and the refactored scanNodeModulesLayout helper used by isHoisted; the extended fallback order in locateFromDepOrRoot that now also checks the workspace root; and the new pnpm v11 test scenarios (hoisted workspace, hoisted single project, isolated project, isolated project with a link: dependency, no-dependencies project) plus their lockfile fixtures and snapshots.

Extended reasoning...

The change touches app-builder-lib's pnpm node-modules collector, specifically the algorithm that decides which on-disk copy of a version-conflicted transitive dependency gets packaged into the app; it has no auth/crypto/permissions surface, but a wrong result silently ships the wrong dependency version in a production app, which is what motivated the fix. The diff is non-trivial (new lazy workspace-root traversal, a reusable layout-scan helper, and an extra fallback search location) and several plausible edge cases (e.g. mixed isolated/hoisted layouts across rootDir and workspace root) were already raised and investigated by the automated bug hunt and ruled out rather than fixed, which is exactly the kind of judgment call a human maintainer should also weigh in on. Test coverage is solid: five new pnpm v11 scenarios directly exercise hoisted workspace, hoisted single-project, isolated, isolated-with-link, and no-dependency layouts against the described bug (lazystream's nested readable-stream@ 2 vs the root's readable-stream@ 3), with matching lockfile fixtures and updated snapshots.

@mmaietta
mmaietta merged commit a787a5a into master Sep 25, 2026
62 of 63 checks passed
@mmaietta
mmaietta deleted the fix/pnpm-hoisted-workspace-appdir branch September 25, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants