Skip to content

Commit

Permalink
refactor: resolve dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
zkochan committed May 16, 2024
1 parent 134a204 commit f69c248
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 79 deletions.
20 changes: 0 additions & 20 deletions pkg-manager/resolve-dependencies/src/nodeIdUtils.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function parentIdsContainsSequence (pkgIds: string[], pkgId1: string, pkgId2: string): boolean {
const pkg1Index = pkgIds.indexOf(pkgId1)
if (pkg1Index === -1 || pkg1Index === pkgIds.length - 1) {
return false
}
const pkg2Index = pkgIds.lastIndexOf(pkgId2)
return pkg1Index < pkg2Index && pkg2Index !== pkgIds.length - 1
}
65 changes: 30 additions & 35 deletions pkg-manager/resolve-dependencies/src/resolveDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ import { encodePkgId } from './encodePkgId'
import { getNonDevWantedDependencies, type WantedDependency } from './getNonDevWantedDependencies'
import { safeIntersect } from './mergePeers'
import { nextNodeId } from './nextNodeId'
import {
nodeIdContainsSequence,
} from './nodeIdUtils'
import { parentIdsContainsSequence } from './parentIdsContainsSequence'
import { hoistPeers, getHoistableOptionalPeers } from './hoistPeers'
import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable'
import { replaceVersionInPref } from './replaceVersionInPref'
Expand All @@ -62,7 +60,7 @@ const dependencyResolvedLogger = logger('_dependency_resolved')

const omitDepsFields = omit(['dependencies', 'optionalDependencies', 'peerDependencies', 'peerDependenciesMeta'])

