Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
options: ResolveDependencyOptions,
): Promise {
const update = Boolean(
options.update ||
options.workspacePackages &&
wantedDepIsLocallyAvailable(options.workspacePackages, wantedDependency, { defaultTag: ctx.defaultTag, registry: ctx.registries.default }))
const proceed = update || options.proceed || !options.currentResolution
const parentIsInstallable = options.parentIsInstallable === undefined || options.parentIsInstallable
const currentLockfileContainsTheDep = options.relDepPath ? Boolean(ctx.currentLockfile.packages?.[options.relDepPath]) : undefined
const depIsLinked = Boolean(options.depPath &&
// if package is not in `node_modules/.pnpm-lock.yaml`
// we can safely assume that it doesn't exist in `node_modules`
currentLockfileContainsTheDep &&
options.relDepPath && options.dependencyLockfile &&
await exists(path.join(ctx.virtualStoreDir, `${options.depPath}/node_modules/${nameVerFromPkgSnapshot(options.relDepPath, options.dependencyLockfile).name}/package.json`)) &&
(options.currentDepth > 0 || wantedDependency.alias && await exists(path.join(ctx.modulesDir, wantedDependency.alias))))
if (!proceed && depIsLinked) {
return null
}
let pkgResponse!: PackageResponse
try {
pkgResponse = await ctx.storeController.requestPackage(wantedDependency, {
alwaysTryWorkspacePackages: options.alwaysTryWorkspacePackages,
currentPackageId: options.pkgId,
currentResolution: options.currentResolution,
defaultTag: ctx.defaultTag,
downloadPriority: -options.currentDepth,
importerDir: ctx.prefix,
lockfileDir: ctx.lockfileDir,
let useManifestInfoFromLockfile = false
let prepare!: boolean
let hasBin!: boolean
if (
!options.update && options.dependencyLockfile && options.relDepPath &&
!pkgResponse.body.updated &&
// peerDependencies field is also used for transitive peer dependencies which should not be linked
// That's why we cannot omit reading package.json of such dependencies.
// This can be removed if we implement something like peerDependenciesMeta.transitive: true
!options.dependencyLockfile.peerDependencies
) {
useManifestInfoFromLockfile = true
prepare = options.dependencyLockfile.prepare === true
hasBin = options.dependencyLockfile.hasBin === true
pkg = Object.assign(
nameVerFromPkgSnapshot(options.relDepPath, options.dependencyLockfile),
options.dependencyLockfile,
)
} else {
// tslint:disable:no-string-literal
pkg = ctx.readPackageHook
? ctx.readPackageHook(pkgResponse.body.manifest || await pkgResponse.bundledManifest!())
: pkgResponse.body.manifest || await pkgResponse.bundledManifest!()
prepare = Boolean(
pkgResponse.body.resolvedVia === 'git-repository' &&
typeof pkg.scripts?.prepare === 'string',
)
if (
options.dependencyLockfile?.deprecated &&
!pkgResponse.body.updated && !pkg.deprecated
const message = `No entry for "${relDepPath}" in ${WANTED_LOCKFILE}`
if (opts.failOnMissingDependencies) {
throw new PnpmError('LOCKFILE_MISSING_DEPENDENCY', message)
}
logger.debug(message)
continue
}
let installable!: boolean
if (!parentIsInstallable) {
installable = false
if (!ctx.pickedPackages[relDepPath]) {
opts.skipped.add(relDepPath)
}
} else {
const pkg = {
...nameVerFromPkgSnapshot(relDepPath, pkgSnapshot),
cpu: pkgSnapshot.cpu,
engines: pkgSnapshot.engines,
os: pkgSnapshot.os,
}
// TODO: relDepPath is not the package ID. Should be fixed
installable = opts.includeIncompatiblePackages || packageIsInstallable(pkgSnapshot.id || relDepPath, pkg, {
engineStrict: opts.engineStrict,
lockfileDir: opts.lockfileDir,
nodeVersion: opts.currentEngine.nodeVersion,
optional: pkgSnapshot.optional === true,
pnpmVersion: opts.currentEngine.pnpmVersion,
}) !== false
if (!installable) {
if (!ctx.pickedPackages[relDepPath]) {
opts.skipped.add(relDepPath)
}
if (dependencyLockfile.peerDependencies && dependencyLockfile.dependencies) {
// This is done to guarantee that the dependency will be relinked with the
// up-to-date peer dependencies
// Covered by test: "peer dependency is grouped with dependency when peer is resolved not from a top dependency"
R.keys(dependencyLockfile.peerDependencies).forEach((peer) => {
delete dependencyLockfile.dependencies![peer]
})
}
const depPath = dp.resolve(registries, relDepPath)
return {
currentResolution: pkgSnapshotToResolution(relDepPath, dependencyLockfile, registries),
dependencyLockfile,
depPath,
optionalDependencyNames: R.keys(dependencyLockfile.optionalDependencies),
pkgId: packageIdFromSnapshot(relDepPath, dependencyLockfile, registries),
relDepPath,
resolvedDependencies: {
...dependencyLockfile.dependencies,
...dependencyLockfile.optionalDependencies,
},
}
} else {
return {
pkgId: dp.tryGetPackageId(registries, relDepPath) || relDepPath, // Does it make sense to set pkgId when we're not sure?
relDepPath,
}
}
}
},
],
])
packageRegistry.set(name, packageStore)
}
}
for (const [relDepPath, pkgSnapshot] of R.toPairs(lockfile.packages || {})) {
const { name, version, peersSuffix } = nameVerFromPkgSnapshot(relDepPath, pkgSnapshot)
const pnpVersion = toPnPVersion(version, peersSuffix)
let packageStore = packageRegistry.get(name)
if (!packageStore) {
packageStore = new Map()
packageRegistry.set(name, packageStore)
}
const packageId = packageIdFromSnapshot(relDepPath, pkgSnapshot, opts.registries)
// TODO: what about packages that are built?
// Also, storeController has .getPackageLocation()
let packageLocation
if (peersSuffix) {
packageLocation = path.relative(opts.lockfileDirectory, path.join(
opts.storeDirectory,
'virtual',
`${name}-virtual-${version}_${peersSuffix}`,
))
} else {
packageLocation = path.relative(opts.lockfileDirectory, path.join(
opts.storeDirectory,
pkgIdToFilename(packageId, opts.lockfileDirectory),
'node_modules',
name,
))
const dependencyLockfile = lockfile.packages?.[relDepPath]
if (dependencyLockfile) {
if (dependencyLockfile.peerDependencies && dependencyLockfile.dependencies) {
// This is done to guarantee that the dependency will be relinked with the
// up-to-date peer dependencies
// Covered by test: "peer dependency is grouped with dependency when peer is resolved not from a top dependency"
R.keys(dependencyLockfile.peerDependencies).forEach((peer) => {
delete dependencyLockfile.dependencies![peer]
})
}
const depPath = dp.resolve(registries, relDepPath)
return {
currentResolution: pkgSnapshotToResolution(relDepPath, dependencyLockfile, registries),
dependencyLockfile,
depPath,
optionalDependencyNames: R.keys(dependencyLockfile.optionalDependencies),
pkgId: packageIdFromSnapshot(relDepPath, dependencyLockfile, registries),
relDepPath,
resolvedDependencies: {
...dependencyLockfile.dependencies,
...dependencyLockfile.optionalDependencies,
},
}
} else {
return {
pkgId: dp.tryGetPackageId(registries, relDepPath) || relDepPath, // Does it make sense to set pkgId when we're not sure?
relDepPath,
}
}
lockfile: Lockfile,
opts: {
prefix: string,
},
) {
const relDepPath = dp.refToRelative(preferredRef, wantedDep.alias)
if (relDepPath === null) return false
const pkgSnapshot = lockfile.packages?.[relDepPath]
if (!pkgSnapshot) {
logger.warn({
message: `Could not find preferred package ${relDepPath} in lockfile`,
prefix: opts.prefix,
})
return false
}
const { version } = nameVerFromPkgSnapshot(relDepPath, pkgSnapshot)
return semver.satisfies(version, wantedDep.pref, true)
}
return R.toPairs(deps).map(([depAlias, ref]) => {
if (importerId && ref.startsWith('link:')) {
return [depAlias, path.join(importerId, ref.substr(5))]
}
const relDepPath = refToRelative(ref, depAlias)
if (!relDepPath) return [depAlias, ref]
const { name, version, peersSuffix } = nameVerFromPkgSnapshot(relDepPath, lockfile.packages![relDepPath])
const pnpVersion = toPnPVersion(version, peersSuffix)
if (depAlias === name) {
return [depAlias, pnpVersion]
}
return [depAlias, [name, pnpVersion]]
})
}
export function getPreferredVersionsFromLockfile (snapshots: PackageSnapshots): PreferredVersions {
const preferredVersions: PreferredVersions = {}
for (const [relDepPath, snapshot] of Object.entries(snapshots)) {
const { name, version } = nameVerFromPkgSnapshot(relDepPath, snapshot)
if (!preferredVersions[name]) {
preferredVersions[name] = { [version]: 'version' }
} else {
preferredVersions[name][version] = 'version'
}
}
return preferredVersions
}
async function getDependencies (
step: LockfileWalkerStep,
depth: number,
opts: {
getIndependentPackageLocation?: (packageId: string, packageName: string) => Promise,
registries: Registries,
lockfileDir: string,
virtualStoreDir: string,
},
): Promise {
const deps: Dependency[] = []
const nextSteps: LockfileWalkerStep[] = []
for (const { pkgSnapshot, relDepPath, next } of step.dependencies) {
const absolutePath = dp.resolve(opts.registries, relDepPath)
const pkgName = nameVerFromPkgSnapshot(relDepPath, pkgSnapshot).name
const modules = path.join(opts.virtualStoreDir, pkgIdToFilename(absolutePath, opts.lockfileDir), 'node_modules')
const independent = opts.getIndependentPackageLocation && packageIsIndependent(pkgSnapshot)
const allDeps = {
...pkgSnapshot.dependencies,
...pkgSnapshot.optionalDependencies,
}
deps.push({
absolutePath,
children: Object.keys(allDeps).reduce((children, alias) => {
children[alias] = dp.refToAbsolute(allDeps[alias], alias, opts.registries)
return children
}, {}),
depth,
location: !independent
? path.join(modules, pkgName)
: await opts.getIndependentPackageLocation!(pkgSnapshot.id || absolutePath, pkgName),