Files
pnpm/installing/deps-resolver/src/updateLockfile.ts
Zoltan Kochan 8acf2708c9 refactor: rename deps-resolver and env-installer packages (#11013)
Rename @pnpm/installing.resolve-dependencies to @pnpm/installing.deps-resolver
for consistency with the <domain>.<leaf> naming convention.
2026-03-18 21:52:01 +01:00

180 lines
5.7 KiB
TypeScript

import * as dp from '@pnpm/deps.path'
import {
type LockfileObject,
type PackageSnapshot,
pruneSharedLockfile,
} from '@pnpm/lockfile.pruner'
import { toLockfileResolution } from '@pnpm/lockfile.utils'
import { logger } from '@pnpm/logger'
import type { DepPath, Registries } from '@pnpm/types'
import type { KeyValuePair } from 'ramda'
import { partition } from 'ramda'
import { depPathToRef } from './depPathToRef.js'
import type { DependenciesGraph } from './index.js'
import type { ResolvedPackage } from './resolveDependencies.js'
export function updateLockfile (
{ dependenciesGraph, lockfile, prefix, registries, lockfileIncludeTarballUrl }: {
dependenciesGraph: DependenciesGraph
lockfile: LockfileObject
prefix: string
registries: Registries
lockfileIncludeTarballUrl?: boolean
}
): LockfileObject {
lockfile.packages = lockfile.packages ?? {}
for (const [depPath, depNode] of Object.entries(dependenciesGraph)) {
const [updatedOptionalDeps, updatedDeps] = partition(
(child) => depNode.optionalDependencies.has(child.alias) || depNode.peerDependencies[child.alias]?.optional === true,
Object.entries<DepPath>(depNode.children).map(([alias, depPath]) => ({ alias, depPath }))
)
lockfile.packages[depPath as DepPath] = toLockfileDependency(depNode, {
depGraph: dependenciesGraph,
depPath,
prevSnapshot: lockfile.packages[depPath as DepPath],
registries,
registry: dp.getRegistryByPackageName(registries, depNode.name),
updatedDeps,
updatedOptionalDeps,
lockfileIncludeTarballUrl,
})
}
const warn = (message: string) => {
logger.warn({ message, prefix })
}
return pruneSharedLockfile(lockfile, { warn, dependenciesGraph })
}
function toLockfileDependency (
pkg: ResolvedPackage & { transitivePeerDependencies: Set<string> },
opts: {
depPath: string
registry: string
registries: Registries
updatedDeps: Array<{ alias: string, depPath: DepPath }>
updatedOptionalDeps: Array<{ alias: string, depPath: DepPath }>
depGraph: DependenciesGraph
prevSnapshot?: PackageSnapshot
lockfileIncludeTarballUrl?: boolean
}
): PackageSnapshot {
const lockfileResolution = toLockfileResolution(
{ name: pkg.name, version: pkg.version },
pkg.resolution,
opts.registry,
opts.lockfileIncludeTarballUrl
)
const newResolvedDeps = updateResolvedDeps(
opts.updatedDeps,
opts.depGraph
)
const newResolvedOptionalDeps = updateResolvedDeps(
opts.updatedOptionalDeps,
opts.depGraph
)
const result = {
resolution: lockfileResolution,
} as PackageSnapshot
if (opts.depPath.includes(':')) {
// There is no guarantee that a non-npmjs.org-hosted package is going to have a version field.
// Also, for local directory dependencies, the version is not needed.
if (
pkg.version &&
(
!('type' in lockfileResolution) ||
lockfileResolution.type !== 'directory'
)
) {
result['version'] = pkg.version
}
}
if (Object.keys(newResolvedDeps).length > 0) {
result['dependencies'] = newResolvedDeps
}
if (Object.keys(newResolvedOptionalDeps).length > 0) {
result['optionalDependencies'] = newResolvedOptionalDeps
}
if (pkg.optional) {
result['optional'] = true
}
if (pkg.transitivePeerDependencies.size) {
result['transitivePeerDependencies'] = Array.from(pkg.transitivePeerDependencies).sort()
}
if (Object.keys(pkg.peerDependencies ?? {}).length > 0) {
const peerPkgs: Record<string, string> = {}
const normalizedPeerDependenciesMeta: Record<string, { optional: true }> = {}
for (const [peer, { version, optional }] of Object.entries(pkg.peerDependencies)) {
peerPkgs[peer] = version
if (optional) {
normalizedPeerDependenciesMeta[peer] = { optional: true }
}
}
result['peerDependencies'] = peerPkgs
if (Object.keys(normalizedPeerDependenciesMeta).length > 0) {
result['peerDependenciesMeta'] = normalizedPeerDependenciesMeta
}
}
if (pkg.additionalInfo.engines != null) {
for (const [engine, version] of Object.entries(pkg.additionalInfo.engines)) {
if (version === '*') continue
result.engines = result.engines ?? {} as any // eslint-disable-line @typescript-eslint/no-explicit-any
result.engines![engine] = version
}
}
if (pkg.additionalInfo.cpu != null) {
result['cpu'] = pkg.additionalInfo.cpu
}
if (pkg.additionalInfo.os != null) {
result['os'] = pkg.additionalInfo.os
}
if (pkg.additionalInfo.libc != null) {
result['libc'] = pkg.additionalInfo.libc
}
if (
Array.isArray(pkg.additionalInfo.bundledDependencies) ||
pkg.additionalInfo.bundledDependencies === true
) {
result['bundledDependencies'] = pkg.additionalInfo.bundledDependencies
} else if (
Array.isArray(pkg.additionalInfo.bundleDependencies) ||
pkg.additionalInfo.bundleDependencies === true
) {
result['bundledDependencies'] = pkg.additionalInfo.bundleDependencies
}
if (pkg.additionalInfo.deprecated) {
result['deprecated'] = pkg.additionalInfo.deprecated
}
if (pkg.hasBin) {
result['hasBin'] = true
}
if (pkg.patch) {
result['patched'] = true
}
return result
}
function updateResolvedDeps (
updatedDeps: Array<{ alias: string, depPath: DepPath }>,
depGraph: DependenciesGraph
): Record<string, string> {
return Object.fromEntries(
updatedDeps
.map(({ alias, depPath }): KeyValuePair<string, string> => {
if (depPath.startsWith('link:')) {
return [alias, depPath]
}
const depNode = depGraph[depPath]
return [
alias,
depPathToRef(depPath, {
alias,
realName: depNode.name,
}),
]
})
)
}