fix: throw a meaningful error message on broken lockfile (#4387)

When `node-linker` is set to `hoisted`, a meaningful error message
should be thrown if the lockfile is broken and `pnpm install` is
executed.
This commit is contained in:
Zoltan Kochan
2022-02-24 06:30:41 +02:00
parent 2149d345a5
commit 70ba51da99
14 changed files with 68 additions and 16 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/filter-lockfile": patch
---
Update `@pnpm/error`.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/real-hoist": patch
"pnpm": patch
---
Throw a meaningful error message on `pnpm install` when the lockfile is broken and `node-linker` is set to `hoisted`.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/error": minor
---
Add new error object: LockfileMissingDependencyError.

View File

@@ -31,5 +31,8 @@
"funding": "https://opencollective.com/pnpm",
"devDependencies": {
"@pnpm/error": "workspace:2.0.0"
},
"dependencies": {
"@pnpm/constants": "workspace:5.0.0"
}
}

View File

@@ -1,3 +1,5 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
export default class PnpmError extends Error {
public readonly code: string
public readonly hint?: string
@@ -55,3 +57,13 @@ function hideAuthInformation (authHeaderValue: string) {
const [authType, token] = authHeaderValue.split(' ')
return `${authType} ${token.substring(0, 4)}[hidden]`
}
export class LockfileMissingDependencyError extends PnpmError {
constructor (depPath: string) {
const message = `Broken lockfile: no entry for '${depPath}' in ${WANTED_LOCKFILE}`
super('LOCKFILE_MISSING_DEPENDENCY', message, {
hint: 'This issue is probably caused by a badly resolved merge conflict.\n' +
'To fix the lockfile, run \'pnpm install --no-frozen-lockfile\'.',
})
}
}

View File

@@ -8,5 +8,9 @@
"src/**/*.ts",
"../../typings/**/*.d.ts"
],
"references": []
"references": [
{
"path": "../constants"
}
]
}

View File

@@ -1,12 +0,0 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import PnpmError from '@pnpm/error'
export default class LockfileMissingDependencyError extends PnpmError {
constructor (depPath: string) {
const message = `Broken lockfile: no entry for '${depPath}' in ${WANTED_LOCKFILE}`
super('LOCKFILE_MISSING_DEPENDENCY', message, {
hint: 'This issue is probably caused by a badly resolved merge conflict.\n' +
'To fix the lockfile, run \'pnpm install --no-frozen-lockfile\'.',
})
}
}

View File

@@ -1,4 +1,5 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { LockfileMissingDependencyError } from '@pnpm/error'
import {
Lockfile,
PackageSnapshots,
@@ -7,7 +8,6 @@ import lockfileWalker, { LockfileWalkerStep } from '@pnpm/lockfile-walker'
import pnpmLogger from '@pnpm/logger'
import { DependenciesField } from '@pnpm/types'
import filterImporter from './filterImporter'
import LockfileMissingDependencyError from './LockfileMissingDependencyError'
const logger = pnpmLogger('lockfile')

View File

@@ -1,4 +1,5 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { LockfileMissingDependencyError } from '@pnpm/error'
import {
Lockfile,
PackageSnapshots,
@@ -10,7 +11,6 @@ import { DependenciesField } from '@pnpm/types'
import * as dp from 'dependency-path'
import unnest from 'ramda/src/unnest'
import filterImporter from './filterImporter'
import LockfileMissingDependencyError from './LockfileMissingDependencyError'
const logger = pnpmLogger('lockfile')

View File

@@ -30,6 +30,7 @@
"compile": "tsc --build && pnpm run lint --fix"
},
"dependencies": {
"@pnpm/error": "workspace:2.0.0",
"@pnpm/lockfile-utils": "workspace:3.2.1",
"@yarnpkg/nm": "3.0.1-rc.10",
"dependency-path": "workspace:8.0.11"

View File

@@ -1,3 +1,4 @@
import { LockfileMissingDependencyError } from '@pnpm/error'
import {
Lockfile,
nameVerFromPkgSnapshot,
@@ -70,8 +71,10 @@ function toTree (nodes: Map<string, HoisterTree>, lockfile: Lockfile, deps: Reco
const key = `${alias}:${depPath}`
let node = nodes.get(key)
if (!node) {
// const { name, version, peersSuffix } = nameVerFromPkgSnapshot(depPath, lockfile.packages![depPath])
const pkgSnapshot = lockfile.packages![depPath]
if (!pkgSnapshot) {
throw new LockfileMissingDependencyError(depPath)
}
const pkgName = nameVerFromPkgSnapshot(depPath, pkgSnapshot).name
node = {
name: alias,

View File

@@ -6,3 +6,20 @@ test('hoist', async () => {
const lockfile = await readWantedLockfile(path.join(__dirname, '../../..'), { ignoreIncompatible: true })
expect(hoist(lockfile!)).toBeTruthy()
})
test('hoist throws an error if the lockfile is broken', () => {
expect(() => hoist({
lockfileVersion: 5,
importers: {
'.': {
dependencies: {
foo: '1.0.0',
},
specifiers: {
foo: '1.0.0',
},
},
},
packages: {},
})).toThrow(/Broken lockfile/)
})

View File

@@ -12,6 +12,9 @@
{
"path": "../dependency-path"
},
{
"path": "../error"
},
{
"path": "../lockfile-file"
},

5
pnpm-lock.yaml generated
View File

@@ -717,7 +717,10 @@ importers:
packages/error:
specifiers:
'@pnpm/constants': workspace:5.0.0
'@pnpm/error': workspace:2.0.0
dependencies:
'@pnpm/constants': link:../constants
devDependencies:
'@pnpm/error': 'link:'
@@ -3054,6 +3057,7 @@ importers:
packages/real-hoist:
specifiers:
'@pnpm/error': workspace:2.0.0
'@pnpm/lockfile-file': workspace:4.3.0
'@pnpm/lockfile-utils': workspace:3.2.1
'@pnpm/logger': ^4.0.0
@@ -3061,6 +3065,7 @@ importers:
'@yarnpkg/nm': 3.0.1-rc.10
dependency-path: workspace:8.0.11
dependencies:
'@pnpm/error': link:../error
'@pnpm/lockfile-utils': link:../lockfile-utils
'@yarnpkg/nm': 3.0.1-rc.10
dependency-path: link:../dependency-path