fix(core): keep real dependencies when omitting peers from npm temp installs - #36518
Merged
Conversation
…nstalls npm marks a package as a peer if anything in the tree peer-depends on it, so `--omit=peer` also prunes packages that are genuine dependencies of the package being installed. `ensurePackage` fetches plugins into a temp dir, so an on-demand plugin lost deps it needs at runtime. Use `--legacy-peer-deps` for npm instead: peers are still not installed, but real dependencies are. Bun does not have this behavior, so it keeps `--omit=peer`.
✅ Deploy Preview for nx-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for nx-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
View your CI Pipeline Execution ↗ for commit 8fcd3f3
☁️ Nx Cloud last updated this comment at |
…nstalls [Self-Healing CI Rerun]
Contributor
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
🎓 Learn more about Self-Healing CI on nx.dev
AgentEnder
approved these changes
Jul 30, 2026
FrozenPandaz
added a commit
that referenced
this pull request
Jul 30, 2026
…nstalls (#36518) ## Current Behavior `ensurePackage` installs on-demand plugins into a temp dir. Since #36295 that install passes `--omit=peer` for npm, so peers resolve from the workspace instead of being duplicated into the temp dir. npm flags a package as a peer if **anything** in the tree peer-depends on it — a real `dependencies` edge does not clear the flag. `--omit=peer` therefore also prunes packages that are genuine dependencies of the package being installed. `@nx/detox` hard-depends on `@nx/jest` and `@nx/eslint`; `@nx/web` declares both as optional peers. So installing `@nx/detox` on npm silently drops both: ```console $ npm i -D @nx/detox@22.7.7 --omit=peer --ignore-scripts $ ls node_modules/@nx detox devkit js module-federation nx-darwin-arm64 react rollup vitest web workspace # @nx/jest and @nx/eslint are missing ``` They are still written to `package-lock.json` with `"peer": true`, are absent from `node_modules/.package-lock.json`, and the install exits 0 with no warning. Generating a React Native app with Detox then fails. Observed on 22.7.x, where `ensure-dependencies.ts` imports `@nx/jest/src/utils/versions`: ``` NX Cannot find module '@nx/jest/src/utils/versions' Require stack: - <tmp>/node_modules/@nx/detox/src/generators/application/lib/ensure-dependencies.js ``` On master the same file imports `@nx/jest/internal` instead — a different subpath of the same pruned package, so it fails the same way. This is not Detox-specific: 14 first-party plugins hard-depend on `@nx/jest` or `@nx/eslint`, and several deep-import `@nx/eslint/src/*` at runtime. Any of them fetched on demand in an npm workspace can lose a dependency it needs. ## Expected Behavior npm uses `--legacy-peer-deps` instead. That ignores `peerDependencies` — the intent of #36295 — without pruning real dependencies: ```console $ npm i -D @nx/detox@22.7.7 --legacy-peer-deps --ignore-scripts $ ls node_modules/@nx detox devkit eslint jest js module-federation nx-darwin-arm64 react rollup vite vitest web workspace ``` bun does not over-prune (verified against the same tree), so bun keeps `--omit=peer`. pnpm and yarn are unchanged. ## Related Issue(s) N/A — regression from #36295, which has not been released yet. ## Notes for reviewers **CI will not exercise this change.** Two independent reasons: 1. The macOS Detox e2e only runs when the diff touches `packages/detox`, `packages/react-native`, `packages/expo`, or their e2e projects (`scripts/check-react-native-changes.js`). #36295 touched only `packages/nx`, so the gate skipped it — and it skips this PR too. 2. Even when that job does run, master's e2e uses a shared base workspace that preinstalls the plugins (`<e2e>/nx/proj-backup/npm/node_modules/@nx/` contains detox, jest, eslint, react-native). So `ensurePackage` short-circuits on `require('@nx/detox')` and the temp-install path never executes at all. Verified locally instead. The end-to-end run was done on **22.7.x**, which has no shared base workspace and so genuinely fetches `@nx/detox` on demand — same branch, same e2e, only the flag differing: | temp dir | `node_modules/@nx/` contents | result | | --- | --- | --- | | `--omit=peer` | detox devkit js module-federation nx-darwin-arm64 react rollup vitest web workspace | 4 tests failed | | `--legacy-peer-deps` | detox devkit **eslint jest** js module-federation nx-darwin-arm64 react rollup vite vitest web workspace | 4 tests passed | `ensurePackage` never calls `cleanup()`, so these temp dirs survive and are the reliable signal — `Fetching ...` log lines are absent from passing runs either way because `runCLI` swallows child stdout on success. Also run: - `nx run e2e-detox:e2e-macos-local` on 22.7.x with this change — 2 suites / 4 tests pass - `nx test nx --testPathPatterns=src/utils/package-json.spec.ts` — passes - `tsc -p packages/nx/tsconfig.lib.json --noEmit` — clean - `nx prepush` — passes - the two `npm i` runs above, against published 22.7.7 **Needs backporting to 22.7.x**, which carries the same flag via `74311e713d` and is the active patch line. Neither line has released `--omit=peer` yet (`nx@22.7.7` still ships the old flag-less install command), so there is no user impact today. <!-- polygraph-session-start --> --- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-dark.svg"><img src="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-light.svg" width="16" height="22" align="middle" alt="Polygraph"></picture> <a href="https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/Fix-npm-temp-install-pruning-real-dependencies-via---omitpeer-f7aff304">View session ↗</a></p> <!-- polygraph-session-end --> --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> (cherry picked from commit 1a1fbe9)
polygraph-snapshot-app Bot
pushed a commit
that referenced
this pull request
Aug 11, 2026
…nstalls (#36518) ## Current Behavior `ensurePackage` installs on-demand plugins into a temp dir. Since #36295 that install passes `--omit=peer` for npm, so peers resolve from the workspace instead of being duplicated into the temp dir. npm flags a package as a peer if **anything** in the tree peer-depends on it — a real `dependencies` edge does not clear the flag. `--omit=peer` therefore also prunes packages that are genuine dependencies of the package being installed. `@nx/detox` hard-depends on `@nx/jest` and `@nx/eslint`; `@nx/web` declares both as optional peers. So installing `@nx/detox` on npm silently drops both: ```console $ npm i -D @nx/detox@22.7.7 --omit=peer --ignore-scripts $ ls node_modules/@nx detox devkit js module-federation nx-darwin-arm64 react rollup vitest web workspace # @nx/jest and @nx/eslint are missing ``` They are still written to `package-lock.json` with `"peer": true`, are absent from `node_modules/.package-lock.json`, and the install exits 0 with no warning. Generating a React Native app with Detox then fails. Observed on 22.7.x, where `ensure-dependencies.ts` imports `@nx/jest/src/utils/versions`: ``` NX Cannot find module '@nx/jest/src/utils/versions' Require stack: - <tmp>/node_modules/@nx/detox/src/generators/application/lib/ensure-dependencies.js ``` On master the same file imports `@nx/jest/internal` instead — a different subpath of the same pruned package, so it fails the same way. This is not Detox-specific: 14 first-party plugins hard-depend on `@nx/jest` or `@nx/eslint`, and several deep-import `@nx/eslint/src/*` at runtime. Any of them fetched on demand in an npm workspace can lose a dependency it needs. ## Expected Behavior npm uses `--legacy-peer-deps` instead. That ignores `peerDependencies` — the intent of #36295 — without pruning real dependencies: ```console $ npm i -D @nx/detox@22.7.7 --legacy-peer-deps --ignore-scripts $ ls node_modules/@nx detox devkit eslint jest js module-federation nx-darwin-arm64 react rollup vite vitest web workspace ``` bun does not over-prune (verified against the same tree), so bun keeps `--omit=peer`. pnpm and yarn are unchanged. ## Related Issue(s) N/A — regression from #36295, which has not been released yet. ## Notes for reviewers **CI will not exercise this change.** Two independent reasons: 1. The macOS Detox e2e only runs when the diff touches `packages/detox`, `packages/react-native`, `packages/expo`, or their e2e projects (`scripts/check-react-native-changes.js`). #36295 touched only `packages/nx`, so the gate skipped it — and it skips this PR too. 2. Even when that job does run, master's e2e uses a shared base workspace that preinstalls the plugins (`<e2e>/nx/proj-backup/npm/node_modules/@nx/` contains detox, jest, eslint, react-native). So `ensurePackage` short-circuits on `require('@nx/detox')` and the temp-install path never executes at all. Verified locally instead. The end-to-end run was done on **22.7.x**, which has no shared base workspace and so genuinely fetches `@nx/detox` on demand — same branch, same e2e, only the flag differing: | temp dir | `node_modules/@nx/` contents | result | | --- | --- | --- | | `--omit=peer` | detox devkit js module-federation nx-darwin-arm64 react rollup vitest web workspace | 4 tests failed | | `--legacy-peer-deps` | detox devkit **eslint jest** js module-federation nx-darwin-arm64 react rollup vite vitest web workspace | 4 tests passed | `ensurePackage` never calls `cleanup()`, so these temp dirs survive and are the reliable signal — `Fetching ...` log lines are absent from passing runs either way because `runCLI` swallows child stdout on success. Also run: - `nx run e2e-detox:e2e-macos-local` on 22.7.x with this change — 2 suites / 4 tests pass - `nx test nx --testPathPatterns=src/utils/package-json.spec.ts` — passes - `tsc -p packages/nx/tsconfig.lib.json --noEmit` — clean - `nx prepush` — passes - the two `npm i` runs above, against published 22.7.7 **Needs backporting to 22.7.x**, which carries the same flag via `74311e713d` and is the active patch line. Neither line has released `--omit=peer` yet (`nx@22.7.7` still ships the old flag-less install command), so there is no user impact today. <!-- polygraph-session-start --> --- <p><picture><source media="(prefers-color-scheme: dark)" srcset="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-dark.svg"><img src="https://static.ops.cloud.nx.app/polygraph/session-logo-v4-light.svg" width="16" height="22" align="middle" alt="Polygraph"></picture> <a href="https://app.trypolygraph.com/orgs/6a061dcb561c062131116eca/sessions/Fix-npm-temp-install-pruning-real-dependencies-via---omitpeer-f7aff304">View session ↗</a></p> <!-- polygraph-session-end --> --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Current Behavior
ensurePackageinstalls on-demand plugins into a temp dir. Since #36295 that install passes--omit=peerfor npm, so peers resolve from the workspace instead of being duplicated into the temp dir.npm flags a package as a peer if anything in the tree peer-depends on it — a real
dependenciesedge does not clear the flag.--omit=peertherefore also prunes packages that are genuine dependencies of the package being installed.@nx/detoxhard-depends on@nx/jestand@nx/eslint;@nx/webdeclares both as optional peers. So installing@nx/detoxon npm silently drops both:They are still written to
package-lock.jsonwith"peer": true, are absent fromnode_modules/.package-lock.json, and the install exits 0 with no warning.Generating a React Native app with Detox then fails. Observed on 22.7.x, where
ensure-dependencies.tsimports@nx/jest/src/utils/versions:On master the same file imports
@nx/jest/internalinstead — a different subpath of the same pruned package, so it fails the same way.This is not Detox-specific: 14 first-party plugins hard-depend on
@nx/jestor@nx/eslint, and several deep-import@nx/eslint/src/*at runtime. Any of them fetched on demand in an npm workspace can lose a dependency it needs.Expected Behavior
npm uses
--legacy-peer-depsinstead. That ignorespeerDependencies— the intent of #36295 — without pruning real dependencies:bun does not over-prune (verified against the same tree), so bun keeps
--omit=peer. pnpm and yarn are unchanged.Related Issue(s)
N/A — regression from #36295, which has not been released yet.
Notes for reviewers
CI will not exercise this change. Two independent reasons:
packages/detox,packages/react-native,packages/expo, or their e2e projects (scripts/check-react-native-changes.js). fix(core): omit peer dependencies when installing packages to a temp dir #36295 touched onlypackages/nx, so the gate skipped it — and it skips this PR too.<e2e>/nx/proj-backup/npm/node_modules/@nx/contains detox, jest, eslint, react-native). SoensurePackageshort-circuits onrequire('@nx/detox')and the temp-install path never executes at all.Verified locally instead. The end-to-end run was done on 22.7.x, which has no shared base workspace and so genuinely fetches
@nx/detoxon demand — same branch, same e2e, only the flag differing:node_modules/@nx/contents--omit=peer--legacy-peer-depsensurePackagenever callscleanup(), so these temp dirs survive and are the reliable signal —Fetching ...log lines are absent from passing runs either way becauserunCLIswallows child stdout on success.Also run:
nx run e2e-detox:e2e-macos-localon 22.7.x with this change — 2 suites / 4 tests passnx test nx --testPathPatterns=src/utils/package-json.spec.ts— passestsc -p packages/nx/tsconfig.lib.json --noEmit— cleannx prepush— passesnpm iruns above, against published 22.7.7Needs backporting to 22.7.x, which carries the same flag via
74311e713dand is the active patch line. Neither line has released--omit=peeryet (nx@22.7.7still ships the old flag-less install command), so there is no user impact today.