mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-24 16:46:06 -04:00
fix(env-installer): prune env lockfile when updating a config dep (#11892)
`pnpm add --config <pkg>` (via `resolveConfigDeps`) wrote the env lockfile without pruning, so optional subdependencies from the previously resolved version remained as orphans. Mirror the prune call from `resolveAndInstallConfigDeps`.
This commit is contained in:
6
.changeset/prune-env-lockfile-on-config-dep-update.md
Normal file
6
.changeset/prune-env-lockfile-on-config-dep-update.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/installing.env-installer": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Fixed `pnpm add --config` leaving orphan entries in `pnpm-lock.env.yaml` (the optional subdependencies of the previously resolved version of the updated config dependency).
|
||||
@@ -15,6 +15,7 @@ import { parseWantedDependency } from '@pnpm/resolving.parse-wanted-dependency'
|
||||
import type { ConfigDependencies, ConfigDependencySpecifiers, RegistryConfig } from '@pnpm/types'
|
||||
|
||||
import { installConfigDeps, type InstallConfigDepsOpts } from './installConfigDeps.js'
|
||||
import { pruneEnvLockfile } from './pruneEnvLockfile.js'
|
||||
import { resolveOptionalSubdeps } from './resolveOptionalSubdeps.js'
|
||||
|
||||
export type ResolveConfigDepsOpts = CreateFetchFromRegistryOptions & ResolverFactoryOptions & InstallConfigDepsOpts & {
|
||||
@@ -78,6 +79,8 @@ export async function resolveConfigDeps (configDeps: string[], opts: ResolveConf
|
||||
envLockfile.snapshots[pkgKey] = optionalSubdeps ? { optionalDependencies: optionalSubdeps } : {}
|
||||
}))
|
||||
|
||||
pruneEnvLockfile(envLockfile)
|
||||
|
||||
await Promise.all([
|
||||
writeSettings({
|
||||
...opts,
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from 'node:path'
|
||||
|
||||
import { expect, test } from '@jest/globals'
|
||||
import { resolveConfigDeps } from '@pnpm/installing.env-installer'
|
||||
import { readEnvLockfile } from '@pnpm/lockfile.fs'
|
||||
import { readEnvLockfile, writeEnvLockfile } from '@pnpm/lockfile.fs'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import { getIntegrity, REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
import { createTempStore } from '@pnpm/testing.temp-store'
|
||||
@@ -140,6 +140,46 @@ test('rejects an optionalDependency declared with a non-exact version', async ()
|
||||
})).rejects.toThrow(/only exact versions are supported/)
|
||||
})
|
||||
|
||||
test('orphan optional subdeps from a previous resolution are pruned', async () => {
|
||||
prepareEmpty()
|
||||
const { storeController, storeDir } = createTempStore()
|
||||
|
||||
// Simulate a prior resolution that left optional subdeps for a now-removed
|
||||
// version of a config dependency. The stale `foo@99.0.0` and its optional
|
||||
// subdep `bar@1.0.0` are not referenced from any current configDependency.
|
||||
await writeEnvLockfile(process.cwd(), {
|
||||
lockfileVersion: '9.0',
|
||||
importers: {
|
||||
'.': { configDependencies: {} },
|
||||
},
|
||||
packages: {
|
||||
'@pnpm.e2e/foo@99.0.0': { resolution: { integrity: 'sha512-stale==' } },
|
||||
'@pnpm.e2e/bar@1.0.0': { resolution: { integrity: 'sha512-stale==' } },
|
||||
},
|
||||
snapshots: {
|
||||
'@pnpm.e2e/foo@99.0.0': { optionalDependencies: { '@pnpm.e2e/bar': '1.0.0' } },
|
||||
'@pnpm.e2e/bar@1.0.0': { optional: true },
|
||||
},
|
||||
})
|
||||
|
||||
await resolveConfigDeps(['@pnpm.e2e/foo@100.0.0'], {
|
||||
registries: {
|
||||
default: registry,
|
||||
},
|
||||
rootDir: process.cwd(),
|
||||
cacheDir: path.resolve('cache'),
|
||||
store: storeController,
|
||||
storeDir,
|
||||
})
|
||||
|
||||
const envLockfile = await readEnvLockfile(process.cwd())
|
||||
expect(envLockfile!.packages['@pnpm.e2e/foo@99.0.0']).toBeUndefined()
|
||||
expect(envLockfile!.packages['@pnpm.e2e/bar@1.0.0']).toBeUndefined()
|
||||
expect(envLockfile!.snapshots['@pnpm.e2e/foo@99.0.0']).toBeUndefined()
|
||||
expect(envLockfile!.snapshots['@pnpm.e2e/bar@1.0.0']).toBeUndefined()
|
||||
expect(envLockfile!.packages['@pnpm.e2e/foo@100.0.0']).toBeDefined()
|
||||
})
|
||||
|
||||
test('fails with frozenLockfile', async () => {
|
||||
prepareEmpty()
|
||||
const { storeController, storeDir } = createTempStore()
|
||||
|
||||
Reference in New Issue
Block a user