refactor(resolve-dependencies): use Maps and Sets instead of objects (#6749)

This commit is contained in:
Bogdan Savluk
2023-07-03 00:19:23 +02:00
committed by GitHub
parent 9b51108100
commit fee263822c
6 changed files with 227 additions and 193 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/resolve-dependencies": patch
---
Refactor resolve-dependencies to use maps and sets instead of objects

View File

@@ -89,12 +89,13 @@ export type DependenciesTreeNode<T> = {
depth: -1
})
export interface DependenciesTree<T> {
// 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<T>
}
export type DependenciesTree<T> = 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<T>
>
export type ResolvedPackagesByDepPath = Record<string, ResolvedPackage>
@@ -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,

View File

@@ -101,7 +101,7 @@ export async function resolveDependencyTree<T> (
childrenByParentDepPath: {} as ChildrenByParentDepPath,
currentLockfile: opts.currentLockfile,
defaultTag: opts.tag,
dependenciesTree: {} as DependenciesTree<ResolvedPackage>,
dependenciesTree: new Map() as DependenciesTree<ResolvedPackage>,
dryRun: opts.dryRun,
engineStrict: opts.engineStrict,
force: opts.force,
@@ -168,13 +168,13 @@ export async function resolveDependencyTree<T> (
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<T> (
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
}

View File

@@ -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<string>
}
export type PartialResolvedPackage = Pick<ResolvedPackage,
@@ -70,8 +69,8 @@ export function resolvePeers<T extends PartialResolvedPackage> (
peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
} {
const depGraph: GenericDependenciesGraph<T> = {}
const pathsByNodeId: Record<string, string> = {}
const depPathsByPkgId: Record<string, string[]> = {}
const pathsByNodeId = new Map<string, string>()
const depPathsByPkgId = new Map<string, Set<string>>()
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<T extends PartialResolvedPackage> (
}
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<string, string> } = {}
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<T extends PartialResolvedPackage> (
}
function nodeDepsCount (node: GenericDependenciesGraphNode) {
return Object.keys(node.children).length + node.resolvedPeerNames.length
return Object.keys(node.children).length + node.resolvedPeerNames.size
}
function deduplicateAll<T extends PartialResolvedPackage> (
depGraph: GenericDependenciesGraph<T>,
duplicates: string[][]
duplicates: Array<Set<string>>
): Record<string, string> {
const { depPathsMap, remainingDuplicates } = deduplicateDepPaths(duplicates, depGraph)
if (remainingDuplicates.length === duplicates.length) {
@@ -150,16 +149,16 @@ function deduplicateAll<T extends PartialResolvedPackage> (
}
function deduplicateDepPaths<T extends PartialResolvedPackage> (
duplicates: string[][],
duplicates: Array<Set<string>>,
depGraph: GenericDependenciesGraph<T>
) {
const depCountSorter = (depPath1: string, depPath2: string) => nodeDepsCount(depGraph[depPath1]) - nodeDepsCount(depGraph[depPath2])
const depPathsMap: Record<string, string> = {}
const remainingDuplicates: string[][] = []
const remainingDuplicates: Array<Set<string>> = []
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<T extends PartialResolvedPackage> (
}
if (unresolvedDepPaths.size) {
remainingDuplicates.push([...unresolvedDepPaths])
remainingDuplicates.push(unresolvedDepPaths)
}
}
return {
@@ -195,11 +194,16 @@ function isCompatibleAndHasMoreDeps<T extends PartialResolvedPackage> (
) {
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<T extends PartialResolvedPackage> (dependenciesTree: DependenciesTree<T>, projects: ProjectToResolve[]) {
@@ -219,7 +223,7 @@ function createPkgsByName<T extends PartialResolvedPackage> (
.keys(directNodeIdsByAlias)
.map((alias) => ({
alias,
node: dependenciesTree[directNodeIdsByAlias[alias]],
node: dependenciesTree.get(directNodeIdsByAlias[alias])!,
nodeId: directNodeIdsByAlias[alias],
}))
)
@@ -241,20 +245,20 @@ function createPkgsByName<T extends PartialResolvedPackage> (
interface PeersCacheItem {
depPath: string
resolvedPeers: Array<[string, string]>
missingPeers: string[]
resolvedPeers: Map<string, string>
missingPeers: Set<string>
}
type PeersCache = Map<string, PeersCacheItem[]>
interface PeersResolution {
missingPeers: string[]
resolvedPeers: Record<string, string>
missingPeers: Set<string>
resolvedPeers: Map<string, string>
}
interface ResolvePeersContext {
pathsByNodeId: { [nodeId: string]: string }
depPathsByPkgId?: Record<string, string[]>
pathsByNodeId: Map<string, string>
depPathsByPkgId?: Map<string, Set<string>>
}
function resolvePeersOfNode<T extends PartialResolvedPackage> (
@@ -271,16 +275,16 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
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<string, string>(), missingPeers: new Set<string>() }
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<string, string>(), missingPeers: new Set<string>() }
}
if (typeof node.children === 'function') {
node.children = node.children()
@@ -293,36 +297,39 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
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<T extends PartialResolvedPackage> (
} = resolvePeersOfChildren(children, parentPkgs, ctx)
const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0
? { resolvedPeers: {}, missingPeers: [] }
? { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() }
: _resolvePeers({
currentDepth: node.depth,
dependenciesTree: ctx.dependenciesTree,
@@ -344,16 +351,26 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
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<string>()
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<T extends PartialResolvedPackage> (
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<T extends PartialResolvedPackage> (
}
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<T extends PartialResolvedPackage> (
}
}
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<T extends PartialResolvedPackage> (
const dir = path.join(modules, resolvedPackage.name)
const transitivePeerDependencies = new Set<string>()
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<T extends PartialResolvedPackage> (
children: Object.assign(
getPreviouslyResolvedChildren(nodeId, ctx.dependenciesTree),
children,
resolvedPeers
Object.fromEntries(resolvedPeers.entries())
),
depPath,
depth: node.depth,
@@ -426,7 +442,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
modules,
peerDependencies,
transitivePeerDependencies,
resolvedPeerNames: Object.keys(allResolvedPeers),
resolvedPeerNames: new Set(allResolvedPeers.keys()),
}
}
return { resolvedPeers: allResolvedPeers, missingPeers: allMissingPeers }
@@ -449,7 +465,7 @@ function getPreviouslyResolvedChildren<T extends PartialResolvedPackage> (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<T extends PartialResolvedPackage> (
lockfileDir: string
}
): PeersResolution {
const allResolvedPeers: Record<string, string> = {}
const allResolvedPeers = new Map<string, string>()
const allMissingPeers = new Set<string>()
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<string, string> = pickBy((_, alias) => !children[alias], allResolvedPeers)
const unknownResolvedPeersOfChildren = new Map<string, string>()
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<T extends PartialResolvedPackage> (
@@ -504,8 +529,8 @@ function _resolvePeers<T extends PartialResolvedPackage> (
peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'>
}
): PeersResolution {
const resolvedPeers: { [alias: string]: string } = {}
const missingPeers = []
const resolvedPeers = new Map<string, string>()
const missingPeers = new Set<string>()
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<T extends PartialResolvedPackage> (
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<T extends PartialResolvedPackage> (
})
}
if (resolved?.nodeId) resolvedPeers[peerName] = resolved.nodeId
if (resolved?.nodeId) resolvedPeers.set(peerName, resolved.nodeId)
}
return { resolvedPeers, missingPeers }
}
@@ -590,7 +615,8 @@ function getLocationFromNodeId<T> (
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,

View File

@@ -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<string, PartialResolvedPackage> = 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<string, DependenciesTreeNode<PartialResolvedPackage>>(([
['>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<PartialResolvedPackage>])),
dedupePeerDependents: true,
virtualStoreDir: '',
lockfileDir: '',

View File

@@ -1,5 +1,6 @@
/// <reference path="../../../__typings__/index.d.ts" />
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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>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: '',
})