mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-23 09:31:52 -04:00
fix(headless): skip optional dep that cannot be installed (#3091)
close #3090
This commit is contained in:
5
.changeset/empty-snails-grin.md
Normal file
5
.changeset/empty-snails-grin.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/headless": patch
|
||||
---
|
||||
|
||||
A failing optional dependency should not cause a crash of headless installation.
|
||||
@@ -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,
|
||||
|
||||
14
packages/headless/test/fixtures/has-nonexistent-optional-dep/package.json
vendored
Normal file
14
packages/headless/test/fixtures/has-nonexistent-optional-dep/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
103
packages/headless/test/fixtures/has-nonexistent-optional-dep/pnpm-lock.yaml
generated
vendored
Normal file
103
packages/headless/test/fixtures/has-nonexistent-optional-dep/pnpm-lock.yaml
generated
vendored
Normal 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
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user