feat: auto install peers from highest match (#7654)

This commit is contained in:
Zoltan Kochan
2024-02-14 15:23:31 +01:00
committed by GitHub
parent 7733f3a943
commit 9f8948c24a
6 changed files with 39 additions and 3 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/resolve-dependencies": minor
"@pnpm/core": minor
---
Add a new option autoInstallPeersFromHighestMatch that makes pnpm install the highest version satisfying one of the peer dependencies even if the peer dependency ranges don't overlap.

View File

@@ -22,6 +22,7 @@ import { type PreResolutionHookContext } from '@pnpm/hooks.types'
export interface StrictInstallOptions {
autoInstallPeers: boolean
autoInstallPeersFromHighestMatch: boolean
forceSharedLockfile: boolean
frozenLockfile: boolean
frozenLockfileIfExists: boolean
@@ -157,6 +158,7 @@ const defaults = (opts: InstallOptions) => {
allowedDeprecatedVersions: {},
allowNonAppliedPatches: false,
autoInstallPeers: true,
autoInstallPeersFromHighestMatch: false,
childConcurrency: 5,
confirmModulesPurge: !opts.force,
depth: 0,

View File

@@ -999,6 +999,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
allowedDeprecatedVersions: opts.allowedDeprecatedVersions,
allowNonAppliedPatches: opts.allowNonAppliedPatches,
autoInstallPeers: opts.autoInstallPeers,
autoInstallPeersFromHighestMatch: opts.autoInstallPeersFromHighestMatch,
currentLockfile: ctx.currentLockfile,
defaultUpdateDepth: opts.depth,
dedupeDirectDeps: opts.dedupeDirectDeps,

View File

@@ -41,6 +41,21 @@ test('do not auto install when there is no common peer dependency range intersec
])
})
test('auto install latest when there is no common peer dependency range intersection', async () => {
await addDistTag({ package: '@pnpm.e2e/peer-c', version: '2.0.0', distTag: 'latest' })
const project = prepareEmpty()
await addDependenciesToPackage({}, ['@pnpm.e2e/wants-peer-c-1', '@pnpm.e2e/wants-peer-c-2'], await testDefaults({
autoInstallPeers: true,
autoInstallPeersFromHighestMatch: true,
}))
const lockfile = await project.readLockfile()
expect(Object.keys(lockfile.packages)).toStrictEqual([
'/@pnpm.e2e/peer-c@2.0.0',
'/@pnpm.e2e/wants-peer-c-1@1.0.0(@pnpm.e2e/peer-c@2.0.0)',
'/@pnpm.e2e/wants-peer-c-2@1.0.0(@pnpm.e2e/peer-c@2.0.0)',
])
})
test('don\'t fail on linked package, when peers are auto installed', async () => {
const pkgManifest = {
dependencies: {

View File

@@ -127,6 +127,7 @@ export interface ChildrenByParentDepPath {
export interface ResolutionContext {
autoInstallPeers: boolean
autoInstallPeersFromHighestMatch: boolean
allowBuild?: (pkgName: string) => boolean
allowedDeprecatedVersions: AllowedDeprecatedVersions
appliedPatches: Set<string>
@@ -446,7 +447,10 @@ async function resolveDependenciesOfImporters (
...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers),
...childrenResults,
...postponedPeersResolution,
].map(({ missingPeers }) => missingPeers).filter(Boolean)
].map(({ missingPeers }) => missingPeers).filter(Boolean),
{
autoInstallPeersFromHighestMatch: ctx.autoInstallPeersFromHighestMatch,
}
)
return {
missingPeers: allMissingPeers,
@@ -574,6 +578,7 @@ export async function resolveDependencies (
parentPkgAliases: options.parentPkgAliases,
currentParentPkgAliases,
postponedPeersResolutionQueue,
autoInstallPeersFromHighestMatch: ctx.autoInstallPeersFromHighestMatch,
}),
}
}
@@ -585,12 +590,14 @@ async function startResolvingPeers (
parentPkgAliases,
pkgAddresses,
postponedPeersResolutionQueue,
autoInstallPeersFromHighestMatch,
}: {
childrenResults: PeersResolutionResult[]
currentParentPkgAliases: ParentPkgAliases
parentPkgAliases: ParentPkgAliases
pkgAddresses: PkgAddress[]
postponedPeersResolutionQueue: PostponedPeersResolutionFunction[]
autoInstallPeersFromHighestMatch: boolean
}
) {
const results = await Promise.all(
@@ -602,7 +609,8 @@ async function startResolvingPeers (
...filterMissingPeersFromPkgAddresses(pkgAddresses, currentParentPkgAliases, resolvedPeers),
...childrenResults,
...results,
].map(({ missingPeers }) => missingPeers).filter(Boolean)
].map(({ missingPeers }) => missingPeers).filter(Boolean),
{ autoInstallPeersFromHighestMatch }
)
return {
missingPeers: allMissingPeers,
@@ -610,7 +618,7 @@ async function startResolvingPeers (
}
}
function mergePkgsDeps (pkgsDeps: Array<Record<string, string>>): Record<string, string> {
function mergePkgsDeps (pkgsDeps: Array<Record<string, string>>, opts: { autoInstallPeersFromHighestMatch: boolean }): Record<string, string> {
const groupedRanges: Record<string, string[]> = {}
for (const deps of pkgsDeps) {
for (const [name, range] of Object.entries(deps)) {
@@ -625,6 +633,8 @@ function mergePkgsDeps (pkgsDeps: Array<Record<string, string>>): Record<string,
const intersection = safeIntersect(ranges)
if (intersection) {
mergedPkgDeps[name] = intersection
} else if (opts.autoInstallPeersFromHighestMatch) {
mergedPkgDeps[name] = ranges.join(' || ')
}
}
return mergedPkgDeps

View File

@@ -71,6 +71,7 @@ export interface ImporterToResolveGeneric<T> extends Importer<T> {
export interface ResolveDependenciesOptions {
autoInstallPeers?: boolean
autoInstallPeersFromHighestMatch?: boolean
allowBuild?: (pkgName: string) => boolean
allowedDeprecatedVersions: AllowedDeprecatedVersions
allowNonAppliedPatches: boolean
@@ -109,6 +110,7 @@ export async function resolveDependencyTree<T> (
const wantedToBeSkippedPackageIds = new Set<string>()
const ctx = {
autoInstallPeers: opts.autoInstallPeers === true,
autoInstallPeersFromHighestMatch: opts.autoInstallPeersFromHighestMatch === true,
allowBuild: opts.allowBuild,
allowedDeprecatedVersions: opts.allowedDeprecatedVersions,
childrenByParentDepPath: {} as ChildrenByParentDepPath,