diff --git a/.changeset/serious-eggs-clap.md b/.changeset/serious-eggs-clap.md new file mode 100644 index 0000000000..0a8a5aa70b --- /dev/null +++ b/.changeset/serious-eggs-clap.md @@ -0,0 +1,6 @@ +--- +"@pnpm/resolve-dependencies": patch +"pnpm": patch +--- + +Reduce memory usage during peer dependencies resolution. diff --git a/pkg-manager/resolve-dependencies/src/resolvePeers.ts b/pkg-manager/resolve-dependencies/src/resolvePeers.ts index bf444fc7cd..258080c35d 100644 --- a/pkg-manager/resolve-dependencies/src/resolvePeers.ts +++ b/pkg-manager/resolve-dependencies/src/resolvePeers.ts @@ -105,6 +105,8 @@ export async function resolvePeers ( const peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects = {} const finishingList: FinishingResolutionPromise[] = [] + const peersCache = new Map() + const purePkgs = new Set() for (const { directNodeIdsByAlias, topParents, rootDir, id } of opts.projects) { const peerDependencyIssues: Pick = { bad: {}, missing: {} } const pkgsByName = Object.fromEntries(Object.entries({ @@ -129,9 +131,9 @@ export async function resolvePeers ( pathsByNodeId, pathsByNodeIdPromises, depPathsByPkgId, - peersCache: new Map(), + peersCache, peerDependencyIssues, - purePkgs: new Set(), + purePkgs, peersSuffixMaxLength: opts.peersSuffixMaxLength, rootDir, virtualStoreDir: opts.virtualStoreDir, @@ -325,16 +327,23 @@ function createPkgsByName ( return parentRefs } +interface MissingPeerInfo { + range: string + optional: boolean +} + +type MissingPeers = Map + interface PeersCacheItem { depPath: pDefer.DeferredPromise resolvedPeers: Map - missingPeers: Set + missingPeers: MissingPeers } type PeersCache = Map interface PeersResolution { - missingPeers: Set + missingPeers: MissingPeers resolvedPeers: Map } @@ -377,7 +386,7 @@ async function resolvePeersOfNode ( } ): Promise { const node = ctx.dependenciesTree.get(nodeId)! - if (node.depth === -1) return { resolvedPeers: new Map(), missingPeers: new Set() } + if (node.depth === -1) return { resolvedPeers: new Map(), missingPeers: new Map() } const resolvedPackage = node.resolvedPackage as T if ( ctx.purePkgs.has(resolvedPackage.pkgIdWithPatchHash) && @@ -386,7 +395,7 @@ async function resolvePeersOfNode ( ) { ctx.pathsByNodeId.set(nodeId, resolvedPackage.pkgIdWithPatchHash as unknown as DepPath) ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(resolvedPackage.pkgIdWithPatchHash as unknown as DepPath) - return { resolvedPeers: new Map(), missingPeers: new Set() } + return { resolvedPeers: new Map(), missingPeers: new Map() } } if (typeof node.children === 'function') { node.children = node.children() @@ -424,6 +433,17 @@ async function resolvePeersOfNode ( } const hit = findHit(ctx, parentPkgs, resolvedPackage.pkgIdWithPatchHash) if (hit != null) { + for (const [peerName, { range: wantedRange, optional }] of hit.missingPeers.entries()) { + if (ctx.peerDependencyIssues.missing[peerName] == null) { + ctx.peerDependencyIssues.missing[peerName] = [] + } + const { parents } = getLocationFromParentNodeIds(ctx) + ctx.peerDependencyIssues.missing[peerName].push({ + optional, + parents, + wantedRange, + }) + } return { missingPeers: hit.missingPeers, finishing: (async () => { @@ -447,7 +467,7 @@ async function resolvePeersOfNode ( }) const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0 - ? { resolvedPeers: new Map(), missingPeers: new Set() } + ? { resolvedPeers: new Map(), missingPeers: new Map() } : _resolvePeers({ currentDepth: node.depth, dependenciesTree: ctx.dependenciesTree, @@ -466,12 +486,12 @@ async function resolvePeersOfNode ( } allResolvedPeers.delete(node.resolvedPackage.name) - const allMissingPeers = new Set() - for (const peer of missingPeersOfChildren) { - allMissingPeers.add(peer) + const allMissingPeers = new Map() + for (const [peer, range] of missingPeersOfChildren.entries()) { + allMissingPeers.set(peer, range) } - for (const peer of missingPeers) { - allMissingPeers.add(peer) + for (const [peer, range] of missingPeers.entries()) { + allMissingPeers.set(peer, range) } let cache: PeersCacheItem @@ -578,7 +598,7 @@ async function resolvePeersOfNode ( transitivePeerDependencies.add(unknownPeer) } } - for (const unknownPeer of missingPeersOfChildren) { + for (const unknownPeer of missingPeersOfChildren.keys()) { if (!peerDependencies[unknownPeer]) { transitivePeerDependencies.add(unknownPeer) } @@ -656,7 +676,7 @@ function findHit (ctx: { return false } } - for (const missingPeer of cache.missingPeers) { + for (const missingPeer of cache.missingPeers.keys()) { if (parentPkgs[missingPeer]) return false } return true @@ -759,7 +779,7 @@ async function resolvePeersOfChildren ( } ): Promise }> { const allResolvedPeers = new Map() - const allMissingPeers = new Set() + const allMissingPeers = new Map() // Partition children based on whether they're repeated in parentPkgs. // This impacts the efficiency of graph traversal and prevents potential out-of-memory errors. @@ -812,8 +832,8 @@ async function resolvePeersOfChildren ( edges.push(peerNodeId) } graph.push([childNodeId, edges]) - for (const missingPeer of missingPeers) { - allMissingPeers.add(missingPeer) + for (const [missingPeer, range] of missingPeers.entries()) { + allMissingPeers.set(missingPeer, range) } } if (calculateDepPaths.length) { @@ -846,7 +866,7 @@ function _resolvePeers ( } ): PeersResolution { const resolvedPeers = new Map() - const missingPeers = new Set() + const missingPeers = new Map() for (const [peerName, { version, optional }] of Object.entries(ctx.resolvedPackage.peerDependencies)) { const peerVersionRange = version.replace(/^workspace:/, '') @@ -854,7 +874,7 @@ function _resolvePeers ( const optionalPeer = optional === true if (!resolved) { - missingPeers.add(peerName) + missingPeers.set(peerName, { range: version, optional: optionalPeer }) const location = getLocationFromParentNodeIds(ctx) if (!ctx.peerDependencyIssues.missing[peerName]) { ctx.peerDependencyIssues.missing[peerName] = []