diff --git a/.changeset/olive-apricots-change.md b/.changeset/olive-apricots-change.md new file mode 100644 index 0000000000..ada22f5871 --- /dev/null +++ b/.changeset/olive-apricots-change.md @@ -0,0 +1,6 @@ +--- +"@pnpm/resolve-dependencies": patch +"pnpm": patch +--- + +Optimize peers resolution to avoid out-of-memory exceptions in some rare cases, when there are too many circular dependencies and peer dependencies [#7149](https://github.com/pnpm/pnpm/pull/7149). diff --git a/pkg-manager/resolve-dependencies/src/resolvePeers.ts b/pkg-manager/resolve-dependencies/src/resolvePeers.ts index 5ffb0e270e..eff66ad9a6 100644 --- a/pkg-manager/resolve-dependencies/src/resolvePeers.ts +++ b/pkg-manager/resolve-dependencies/src/resolvePeers.ts @@ -9,6 +9,7 @@ import type { } from '@pnpm/types' import { depPathToFilename, createPeersFolderSuffix } from '@pnpm/dependency-path' import mapValues from 'ramda/src/map' +import partition from 'ramda/src/partition' import pick from 'ramda/src/pick' import scan from 'ramda/src/scan' import { @@ -497,7 +498,12 @@ function resolvePeersOfChildren ( const allResolvedPeers = new Map() const allMissingPeers = new Set() - for (const childNodeId of Object.values(children)) { + // Partition children based on whether they're repeated in parentPkgs. + // This impacts the efficiency of graph traversal and prevents potential out-of-memory errors.mes can even lead to out-of-memory exceptions. + const [repeated, notRepeated] = partition(([alias]) => parentPkgs[alias] != null, Object.entries(children)) + + // Resolving non-repeated nodes before repeated nodes proved to be slightly faster. + for (const [, childNodeId] of [...notRepeated, ...repeated]) { const { resolvedPeers, missingPeers } = resolvePeersOfNode(childNodeId, parentPkgs, ctx) for (const [k, v] of resolvedPeers) { allResolvedPeers.set(k, v)