mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-10 18:18:56 -04:00
feat: auto install peers from highest match (#7654)
This commit is contained in:
6
.changeset/hungry-pans-sneeze.md
Normal file
6
.changeset/hungry-pans-sneeze.md
Normal 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.
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user