From fee263822c1f9644b5d443e99ac54dd234e014e5 Mon Sep 17 00:00:00 2001 From: Bogdan Savluk Date: Mon, 3 Jul 2023 00:19:23 +0200 Subject: [PATCH] refactor(resolve-dependencies): use Maps and Sets instead of objects (#6749) --- .changeset/fair-apples-mix.md | 5 + .../src/resolveDependencies.ts | 25 +-- .../src/resolveDependencyTree.ts | 12 +- .../resolve-dependencies/src/resolvePeers.ts | 200 ++++++++++-------- .../test/dedupeDepPaths.test.ts | 15 +- .../resolve-dependencies/test/resolvePeers.ts | 163 +++++++------- 6 files changed, 227 insertions(+), 193 deletions(-) create mode 100644 .changeset/fair-apples-mix.md diff --git a/.changeset/fair-apples-mix.md b/.changeset/fair-apples-mix.md new file mode 100644 index 0000000000..3f202ab3e0 --- /dev/null +++ b/.changeset/fair-apples-mix.md @@ -0,0 +1,5 @@ +--- +"@pnpm/resolve-dependencies": patch +--- + +Refactor resolve-dependencies to use maps and sets instead of objects diff --git a/pkg-manager/resolve-dependencies/src/resolveDependencies.ts b/pkg-manager/resolve-dependencies/src/resolveDependencies.ts index b0ea1bca3f..38143d77ed 100644 --- a/pkg-manager/resolve-dependencies/src/resolveDependencies.ts +++ b/pkg-manager/resolve-dependencies/src/resolveDependencies.ts @@ -89,12 +89,13 @@ export type DependenciesTreeNode = { depth: -1 }) -export interface DependenciesTree { - // a node ID is the join of the package's keypath with a colon - // E.g., a subdeps node ID which parent is `foo` will be - // registry.npmjs.org/foo/1.0.0:registry.npmjs.org/bar/1.0.0 - [nodeId: string]: DependenciesTreeNode -} +export type DependenciesTree = Map< +// a node ID is the join of the package's keypath with a colon +// E.g., a subdeps node ID which parent is `foo` will be +// registry.npmjs.org/foo/1.0.0:registry.npmjs.org/bar/1.0.0 +string, +DependenciesTreeNode +> export type ResolvedPackagesByDepPath = Record @@ -680,7 +681,7 @@ async function resolveDependenciesOfDependency ( if (resolveDependencyResult == null) return { resolveDependencyResult: null } if (resolveDependencyResult.isLinkedDependency) { - ctx.dependenciesTree[createNodeIdForLinkedLocalPkg(ctx.lockfileDir, resolveDependencyResult.resolution.directory)] = { + ctx.dependenciesTree.set(createNodeIdForLinkedLocalPkg(ctx.lockfileDir, resolveDependencyResult.resolution.directory), { children: {}, depth: -1, installable: true, @@ -688,7 +689,7 @@ async function resolveDependenciesOfDependency ( name: resolveDependencyResult.name, version: resolveDependencyResult.version, }, - } + }) return { resolveDependencyResult } } if (!resolveDependencyResult.isNew) { @@ -815,7 +816,7 @@ async function resolveChildren ( alias: child.alias, depPath: child.depPath, })) - ctx.dependenciesTree[parentPkg.nodeId] = { + ctx.dependenciesTree.set(parentPkg.nodeId, { children: pkgAddresses.reduce((chn, child) => { chn[child.alias] = (child as PkgAddress).nodeId ?? child.pkgId return chn @@ -823,7 +824,7 @@ async function resolveChildren ( depth: parentDepth, installable: parentPkg.installable, resolvedPackage: ctx.resolvedPackagesByDepPath[parentPkg.depPath], - } + }) return resolvingPeers } @@ -1304,8 +1305,8 @@ async function resolveDependency ( ctx.resolvedPackagesByDepPath[depPath].fetchingBundledManifest = pkgResponse.bundledManifest! } - if (ctx.dependenciesTree[nodeId]) { - ctx.dependenciesTree[nodeId].depth = Math.min(ctx.dependenciesTree[nodeId].depth, options.currentDepth) + if (ctx.dependenciesTree.has(nodeId)) { + ctx.dependenciesTree.get(nodeId)!.depth = Math.min(ctx.dependenciesTree.get(nodeId)!.depth, options.currentDepth) } else { ctx.pendingNodes.push({ alias: wantedDependency.alias || pkg.name, diff --git a/pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts b/pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts index fb9c571377..acaeffe548 100644 --- a/pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts +++ b/pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts @@ -101,7 +101,7 @@ export async function resolveDependencyTree ( childrenByParentDepPath: {} as ChildrenByParentDepPath, currentLockfile: opts.currentLockfile, defaultTag: opts.tag, - dependenciesTree: {} as DependenciesTree, + dependenciesTree: new Map() as DependenciesTree, dryRun: opts.dryRun, engineStrict: opts.engineStrict, force: opts.force, @@ -168,13 +168,13 @@ export async function resolveDependencyTree ( const directDepsByImporterId = zipObj(importers.map(({ id }) => id), pkgAddressesByImporters) ctx.pendingNodes.forEach((pendingNode) => { - ctx.dependenciesTree[pendingNode.nodeId] = { + ctx.dependenciesTree.set(pendingNode.nodeId, { children: () => buildTree(ctx, pendingNode.nodeId, pendingNode.resolvedPackage.id, ctx.childrenByParentDepPath[pendingNode.resolvedPackage.depPath], pendingNode.depth + 1, pendingNode.installable), depth: pendingNode.depth, installable: pendingNode.installable, resolvedPackage: pendingNode.resolvedPackage, - } + }) }) const resolvedImporters = {} as { @@ -197,7 +197,7 @@ export async function resolveDependencyTree ( if (dep.isLinkedDependency === true) { return dep } - const resolvedPackage = ctx.dependenciesTree[dep.nodeId].resolvedPackage as ResolvedPackage + const resolvedPackage = ctx.dependenciesTree.get(dep.nodeId)!.resolvedPackage as ResolvedPackage return { alias: dep.alias, dev: resolvedPackage.dev, @@ -254,7 +254,7 @@ function buildTree ( const childNodeId = createNodeId(parentNodeId, child.depPath) childrenNodeIds[child.alias] = childNodeId installable = installable && !ctx.skipped.has(child.depPath) - ctx.dependenciesTree[childNodeId] = { + ctx.dependenciesTree.set(childNodeId, { children: () => buildTree(ctx, childNodeId, child.depPath, @@ -265,7 +265,7 @@ function buildTree ( depth, installable, resolvedPackage: ctx.resolvedPackagesByDepPath[child.depPath], - } + }) } return childrenNodeIds } diff --git a/pkg-manager/resolve-dependencies/src/resolvePeers.ts b/pkg-manager/resolve-dependencies/src/resolvePeers.ts index b0fd68528b..5ffb0e270e 100644 --- a/pkg-manager/resolve-dependencies/src/resolvePeers.ts +++ b/pkg-manager/resolve-dependencies/src/resolvePeers.ts @@ -10,7 +10,6 @@ import type { import { depPathToFilename, createPeersFolderSuffix } from '@pnpm/dependency-path' import mapValues from 'ramda/src/map' import pick from 'ramda/src/pick' -import pickBy from 'ramda/src/pickBy' import scan from 'ramda/src/scan' import { type DependenciesTree, @@ -31,7 +30,7 @@ export interface GenericDependenciesGraphNode { installable: boolean isBuilt?: boolean isPure: boolean - resolvedPeerNames: string[] + resolvedPeerNames: Set } export type PartialResolvedPackage = Pick ( peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects } { const depGraph: GenericDependenciesGraph = {} - const pathsByNodeId: Record = {} - const depPathsByPkgId: Record = {} + const pathsByNodeId = new Map() + const depPathsByPkgId = new Map>() const _createPkgsByName = createPkgsByName.bind(null, opts.dependenciesTree) const rootPkgsByName = opts.resolvePeersFromWorkspaceRoot ? getRootPkgsByName(opts.dependenciesTree, opts.projects) : {} const peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects = {} @@ -104,15 +103,15 @@ export function resolvePeers ( } Object.values(depGraph).forEach((node) => { - node.children = mapValues((childNodeId) => pathsByNodeId[childNodeId] ?? childNodeId, node.children) + node.children = mapValues((childNodeId) => pathsByNodeId.get(childNodeId) ?? childNodeId, node.children) }) const dependenciesByProjectId: { [id: string]: Record } = {} for (const { directNodeIdsByAlias, id } of opts.projects) { - dependenciesByProjectId[id] = mapValues((nodeId) => pathsByNodeId[nodeId], directNodeIdsByAlias) + dependenciesByProjectId[id] = mapValues((nodeId) => pathsByNodeId.get(nodeId)!, directNodeIdsByAlias) } if (opts.dedupePeerDependents) { - const duplicates = Object.values(depPathsByPkgId).filter((item) => item.length > 1) + const duplicates = Array.from(depPathsByPkgId.values()).filter((item) => item.size > 1) const allDepPathsMap = deduplicateAll(depGraph, duplicates) for (const { id } of opts.projects) { dependenciesByProjectId[id] = mapValues((depPath) => allDepPathsMap[depPath] ?? depPath, dependenciesByProjectId[id]) @@ -126,12 +125,12 @@ export function resolvePeers ( } function nodeDepsCount (node: GenericDependenciesGraphNode) { - return Object.keys(node.children).length + node.resolvedPeerNames.length + return Object.keys(node.children).length + node.resolvedPeerNames.size } function deduplicateAll ( depGraph: GenericDependenciesGraph, - duplicates: string[][] + duplicates: Array> ): Record { const { depPathsMap, remainingDuplicates } = deduplicateDepPaths(duplicates, depGraph) if (remainingDuplicates.length === duplicates.length) { @@ -150,16 +149,16 @@ function deduplicateAll ( } function deduplicateDepPaths ( - duplicates: string[][], + duplicates: Array>, depGraph: GenericDependenciesGraph ) { const depCountSorter = (depPath1: string, depPath2: string) => nodeDepsCount(depGraph[depPath1]) - nodeDepsCount(depGraph[depPath2]) const depPathsMap: Record = {} - const remainingDuplicates: string[][] = [] + const remainingDuplicates: Array> = [] for (const depPaths of duplicates) { - const unresolvedDepPaths = new Set(depPaths) - let currentDepPaths = depPaths.sort(depCountSorter) + const unresolvedDepPaths = new Set(depPaths.values()) + let currentDepPaths = [...depPaths].sort(depCountSorter) while (currentDepPaths.length) { const depPath1 = currentDepPaths.pop()! @@ -179,7 +178,7 @@ function deduplicateDepPaths ( } if (unresolvedDepPaths.size) { - remainingDuplicates.push([...unresolvedDepPaths]) + remainingDuplicates.push(unresolvedDepPaths) } } return { @@ -195,11 +194,16 @@ function isCompatibleAndHasMoreDeps ( ) { const node1 = depGraph[depPath1] const node2 = depGraph[depPath2] - const node1DepPaths = Object.values(node1.children) + if (nodeDepsCount(node1) < nodeDepsCount(node2)) return false + + const node1DepPathsSet = new Set(Object.values(node1.children)) const node2DepPaths = Object.values(node2.children) - return nodeDepsCount(node1) > nodeDepsCount(node2) && - node2DepPaths.every((depPath) => node1DepPaths.includes(depPath)) && - node2.resolvedPeerNames.every((depPath) => node1.resolvedPeerNames.includes(depPath)) + if (!node2DepPaths.every((depPath) => node1DepPathsSet.has(depPath))) return false + + for (const depPath of node2.resolvedPeerNames) { + if (!node1.resolvedPeerNames.has(depPath)) return false + } + return true } function getRootPkgsByName (dependenciesTree: DependenciesTree, projects: ProjectToResolve[]) { @@ -219,7 +223,7 @@ function createPkgsByName ( .keys(directNodeIdsByAlias) .map((alias) => ({ alias, - node: dependenciesTree[directNodeIdsByAlias[alias]], + node: dependenciesTree.get(directNodeIdsByAlias[alias])!, nodeId: directNodeIdsByAlias[alias], })) ) @@ -241,20 +245,20 @@ function createPkgsByName ( interface PeersCacheItem { depPath: string - resolvedPeers: Array<[string, string]> - missingPeers: string[] + resolvedPeers: Map + missingPeers: Set } type PeersCache = Map interface PeersResolution { - missingPeers: string[] - resolvedPeers: Record + missingPeers: Set + resolvedPeers: Map } interface ResolvePeersContext { - pathsByNodeId: { [nodeId: string]: string } - depPathsByPkgId?: Record + pathsByNodeId: Map + depPathsByPkgId?: Map> } function resolvePeersOfNode ( @@ -271,16 +275,16 @@ function resolvePeersOfNode ( lockfileDir: string } ): PeersResolution { - const node = ctx.dependenciesTree[nodeId] - if (node.depth === -1) return { resolvedPeers: {}, missingPeers: [] } + const node = ctx.dependenciesTree.get(nodeId)! + if (node.depth === -1) return { resolvedPeers: new Map(), missingPeers: new Set() } const resolvedPackage = node.resolvedPackage as T if ( ctx.purePkgs.has(resolvedPackage.depPath) && ctx.depGraph[resolvedPackage.depPath].depth <= node.depth && Object.keys(resolvedPackage.peerDependencies).length === 0 ) { - ctx.pathsByNodeId[nodeId] = resolvedPackage.depPath - return { resolvedPeers: {}, missingPeers: [] } + ctx.pathsByNodeId.set(nodeId, resolvedPackage.depPath) + return { resolvedPeers: new Map(), missingPeers: new Set() } } if (typeof node.children === 'function') { node.children = node.children() @@ -293,36 +297,39 @@ function resolvePeersOfNode ( toPkgByName( Object.entries(children).map(([alias, nodeId]) => ({ alias, - node: ctx.dependenciesTree[nodeId], + node: ctx.dependenciesTree.get(nodeId)!, nodeId, })) ) ) - const hit = ctx.peersCache.get(resolvedPackage.depPath)?.find((cache) => - cache.resolvedPeers - .every(([name, cachedNodeId]) => { - const parentPkgNodeId = parentPkgs[name]?.nodeId - if (!parentPkgNodeId || !cachedNodeId) return false - if (parentPkgNodeId === cachedNodeId) return true - if ( - ctx.pathsByNodeId[cachedNodeId] && - ctx.pathsByNodeId[cachedNodeId] === ctx.pathsByNodeId[parentPkgNodeId] - ) return true - if (!ctx.dependenciesTree[parentPkgNodeId] && parentPkgNodeId.startsWith('link:')) { - return false - } - const parentDepPath = (ctx.dependenciesTree[parentPkgNodeId].resolvedPackage as T).depPath - if (!ctx.purePkgs.has(parentDepPath)) return false - const cachedDepPath = (ctx.dependenciesTree[cachedNodeId].resolvedPackage as T).depPath - return parentDepPath === cachedDepPath - }) && cache.missingPeers.every((missingPeer) => !parentPkgs[missingPeer]) - ) + const hit = ctx.peersCache.get(resolvedPackage.depPath)?.find((cache) => { + for (const [name, cachedNodeId] of cache.resolvedPeers) { + const parentPkgNodeId = parentPkgs[name]?.nodeId + if (!parentPkgNodeId || !cachedNodeId) return false + if (parentPkgNodeId === cachedNodeId) continue + if ( + ctx.pathsByNodeId.has(cachedNodeId) && + ctx.pathsByNodeId.get(cachedNodeId) === ctx.pathsByNodeId.get(parentPkgNodeId) + ) continue + if (!ctx.dependenciesTree.has(parentPkgNodeId) && parentPkgNodeId.startsWith('link:')) { + return false + } + const parentDepPath = (ctx.dependenciesTree.get(parentPkgNodeId)!.resolvedPackage as T).depPath + if (!ctx.purePkgs.has(parentDepPath)) return false + const cachedDepPath = (ctx.dependenciesTree.get(cachedNodeId)!.resolvedPackage as T).depPath + if (parentDepPath !== cachedDepPath) return false + } + for (const missingPeer of cache.missingPeers) { + if (parentPkgs[missingPeer]) return false + } + return true + }) if (hit != null) { - ctx.pathsByNodeId[nodeId] = hit.depPath + ctx.pathsByNodeId.set(nodeId, hit.depPath) ctx.depGraph[hit.depPath].depth = Math.min(ctx.depGraph[hit.depPath].depth, node.depth) return { missingPeers: hit.missingPeers, - resolvedPeers: Object.fromEntries(hit.resolvedPeers), + resolvedPeers: hit.resolvedPeers, } } @@ -332,7 +339,7 @@ function resolvePeersOfNode ( } = resolvePeersOfChildren(children, parentPkgs, ctx) const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0 - ? { resolvedPeers: {}, missingPeers: [] } + ? { resolvedPeers: new Map(), missingPeers: new Set() } : _resolvePeers({ currentDepth: node.depth, dependenciesTree: ctx.dependenciesTree, @@ -344,16 +351,26 @@ function resolvePeersOfNode ( rootDir: ctx.rootDir, }) - const allResolvedPeers = Object.assign(unknownResolvedPeersOfChildren, resolvedPeers) - delete allResolvedPeers[node.resolvedPackage.name] - const allMissingPeers = Array.from(new Set([...missingPeersOfChildren, ...missingPeers])) + const allResolvedPeers = unknownResolvedPeersOfChildren + for (const [k, v] of resolvedPeers) { + allResolvedPeers.set(k, v) + } + allResolvedPeers.delete(node.resolvedPackage.name) + + const allMissingPeers = new Set() + for (const peer of missingPeersOfChildren) { + allMissingPeers.add(peer) + } + for (const peer of missingPeers) { + allMissingPeers.add(peer) + } let depPath: string - if (Object.keys(allResolvedPeers).length === 0) { + if (allResolvedPeers.size === 0) { depPath = resolvedPackage.depPath } else { const peersFolderSuffix = createPeersFolderSuffix( - Object.entries(allResolvedPeers) + [...allResolvedPeers.entries()] .map(([alias, nodeId]) => { if (nodeId.startsWith('link:')) { const linkedDir = nodeId.slice(5) @@ -362,7 +379,7 @@ function resolvePeersOfNode ( version: filenamify(linkedDir, { replacement: '+' }), } } - const { name, version } = ctx.dependenciesTree[nodeId].resolvedPackage + const { name, version } = ctx.dependenciesTree.get(nodeId)!.resolvedPackage return { name, version } }) ) @@ -370,14 +387,14 @@ function resolvePeersOfNode ( } const localLocation = path.join(ctx.virtualStoreDir, depPathToFilename(depPath)) const modules = path.join(localLocation, 'node_modules') - const isPure = Object.keys(allResolvedPeers).length === 0 && allMissingPeers.length === 0 + const isPure = allResolvedPeers.size === 0 && allMissingPeers.size === 0 if (isPure) { ctx.purePkgs.add(resolvedPackage.depPath) } else { const cache = { missingPeers: allMissingPeers, depPath, - resolvedPeers: Object.entries(allResolvedPeers), + resolvedPeers: allResolvedPeers, } if (ctx.peersCache.has(resolvedPackage.depPath)) { ctx.peersCache.get(resolvedPackage.depPath)!.push(cache) @@ -386,13 +403,13 @@ function resolvePeersOfNode ( } } - ctx.pathsByNodeId[nodeId] = depPath + ctx.pathsByNodeId.set(nodeId, depPath) if (ctx.depPathsByPkgId != null) { - if (!ctx.depPathsByPkgId[resolvedPackage.depPath]) { - ctx.depPathsByPkgId[resolvedPackage.depPath] = [] + if (!ctx.depPathsByPkgId.has(resolvedPackage.depPath)) { + ctx.depPathsByPkgId.set(resolvedPackage.depPath, new Set()) } - if (!ctx.depPathsByPkgId[resolvedPackage.depPath].includes(depPath)) { - ctx.depPathsByPkgId[resolvedPackage.depPath].push(depPath) + if (!ctx.depPathsByPkgId.get(resolvedPackage.depPath)!.has(depPath)) { + ctx.depPathsByPkgId.get(resolvedPackage.depPath)!.add(depPath) } } const peerDependencies = { ...resolvedPackage.peerDependencies } @@ -400,15 +417,14 @@ function resolvePeersOfNode ( const dir = path.join(modules, resolvedPackage.name) const transitivePeerDependencies = new Set() - const unknownPeers = [ - ...Object.keys(unknownResolvedPeersOfChildren), - ...missingPeersOfChildren, - ] - if (unknownPeers.length > 0) { - for (const unknownPeer of unknownPeers) { - if (!peerDependencies[unknownPeer]) { - transitivePeerDependencies.add(unknownPeer) - } + for (const unknownPeer of unknownResolvedPeersOfChildren.keys()) { + if (!peerDependencies[unknownPeer]) { + transitivePeerDependencies.add(unknownPeer) + } + } + for (const unknownPeer of missingPeersOfChildren) { + if (!peerDependencies[unknownPeer]) { + transitivePeerDependencies.add(unknownPeer) } } ctx.depGraph[depPath] = { @@ -416,7 +432,7 @@ function resolvePeersOfNode ( children: Object.assign( getPreviouslyResolvedChildren(nodeId, ctx.dependenciesTree), children, - resolvedPeers + Object.fromEntries(resolvedPeers.entries()) ), depPath, depth: node.depth, @@ -426,7 +442,7 @@ function resolvePeersOfNode ( modules, peerDependencies, transitivePeerDependencies, - resolvedPeerNames: Object.keys(allResolvedPeers), + resolvedPeerNames: new Set(allResolvedPeers.keys()), } } return { resolvedPeers: allResolvedPeers, missingPeers: allMissingPeers } @@ -449,7 +465,7 @@ function getPreviouslyResolvedChildren (nodeId nodeIdChunks.pop() nodeIdChunks.reduce((accNodeId, part) => { accNodeId += `>${part}>${ownId}` - const parentNode = dependenciesTree[`${accNodeId}>`] + const parentNode = dependenciesTree.get(`${accNodeId}>`)! if (typeof parentNode.children === 'function') { parentNode.children = parentNode.children() } @@ -478,18 +494,27 @@ function resolvePeersOfChildren ( lockfileDir: string } ): PeersResolution { - const allResolvedPeers: Record = {} + const allResolvedPeers = new Map() const allMissingPeers = new Set() for (const childNodeId of Object.values(children)) { const { resolvedPeers, missingPeers } = resolvePeersOfNode(childNodeId, parentPkgs, ctx) - Object.assign(allResolvedPeers, resolvedPeers) - missingPeers.forEach((missingPeer) => allMissingPeers.add(missingPeer)) + for (const [k, v] of resolvedPeers) { + allResolvedPeers.set(k, v) + } + for (const missingPeer of missingPeers) { + allMissingPeers.add(missingPeer) + } } - const unknownResolvedPeersOfChildren: Record = pickBy((_, alias) => !children[alias], allResolvedPeers) + const unknownResolvedPeersOfChildren = new Map() + for (const [alias, v] of allResolvedPeers) { + if (!children[alias]) { + unknownResolvedPeersOfChildren.set(alias, v) + } + } - return { resolvedPeers: unknownResolvedPeersOfChildren, missingPeers: Array.from(allMissingPeers) } + return { resolvedPeers: unknownResolvedPeersOfChildren, missingPeers: allMissingPeers } } function _resolvePeers ( @@ -504,8 +529,8 @@ function _resolvePeers ( peerDependencyIssues: Pick } ): PeersResolution { - const resolvedPeers: { [alias: string]: string } = {} - const missingPeers = [] + const resolvedPeers = new Map() + const missingPeers = new Set() for (const peerName in ctx.resolvedPackage.peerDependencies) { // eslint-disable-line:forin const peerVersionRange = ctx.resolvedPackage.peerDependencies[peerName].replace(/^workspace:/, '') @@ -513,7 +538,7 @@ function _resolvePeers ( const optionalPeer = ctx.resolvedPackage.peerDependenciesMeta?.[peerName]?.optional === true if (!resolved) { - missingPeers.push(peerName) + missingPeers.add(peerName) const location = getLocationFromNodeIdAndPkg({ dependenciesTree: ctx.dependenciesTree, nodeId: ctx.nodeId, @@ -554,7 +579,7 @@ function _resolvePeers ( }) } - if (resolved?.nodeId) resolvedPeers[peerName] = resolved.nodeId + if (resolved?.nodeId) resolvedPeers.set(peerName, resolved.nodeId) } return { resolvedPeers, missingPeers } } @@ -590,7 +615,8 @@ function getLocationFromNodeId ( const parts = splitNodeId(nodeId).slice(0, -1) const parents = scan((prevNodeId, pkgId) => createNodeId(prevNodeId, pkgId), '>', parts) .slice(2) - .map((nid) => pick(['name', 'version'], dependenciesTree[nid].resolvedPackage as ResolvedPackage)) + + .map((nid) => pick(['name', 'version'], dependenciesTree.get(nid)!.resolvedPackage as ResolvedPackage)) return { projectId: parts[0], parents, diff --git a/pkg-manager/resolve-dependencies/test/dedupeDepPaths.test.ts b/pkg-manager/resolve-dependencies/test/dedupeDepPaths.test.ts index 60fff06904..9f7770fc03 100644 --- a/pkg-manager/resolve-dependencies/test/dedupeDepPaths.test.ts +++ b/pkg-manager/resolve-dependencies/test/dedupeDepPaths.test.ts @@ -1,7 +1,8 @@ -import { resolvePeers } from '../lib/resolvePeers' +import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers' +import { type DependenciesTreeNode } from '../lib/resolveDependencies' test('packages are not deduplicated when versions do not match', () => { - const fooPkg = { + const fooPkg: PartialResolvedPackage = { name: 'foo', version: '1.0.0', depPath: 'foo/1.0.0', @@ -16,7 +17,7 @@ test('packages are not deduplicated when versions do not match', () => { }, } - const peers = Object.fromEntries( + const peers: Record = Object.fromEntries( [ ['bar', '1.0.0'], ['bar', '2.0.0'], @@ -29,7 +30,7 @@ test('packages are not deduplicated when versions do not match', () => { version, depPath: `${name}/${version}`, peerDependencies: {}, - }, + } satisfies PartialResolvedPackage, ]) ) @@ -74,7 +75,7 @@ test('packages are not deduplicated when versions do not match', () => { id: 'project4', }, ], - dependenciesTree: Object.fromEntries([ + dependenciesTree: new Map>(([ ['>project1>foo/1.0.0>', fooPkg], ['>project1>bar/1.0.0>', peers.bar_1_0_0], @@ -89,12 +90,12 @@ test('packages are not deduplicated when versions do not match', () => { ['>project4>bar/2.0.0>', peers.bar_2_0_0], ['>project4>baz/2.0.0>', peers.baz_2_0_0], - ].map(([path, resolvedPackage]) => [path, { + ] satisfies Array<[string, PartialResolvedPackage]>).map(([path, resolvedPackage]) => [path, { children: {}, installable: {}, resolvedPackage, depth: 0, - }])), + } as DependenciesTreeNode])), dedupePeerDependents: true, virtualStoreDir: '', lockfileDir: '', diff --git a/pkg-manager/resolve-dependencies/test/resolvePeers.ts b/pkg-manager/resolve-dependencies/test/resolvePeers.ts index 4041cc97ce..158d82ff02 100644 --- a/pkg-manager/resolve-dependencies/test/resolvePeers.ts +++ b/pkg-manager/resolve-dependencies/test/resolvePeers.ts @@ -1,5 +1,6 @@ /// -import { resolvePeers } from '../lib/resolvePeers' +import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers' +import { type DependenciesTreeNode } from '../lib/resolveDependencies' test('resolve peer dependencies of cyclic dependencies', () => { const fooPkg = { @@ -31,24 +32,24 @@ test('resolve peer dependencies of cyclic dependencies', () => { id: '', }, ], - dependenciesTree: { - '>foo/1.0.0>': { + dependenciesTree: new Map>([ + ['>foo/1.0.0>', { children: { bar: '>foo/1.0.0>bar/1.0.0>', }, installable: true, resolvedPackage: fooPkg, depth: 0, - }, - '>foo/1.0.0>bar/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>', { children: { qar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>', }, installable: true, resolvedPackage: barPkg, depth: 1, - }, - '>foo/1.0.0>bar/1.0.0>qar/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>', { children: { zoo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>', }, @@ -63,8 +64,8 @@ test('resolve peer dependencies of cyclic dependencies', () => { }, }, depth: 2, - }, - '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>', { children: { foo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>', bar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>', @@ -79,20 +80,20 @@ test('resolve peer dependencies of cyclic dependencies', () => { }, }, depth: 3, - }, - '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 4, - }, - '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 4, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', }) @@ -139,22 +140,22 @@ test('when a package is referenced twice in the dependencies graph and one of th id: '', }, ], - dependenciesTree: { - '>zoo/1.0.0>': { + dependenciesTree: new Map>([ + ['>zoo/1.0.0>', { children: { foo: '>zoo/1.0.0>foo/1.0.0>', }, installable: true, resolvedPackage: zooPkg, depth: 0, - }, - '>zoo/1.0.0>foo/1.0.0>': { + }], + ['>zoo/1.0.0>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 1, - }, - '>bar/1.0.0>': { + }], + ['>bar/1.0.0>', { children: { zoo: '>bar/1.0.0>zoo/1.0.0>', qar: '>bar/1.0.0>qar/1.0.0>', @@ -162,22 +163,22 @@ test('when a package is referenced twice in the dependencies graph and one of th installable: true, resolvedPackage: barPkg, depth: 0, - }, - '>bar/1.0.0>zoo/1.0.0>': { + }], + ['>bar/1.0.0>zoo/1.0.0>', { children: { foo: '>bar/1.0.0>zoo/1.0.0>foo/1.0.0>', }, installable: true, resolvedPackage: zooPkg, depth: 1, - }, - '>bar/1.0.0>zoo/1.0.0>foo/1.0.0>': { + }], + ['>bar/1.0.0>zoo/1.0.0>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 2, - }, - '>bar/1.0.0>qar/1.0.0>': { + }], + ['>bar/1.0.0>qar/1.0.0>', { children: {}, installable: true, resolvedPackage: { @@ -187,8 +188,8 @@ test('when a package is referenced twice in the dependencies graph and one of th peerDependencies: {}, }, depth: 1, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', }) @@ -308,68 +309,68 @@ describe('peer dependency issues', () => { id: 'project6', }, ], - dependenciesTree: { - '>project1>foo/1.0.0>': { + dependenciesTree: new Map>([ + ['>project1>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 0, - }, - '>project2>bar/1.0.0>': { + }], + ['>project2>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 0, - }, - '>project3>foo/1.0.0>': { + }], + ['>project3>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 0, - }, - '>project3>bar/1.0.0>': { + }], + ['>project3>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 0, - }, - '>project4>bar/1.0.0>': { + }], + ['>project4>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 0, - }, - '>project4>qar/1.0.0>': { + }], + ['>project4>qar/1.0.0>', { children: {}, installable: true, resolvedPackage: qarPkg, depth: 0, - }, - '>project5>foo/1.0.0>': { + }], + ['>project5>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: fooPkg, depth: 0, - }, - '>project5>bar/2.0.0>': { + }], + ['>project5>bar/2.0.0>', { children: {}, installable: true, resolvedPackage: barWithOptionalPeer, depth: 0, - }, - '>project6>foo/2.0.0>': { + }], + ['>project6>foo/2.0.0>', { children: {}, installable: true, resolvedPackage: fooWithOptionalPeer, depth: 0, - }, - '>project6>bar/2.0.0>': { + }], + ['>project6>bar/2.0.0>', { children: {}, installable: true, resolvedPackage: barWithOptionalPeer, depth: 0, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', }) @@ -408,8 +409,8 @@ describe('unmet peer dependency issues', () => { id: 'project1', }, ], - dependenciesTree: { - '>project1>foo/1.0.0>': { + dependenciesTree: new Map>([ + ['>project1>foo/1.0.0>', { children: {}, installable: true, resolvedPackage: { @@ -422,8 +423,8 @@ describe('unmet peer dependency issues', () => { }, }, depth: 0, - }, - '>project1>peer1/1.0.0-rc.0>': { + }], + ['>project1>peer1/1.0.0-rc.0>', { children: {}, installable: true, resolvedPackage: { @@ -433,8 +434,8 @@ describe('unmet peer dependency issues', () => { peerDependencies: {}, }, depth: 0, - }, - '>project1>peer2/1.1.0-rc.0>': { + }], + ['>project1>peer2/1.1.0-rc.0>', { children: {}, installable: true, resolvedPackage: { @@ -444,8 +445,8 @@ describe('unmet peer dependency issues', () => { peerDependencies: {}, }, depth: 0, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', }) @@ -469,8 +470,8 @@ describe('unmet peer dependency issue resolved from subdependency', () => { id: 'project', }, ], - dependenciesTree: { - '>project>foo/1.0.0>': { + dependenciesTree: new Map>([ + ['>project>foo/1.0.0>', { children: { dep: '>project>foo/1.0.0>dep/1.0.0>', bar: '>project>foo/1.0.0>bar/1.0.0>', @@ -483,8 +484,8 @@ describe('unmet peer dependency issue resolved from subdependency', () => { peerDependencies: {}, }, depth: 0, - }, - '>project>foo/1.0.0>dep/1.0.0>': { + }], + ['>project>foo/1.0.0>dep/1.0.0>', { children: {}, installable: true, resolvedPackage: { @@ -494,8 +495,8 @@ describe('unmet peer dependency issue resolved from subdependency', () => { peerDependencies: {}, }, depth: 1, - }, - '>project>foo/1.0.0>bar/1.0.0>': { + }], + ['>project>foo/1.0.0>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: { @@ -507,8 +508,8 @@ describe('unmet peer dependency issue resolved from subdependency', () => { }, }, depth: 1, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', }) @@ -560,48 +561,48 @@ test('resolve peer dependencies with npm aliases', () => { id: '', }, ], - dependenciesTree: { - '>foo/1.0.0>': { + dependenciesTree: new Map>([ + ['>foo/1.0.0>', { children: { bar: '>foo/1.0.0>bar/1.0.0>', }, installable: true, resolvedPackage: fooPkg, depth: 0, - }, - '>foo/1.0.0>bar/1.0.0>': { + }], + ['>foo/1.0.0>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 1, - }, - '>foo/2.0.0>': { + }], + ['>foo/2.0.0>', { children: { bar: '>foo/2.0.0>bar/2.0.0>', }, installable: true, resolvedPackage: fooAliasPkg, depth: 0, - }, - '>foo/2.0.0>bar/2.0.0>': { + }], + ['>foo/2.0.0>bar/2.0.0>', { children: {}, installable: true, resolvedPackage: barAliasPkg, depth: 1, - }, - '>bar/1.0.0>': { + }], + ['>bar/1.0.0>', { children: {}, installable: true, resolvedPackage: barPkg, depth: 0, - }, - '>bar/2.0.0>': { + }], + ['>bar/2.0.0>', { children: {}, installable: true, resolvedPackage: barAliasPkg, depth: 0, - }, - }, + }], + ]), virtualStoreDir: '', lockfileDir: '', })