fix(headless): skip optional dep that cannot be installed (#3091)

close #3090
This commit is contained in:
Zoltan Kochan
2021-01-22 11:30:25 +02:00
committed by GitHub
parent 698d7afcd9
commit 1c851f2a6b
5 changed files with 165 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/headless": patch
---
A failing optional dependency should not cause a crash of headless installation.

View File

@@ -50,6 +50,7 @@ import packageIsInstallable from '@pnpm/package-is-installable'
import { fromDir as readPackageFromDir } from '@pnpm/read-package-json'
import { readProjectManifestOnly, safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
import {
FetchPackageToStoreFunction,
PackageFilesResponse,
StoreController,
} from '@pnpm/store-controller-types'
@@ -592,13 +593,19 @@ async function lockfileToDepGraph (
requester: opts.lockfileDir,
status: 'resolved',
})
let fetchResponse = opts.storeController.fetchPackage({
force: false,
lockfileDir: opts.lockfileDir,
pkgId: packageId,
resolution,
})
if (fetchResponse instanceof Promise) fetchResponse = await fetchResponse
let fetchResponse!: ReturnType<FetchPackageToStoreFunction>
try {
fetchResponse = opts.storeController.fetchPackage({
force: false,
lockfileDir: opts.lockfileDir,
pkgId: packageId,
resolution,
})
if (fetchResponse instanceof Promise) fetchResponse = await fetchResponse
} catch (err) {
if (pkgSnapshot.optional) return
throw err
}
fetchResponse.files() // eslint-disable-line
.then(({ fromStore }) => {
progressLogger.debug({
@@ -741,7 +748,13 @@ function linkAllPkgs (
) {
return Promise.all(
depNodes.map(async (depNode) => {
const filesResponse = await depNode.fetchingFiles()
let filesResponse!: PackageFilesResponse
try {
filesResponse = await depNode.fetchingFiles()
} catch (err) {
if (depNode.optional) return
throw err
}
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
filesResponse,

View File

@@ -0,0 +1,14 @@
{
"name": "has-nonexistent-optional-dep",
"version": "1.0.0",
"dependencies": {
"is-positive": "^1.0.0",
"rimraf": "^2.6.2"
},
"devDependencies": {
"is-negative": "^2.1.0"
},
"optionalDependencies": {
"@zkochan/not-exists": "1.2.0"
}
}

View File

@@ -0,0 +1,103 @@
dependencies:
is-positive: 1.0.0
rimraf: 2.7.1
devDependencies:
is-negative: 2.1.0
lockfileVersion: 5.1
optionalDependencies:
'@zkochan/not-exists': 1.2.0
packages:
/balanced-match/1.0.0:
dev: false
resolution:
integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
/brace-expansion/1.1.11:
dependencies:
balanced-match: 1.0.0
concat-map: 0.0.1
dev: false
resolution:
integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
/@zkochan/not-exists/1.2.0:
dev: false
engines:
node: '>=0.1.90'
optional: true
resolution:
integrity: sha512-lweugcX5nailCqZBttArTojZZpHGWhmFJX78KJHlxwhM8tLAy5QCgRgRxrubrksdvA+2Y3inWG5TToyyjL82BQ==
/concat-map/0.0.1:
dev: false
resolution:
integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
/fs.realpath/1.0.0:
dev: false
resolution:
integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
/glob/7.1.6:
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 3.0.4
once: 1.4.0
path-is-absolute: 1.0.1
dev: false
resolution:
integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
/inflight/1.0.6:
dependencies:
once: 1.4.0
wrappy: 1.0.2
dev: false
resolution:
integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
/inherits/2.0.4:
dev: false
resolution:
integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
/is-negative/2.1.0:
dev: true
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-8Nhjd6oVpkw0lh84rCqb4rQKEYc=
/is-positive/1.0.0:
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-iACYVrZKLx632LsBeUGEJK4EUss=
/minimatch/3.0.4:
dependencies:
brace-expansion: 1.1.11
dev: false
resolution:
integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
/once/1.4.0:
dependencies:
wrappy: 1.0.2
dev: false
resolution:
integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
/path-is-absolute/1.0.1:
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
/rimraf/2.7.1:
dependencies:
glob: 7.1.6
dev: false
hasBin: true
resolution:
integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
/wrappy/1.0.2:
dev: false
resolution:
integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
specifiers:
'@zkochan/not-exists': 1.2.0
is-negative: ^2.1.0
is-positive: ^1.0.0
rimraf: ^2.6.2

View File

@@ -233,6 +233,28 @@ test('not installing optional deps', async () => {
await project.has('pkg-with-good-optional')
})
test('skipping optional dependency if it cannot be fetched', async () => {
const prefix = path.join(fixtures, 'has-nonexistent-optional-dep')
const reporter = sinon.spy()
await headless(await testDefaults({
lockfileDir: prefix,
reporter,
}, {
retry: {
retries: 0,
},
}))
const project = assertProject(prefix)
expect(project.requireModule('is-positive')).toBeTruthy()
expect(project.requireModule('rimraf')).toBeTruthy()
expect(project.requireModule('is-negative')).toBeTruthy()
expect(await project.readCurrentLockfile()).toBeTruthy()
expect(await project.readModulesManifest()).toBeTruthy()
})
test('run pre/postinstall scripts', async () => {
const prefix = path.join(fixtures, 'deps-have-lifecycle-scripts')
const outputJsonPath = path.join(prefix, 'output.json')