mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-10 18:18:56 -04:00
fix: finish installation before throwing peer dependencies error (#4095)
This commit is contained in:
7
.changeset/gentle-peaches-cross.md
Normal file
7
.changeset/gentle-peaches-cross.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/core": patch
|
||||
"@pnpm/resolve-dependencies": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Installation should be finished before an error about bad/missing peer dependencies is printed and kills the process.
|
||||
@@ -74,6 +74,7 @@ import extendOptions, {
|
||||
} from './extendInstallOptions'
|
||||
import { getPreferredVersionsFromLockfile, getAllUniqueSpecs } from './getPreferredVersions'
|
||||
import linkPackages from './link'
|
||||
import reportPeerDependencyIssues from './reportPeerDependencyIssues'
|
||||
|
||||
const BROKEN_LOCKFILE_INTEGRITY_ERRORS = new Set([
|
||||
'ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE',
|
||||
@@ -716,6 +717,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
linkedDependenciesByProjectId,
|
||||
newLockfile,
|
||||
outdatedDependencies,
|
||||
peerDependencyIssues,
|
||||
wantedToBeSkippedPackageIds,
|
||||
waitTillAllFetchingsFinish,
|
||||
} = await resolveDependencies(
|
||||
@@ -739,7 +741,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
registries: ctx.registries,
|
||||
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
|
||||
storeController: opts.storeController,
|
||||
strictPeerDependencies: opts.strictPeerDependencies,
|
||||
tag: opts.tag,
|
||||
updateMatching: opts.updateMatching,
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
@@ -788,7 +789,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
symlink: opts.symlink,
|
||||
skipped: ctx.skipped,
|
||||
storeController: opts.storeController,
|
||||
strictPeerDependencies: opts.strictPeerDependencies,
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
wantedLockfile: newLockfile,
|
||||
wantedToBeSkippedPackageIds,
|
||||
@@ -961,6 +961,13 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
|
||||
await opts.storeController.close()
|
||||
|
||||
if (peerDependencyIssues.length > 0) {
|
||||
reportPeerDependencyIssues(peerDependencyIssues, {
|
||||
lockfileDir: opts.lockfileDir,
|
||||
strictPeerDependencies: opts.strictPeerDependencies,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
newLockfile,
|
||||
projects: projects.map(({ manifest, rootDir }) => ({ rootDir, manifest })),
|
||||
|
||||
@@ -64,7 +64,6 @@ export default async function linkPackages (
|
||||
symlink: boolean
|
||||
skipped: Set<string>
|
||||
storeController: StoreController
|
||||
strictPeerDependencies: boolean
|
||||
virtualStoreDir: string
|
||||
wantedLockfile: Lockfile
|
||||
wantedToBeSkippedPackageIds: Set<string>
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
import path from 'path'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import logger from '@pnpm/logger'
|
||||
import scan from 'ramda/src/scan'
|
||||
import {
|
||||
createNodeId,
|
||||
splitNodeId,
|
||||
} from './nodeIdUtils'
|
||||
import {
|
||||
DependenciesTree,
|
||||
ResolvedPackage,
|
||||
} from './resolveDependencies'
|
||||
import { PartialResolvedPackage, PeerDependencyIssue } from './resolvePeers'
|
||||
import { PeerDependencyIssue, PeerDependencyIssueLocation } from '@pnpm/resolve-dependencies'
|
||||
|
||||
export default function<T extends PartialResolvedPackage> (
|
||||
export default function (
|
||||
peerDependencyIssues: PeerDependencyIssue[],
|
||||
opts: {
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
lockfileDir: string
|
||||
strictPeerDependencies: boolean
|
||||
}
|
||||
@@ -44,19 +33,13 @@ export default function<T extends PartialResolvedPackage> (
|
||||
}
|
||||
}
|
||||
|
||||
function peerDependencyIssueMessage<T extends PartialResolvedPackage> (
|
||||
function peerDependencyIssueMessage (
|
||||
opts: {
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
lockfileDir: string
|
||||
peerDependencyIssue: PeerDependencyIssue
|
||||
}
|
||||
) {
|
||||
const friendlyPath = nodeIdToFriendlyPath({
|
||||
dependenciesTree: opts.dependenciesTree,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
nodeId: opts.peerDependencyIssue.nodeId,
|
||||
rootDir: opts.peerDependencyIssue.rootDir,
|
||||
})
|
||||
const friendlyPath = locationToFriendlyPath(opts.peerDependencyIssue.location)
|
||||
if (opts.peerDependencyIssue.foundPeerVersion) {
|
||||
return `${friendlyPath ? `${friendlyPath}: ` : ''}${packageFriendlyId(opts.peerDependencyIssue.pkg)} \
|
||||
requires a peer of ${opts.peerDependencyIssue.wantedPeer.name}@${opts.peerDependencyIssue.wantedPeer.range} but version ${opts.peerDependencyIssue.foundPeerVersion} was installed.`
|
||||
@@ -69,26 +52,10 @@ function packageFriendlyId (manifest: {name: string, version: string}) {
|
||||
return `${manifest.name}@${manifest.version}`
|
||||
}
|
||||
|
||||
function nodeIdToFriendlyPath<T extends PartialResolvedPackage> (
|
||||
{
|
||||
dependenciesTree,
|
||||
lockfileDir,
|
||||
nodeId,
|
||||
rootDir,
|
||||
}: {
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
lockfileDir: string
|
||||
nodeId: string
|
||||
rootDir: string
|
||||
}
|
||||
) {
|
||||
const parts = splitNodeId(nodeId).slice(0, -1)
|
||||
const result = scan((prevNodeId, pkgId) => createNodeId(prevNodeId, pkgId), '>', parts)
|
||||
.slice(2)
|
||||
.map((nid) => (dependenciesTree[nid].resolvedPackage as ResolvedPackage).name)
|
||||
const projectPath = path.relative(lockfileDir, rootDir)
|
||||
if (projectPath) {
|
||||
result.unshift(projectPath)
|
||||
function locationToFriendlyPath (location: PeerDependencyIssueLocation) {
|
||||
const result = location.parents.map(({ name }) => name)
|
||||
if (location.projectPath) {
|
||||
result.unshift(location.projectPath)
|
||||
}
|
||||
return result.join(' > ')
|
||||
}
|
||||
@@ -69,7 +69,6 @@ export async function listMissingPeers (
|
||||
registries: ctx.registries,
|
||||
saveWorkspaceProtocol: false, // this doesn't matter in our case. We won't write changes to package.json files
|
||||
storeController: opts.storeController,
|
||||
strictPeerDependencies: false,
|
||||
tag: 'latest',
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
wantedLockfile: ctx.wantedLockfile,
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
} from '@pnpm/types'
|
||||
import difference from 'ramda/src/difference'
|
||||
import depPathToRef from './depPathToRef'
|
||||
import reportPeerDependencyIssues from './reportPeerDependencyIssues'
|
||||
import resolveDependencyTree, {
|
||||
Importer,
|
||||
LinkedDependency,
|
||||
@@ -32,6 +31,8 @@ import resolveDependencyTree, {
|
||||
import resolvePeers, {
|
||||
GenericDependenciesGraph,
|
||||
GenericDependenciesGraphNode,
|
||||
PeerDependencyIssue,
|
||||
PeerDependencyIssueLocation,
|
||||
} from './resolvePeers'
|
||||
import toResolveImporter from './toResolveImporter'
|
||||
import updateLockfile from './updateLockfile'
|
||||
@@ -43,6 +44,8 @@ export type DependenciesGraphNode = GenericDependenciesGraphNode & ResolvedPacka
|
||||
|
||||
export {
|
||||
LinkedDependency,
|
||||
PeerDependencyIssue,
|
||||
PeerDependencyIssueLocation,
|
||||
ResolvedPackage,
|
||||
}
|
||||
|
||||
@@ -77,7 +80,6 @@ export default async function (
|
||||
defaultUpdateDepth: number
|
||||
preserveWorkspaceProtocol: boolean
|
||||
saveWorkspaceProtocol: boolean
|
||||
strictPeerDependencies: boolean
|
||||
}
|
||||
) {
|
||||
const _toResolveImporter = toResolveImporter.bind(null, {
|
||||
@@ -166,14 +168,6 @@ export default async function (
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
})
|
||||
|
||||
if (peerDependencyIssues.length > 0) {
|
||||
reportPeerDependencyIssues(peerDependencyIssues, {
|
||||
dependenciesTree,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
strictPeerDependencies: opts.strictPeerDependencies,
|
||||
})
|
||||
}
|
||||
|
||||
for (const { id, manifest } of projectsToLink) {
|
||||
for (const [alias, depPath] of Object.entries(dependenciesByProjectId[id])) {
|
||||
const projectSnapshot = opts.wantedLockfile.importers[id]
|
||||
|
||||
@@ -5,16 +5,23 @@ import { depPathToFilename } from 'dependency-path'
|
||||
import { KeyValuePair } from 'ramda'
|
||||
import fromPairs from 'ramda/src/fromPairs'
|
||||
import isEmpty from 'ramda/src/isEmpty'
|
||||
import pick from 'ramda/src/pick'
|
||||
import scan from 'ramda/src/scan'
|
||||
import semver from 'semver'
|
||||
import {
|
||||
DependenciesTree,
|
||||
DependenciesTreeNode,
|
||||
ResolvedPackage,
|
||||
} from './resolveDependencies'
|
||||
import { splitNodeId } from './nodeIdUtils'
|
||||
import { createNodeId, splitNodeId } from './nodeIdUtils'
|
||||
|
||||
export interface PeerDependencyIssueLocation {
|
||||
parents: Array<{ name: string, version: string }>
|
||||
projectPath: string
|
||||
}
|
||||
|
||||
export interface PeerDependencyIssue {
|
||||
nodeId: string
|
||||
location: PeerDependencyIssueLocation
|
||||
pkg: PartialResolvedPackage
|
||||
rootDir: string
|
||||
foundPeerVersion?: string
|
||||
@@ -401,7 +408,12 @@ function resolvePeers<T extends PartialResolvedPackage> (
|
||||
continue
|
||||
}
|
||||
ctx.peerDependencyIssues.push({
|
||||
nodeId: ctx.nodeId,
|
||||
location: getLocationFromNodeId({
|
||||
dependenciesTree: ctx.dependenciesTree,
|
||||
nodeId: ctx.nodeId,
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
rootDir: ctx.rootDir,
|
||||
}),
|
||||
pkg: ctx.resolvedPackage,
|
||||
rootDir: ctx.rootDir,
|
||||
wantedPeer: {
|
||||
@@ -414,7 +426,12 @@ function resolvePeers<T extends PartialResolvedPackage> (
|
||||
|
||||
if (!semver.satisfies(resolved.version, peerVersionRange, { loose: true })) {
|
||||
ctx.peerDependencyIssues.push({
|
||||
nodeId: ctx.nodeId,
|
||||
location: getLocationFromNodeId({
|
||||
dependenciesTree: ctx.dependenciesTree,
|
||||
nodeId: ctx.nodeId,
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
rootDir: ctx.rootDir,
|
||||
}),
|
||||
pkg: ctx.resolvedPackage,
|
||||
rootDir: ctx.rootDir,
|
||||
foundPeerVersion: resolved.version,
|
||||
@@ -430,6 +447,30 @@ function resolvePeers<T extends PartialResolvedPackage> (
|
||||
return { resolvedPeers, missingPeers }
|
||||
}
|
||||
|
||||
function getLocationFromNodeId<T> (
|
||||
{
|
||||
dependenciesTree,
|
||||
lockfileDir,
|
||||
nodeId,
|
||||
rootDir,
|
||||
}: {
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
lockfileDir: string
|
||||
nodeId: string
|
||||
rootDir: string
|
||||
}
|
||||
) {
|
||||
const parts = splitNodeId(nodeId).slice(0, -1)
|
||||
const parents = scan((prevNodeId, pkgId) => createNodeId(prevNodeId, pkgId), '>', parts)
|
||||
.slice(2)
|
||||
.map((nid) => pick(['name', 'version'], dependenciesTree[nid].resolvedPackage as ResolvedPackage))
|
||||
const projectPath = path.relative(lockfileDir, rootDir)
|
||||
return {
|
||||
projectPath,
|
||||
parents,
|
||||
}
|
||||
}
|
||||
|
||||
interface ParentRefs {
|
||||
[name: string]: ParentRef
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user