export function nodeIdToParents (
export function getPkgsInfoFromDepPaths (
depPaths: string[],
resolvedPackagesByDepPath: ResolvedPackagesByDepPath
): Array<{ id: string, name: string, version: string }> {
Expand Down Expand Up @@ -118,13 +116,13 @@ export interface PendingNode {
resolvedPackage: ResolvedPackage
depth: number
installable: boolean
parentDepPaths: string[]
parentIds: string[]
}

export interface ChildrenByParentDepPath {
[depPath: string]: Array<{
export interface ChildrenByParentId {
[id: string]: Array<{
alias: string
depPath: string
id: string
}>
}

Expand All @@ -143,7 +141,7 @@ export interface ResolutionContext {
ignoreScripts?: boolean
resolvedPackagesByDepPath: ResolvedPackagesByDepPath
outdatedDependencies: { [pkgId: string]: string }
childrenByParentDepPath: ChildrenByParentDepPath
childrenByParentId: ChildrenByParentId
patchedDependencies?: Record<string, PatchFile>
pendingNodes: PendingNode[]
wantedLockfile: Lockfile
Expand Down Expand Up @@ -245,15 +243,15 @@ export interface ResolvedPackage {
parentImporterIds: Set<string>
}

type ParentPkg = Pick<PkgAddress, 'nodeId' | 'installable' | 'depPath' | 'rootDir' | 'optional'>
type ParentPkg = Pick<PkgAddress, 'nodeId' | 'installable' | 'depPath' | 'rootDir' | 'optional' | 'pkgId'>

export type ParentPkgAliases = Record<string, PkgAddress | true>

export type UpdateMatchingFunction = (pkgName: string) => boolean

interface ResolvedDependenciesOptions {
currentDepth: number
parentDepPaths: string[]
parentIds: string[]
parentPkg: ParentPkg
parentPkgAliases: ParentPkgAliases
// If the package has been updated, the dependencies
Expand Down Expand Up @@ -377,12 +375,14 @@ interface PkgAddressesByImportersWithoutPeers extends PeersResolutionResult {
pkgAddresses: Array<PkgAddress | LinkedDependency>
}

export type ImporterToResolveOptions = Omit<ResolvedDependenciesOptions, 'parentPkgAliases' | 'publishedBy'>

export interface ImporterToResolve {
updatePackageManifest: boolean
preferredVersions: PreferredVersions
parentPkgAliases: ParentPkgAliases
wantedDependencies: Array<WantedDependency & { updateDepth?: number }>
options: Omit<ResolvedDependenciesOptions, 'parentPkgAliases' | 'publishedBy'>
options: ImporterToResolveOptions
}

interface ResolveDependenciesOfImportersResult {
Expand Down Expand Up @@ -741,7 +741,7 @@ async function resolveDependenciesOfDependency (
updateMatching: options.updateMatching,
supportedArchitectures: options.supportedArchitectures,
updateToLatest: options.updateToLatest,
parentDepPaths: options.parentDepPaths,
parentIds: options.parentIds,
}
const resolveDependencyResult = await resolveDependency(extendedWantedDep.wantedDependency, ctx, resolveDependencyOpts)

Expand Down Expand Up @@ -771,11 +771,10 @@ async function resolveDependenciesOfDependency (
}

const postponedResolution = resolveChildren.bind(null, ctx, {
parentNodeId: options.parentPkg.nodeId,
parentPkg: resolveDependencyResult,
dependencyLockfile: extendedWantedDep.infoFromLockfile?.dependencyLockfile,
parentDepth: options.currentDepth,
parentDepPaths: [...options.parentDepPaths, resolveDependencyResult.depPath],
parentIds: [...options.parentIds, resolveDependencyResult.pkgId],
updateDepth,
prefix: options.prefix,
updateMatching: options.updateMatching,
Expand Down Expand Up @@ -822,19 +821,17 @@ function filterMissingPeers (
async function resolveChildren (
ctx: ResolutionContext,
{
parentNodeId,
parentPkg,
parentDepPaths,
parentIds,
dependencyLockfile,
parentDepth,
updateDepth,
updateMatching,
prefix,
supportedArchitectures,
}: {
parentNodeId: string
parentPkg: PkgAddress
parentDepPaths: string[]
parentIds: string[]
dependencyLockfile: PackageSnapshot | undefined
parentDepth: number
updateDepth: number
Expand Down Expand Up @@ -888,12 +885,12 @@ async function resolveChildren (
updateDepth,
updateMatching,
supportedArchitectures,
parentDepPaths,
parentIds,
}
)
ctx.childrenByParentDepPath[parentPkg.depPath] = pkgAddresses.map((child) => ({
ctx.childrenByParentId[parentPkg.pkgId] = pkgAddresses.map((child) => ({
alias: child.alias,
depPath: child.depPath,
id: child.pkgId,
}))
ctx.dependenciesTree.set(parentPkg.nodeId, {
children: pkgAddresses.reduce((chn, child) => {
Expand Down Expand Up @@ -1086,7 +1083,7 @@ interface ResolveDependencyOptions {
dependencyLockfile?: PackageSnapshot
}
parentPkg: ParentPkg
parentDepPaths: string[]
parentIds: string[]
parentPkgAliases: ParentPkgAliases
preferredVersions: PreferredVersions
prefix: string
Expand Down Expand Up @@ -1175,7 +1172,7 @@ async function resolveDependency (
supportedArchitectures: options.supportedArchitectures,
onFetchError: (err: any) => { // eslint-disable-line
err.prefix = options.prefix
err.pkgsStack = nodeIdToParents(options.parentDepPaths, ctx.resolvedPackagesByDepPath)
err.pkgsStack = getPkgsInfoFromDepPaths(options.parentIds, ctx.resolvedPackagesByDepPath)
return err
},
updateToLatest: options.updateToLatest,
Expand All @@ -1189,14 +1186,14 @@ async function resolveDependency (
pref: wantedDependency.pref,
version: wantedDependency.alias ? wantedDependency.pref : undefined,
},
parents: nodeIdToParents(options.parentDepPaths, ctx.resolvedPackagesByDepPath),
parents: getPkgsInfoFromDepPaths(options.parentIds, ctx.resolvedPackagesByDepPath),
prefix: options.prefix,
reason: 'resolution_failure',
})
return null
}
err.prefix = options.prefix
err.pkgsStack = nodeIdToParents(options.parentDepPaths, ctx.resolvedPackagesByDepPath)
err.pkgsStack = getPkgsInfoFromDepPaths(options.parentIds, ctx.resolvedPackagesByDepPath)
throw err
}

Expand Down Expand Up @@ -1298,10 +1295,10 @@ async function resolveDependency (
// because zoo is a new parent package:
// foo > bar > qar > zoo > qar
if (
nodeIdContainsSequence(
options.parentDepPaths,
options.parentPkg.depPath,
depPath
parentIdsContainsSequence(
options.parentIds,
options.parentPkg.pkgId,
pkgResponse.body.id
) || depPath === options.parentPkg.depPath
) {
return null
Expand Down Expand Up @@ -1351,9 +1348,7 @@ async function resolveDependency (
}
// In case of leaf dependencies (dependencies that have no prod deps or peer deps),
// we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless.
const nodeId = pkgIsLeaf(pkg)
? `>${depPath}>`
: nextNodeId()
const nodeId = pkgIsLeaf(pkg) ? depPath : nextNodeId()

const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable
const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false
Expand Down Expand Up @@ -1422,7 +1417,7 @@ async function resolveDependency (
ctx.pendingNodes.push({
alias: wantedDependency.alias || pkg.name,
depth: options.currentDepth,
parentDepPaths: options.parentDepPaths,
parentIds: options.parentIds,
installable,
nodeId,
resolvedPackage: ctx.resolvedPackagesByDepPath[depPath],
Expand All @@ -1434,7 +1429,7 @@ async function resolveDependency (
? path.resolve(ctx.lockfileDir, (pkgResponse.body.resolution as DirectoryResolution).directory)
: options.prefix
let missingPeersOfChildren!: MissingPeersOfChildren | undefined
if (ctx.hoistPeers && !options.parentDepPaths.includes(depPath)) {
if (ctx.hoistPeers && !options.parentIds.includes(pkgResponse.body.id)) {
if (ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id]) {
if (!options.parentPkg.nodeId.startsWith(ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].parentImporterId)) {
missingPeersOfChildren = ctx.missingPeersOfChildrenByPkgId[pkgResponse.body.id].missingPeersOfChildren
Expand Down
43 changes: 21 additions & 22 deletions pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import partition from 'ramda/src/partition'
import zipObj from 'ramda/src/zipObj'
import { type WantedDependency } from './getNonDevWantedDependencies'
import { nextNodeId } from './nextNodeId'
import { parentIdsContainsSequence } from './parentIdsContainsSequence'
import {
nodeIdContainsSequence,
} from './nodeIdUtils'
import {
type ChildrenByParentDepPath,
type ChildrenByParentId,
type DependenciesTree,
type LinkedDependency,
type ImporterToResolve,
type ImporterToResolveOptions,
type ParentPkgAliases,
type PendingNode,
type PkgAddress,
Expand All @@ -29,7 +28,6 @@ import {
type ResolutionContext,
} from './resolveDependencies'

export * from './nodeIdUtils'
export type { LinkedDependency, ResolvedPackage, DependenciesTree, DependenciesTreeNode } from './resolveDependencies'

export interface ResolvedImporters {
Expand Down Expand Up @@ -130,7 +128,7 @@ export async function resolveDependencyTree<T> (
autoInstallPeersFromHighestMatch: opts.autoInstallPeersFromHighestMatch === true,
allowBuild: opts.allowBuild,
allowedDeprecatedVersions: opts.allowedDeprecatedVersions,
childrenByParentDepPath: {} as ChildrenByParentDepPath,
childrenByParentId: {} as ChildrenByParentId,
currentLockfile: opts.currentLockfile,
defaultTag: opts.tag,
dependenciesTree: new Map() as DependenciesTree<ResolvedPackage>,
Expand Down Expand Up @@ -170,16 +168,17 @@ export async function resolveDependencyTree<T> (
// We only need to proceed resolving every dependency
// if the newly added dependency has peer dependencies.
const proceed = importer.id === '.' || importer.hasRemovedDependencies === true || importer.wantedDependencies.some((wantedDep: any) => wantedDep.isNew) // eslint-disable-line @typescript-eslint/no-explicit-any
const resolveOpts = {
const resolveOpts: ImporterToResolveOptions = {
currentDepth: 0,
parentPkg: {
installable: true,
nodeId: `>${importer.id}>`,
optional: false,
depPath: importer.id,
pkgId: importer.id,
rootDir: importer.rootDir,
},
parentDepPaths: [],
parentIds: [],
proceed,
resolvedDependencies: {
...projectSnapshot.dependencies,
Expand Down Expand Up @@ -207,9 +206,9 @@ export async function resolveDependencyTree<T> (

ctx.pendingNodes.forEach((pendingNode) => {
ctx.dependenciesTree.set(pendingNode.nodeId, {
children: () => buildTree(ctx, pendingNode.resolvedPackage.id,
pendingNode.parentDepPaths,
ctx.childrenByParentDepPath[pendingNode.resolvedPackage.depPath], pendingNode.depth + 1, pendingNode.installable),
children: () => buildTree(ctx, pendingNode.resolvedPackage.depPath,
pendingNode.parentIds,
ctx.childrenByParentId[pendingNode.resolvedPackage.depPath], pendingNode.depth + 1, pendingNode.installable),
depth: pendingNode.depth,
installable: pendingNode.installable,
resolvedPackage: pendingNode.resolvedPackage,
Expand Down Expand Up @@ -262,40 +261,40 @@ export async function resolveDependencyTree<T> (

function buildTree (
ctx: {
childrenByParentDepPath: ChildrenByParentDepPath
childrenByParentId: ChildrenByParentId
dependenciesTree: DependenciesTree<ResolvedPackage>
resolvedPackagesByDepPath: ResolvedPackagesByDepPath
skipped: Set<string>
},
parentId: string,
parentDepPaths: string[],
children: Array<{ alias: string, depPath: string }>,
parentIds: string[],
children: Array<{ alias: string, id: string }>,
depth: number,
installable: boolean
): Record<string, string> {
const childrenNodeIds: Record<string, string> = {}
for (const child of children) {
if (child.depPath.startsWith('link:')) {
childrenNodeIds[child.alias] = child.depPath
if (child.id.startsWith('link:')) {
childrenNodeIds[child.alias] = child.id
continue
}
if (nodeIdContainsSequence(parentDepPaths, parentId, child.depPath) || parentId === child.depPath) {
if (parentIdsContainsSequence(parentIds, parentId, child.id) || parentId === child.id) {
continue
}
const childNodeId = nextNodeId()
childrenNodeIds[child.alias] = childNodeId
installable = installable || !ctx.skipped.has(child.depPath)
installable = installable || !ctx.skipped.has(child.id)
ctx.dependenciesTree.set(childNodeId, {
children: () => buildTree(ctx,
child.depPath,
[...parentDepPaths, child.depPath],
ctx.childrenByParentDepPath[child.depPath],
child.id,
[...parentIds, child.id],
ctx.childrenByParentId[child.id],
depth + 1,
installable
),
depth,
installable,
resolvedPackage: ctx.resolvedPackagesByDepPath[child.depPath],
resolvedPackage: ctx.resolvedPackagesByDepPath[child.id],
})
}
return childrenNodeIds
Expand Down
4 changes: 2 additions & 2 deletions pkg-manager/resolve-dependencies/src/resolvePeers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,8 +656,8 @@ function getPreviouslyResolvedChildren<T extends PartialResolvedPackage> (parent

if (!ownDepPath || !parentDepPathsChain.includes(ownDepPath)) return allChildren

for (const parentNodeId of [...parentNodeIds].reverse()) {
const parentNode = dependenciesTree.get(parentNodeId)!
for (let i = parentNodeIds.length - 1; i >= 0; i--) {
const parentNode = dependenciesTree.get(parentNodeIds[i])!
if ((parentNode.resolvedPackage as T).depPath === ownDepPath) {
if (typeof parentNode.children === 'function') {
parentNode.children = parentNode.children()
Expand Down

0 comments on commit f69c248

Please sign in to comment.