fix: catalog snapshots removed on filtered install with dedupe-peer-dependents=false (#9175)

* test: add catalog test for --filter and dedupe-peer-dependents=false

* fix: disable dedupe-injected-deps when deploying

* fix: catalog snapshots removed with dedupe-peer-dependents=false
This commit is contained in:
Brandon Cheng
2025-03-02 06:42:46 -05:00
committed by GitHub
parent 7ff67fa0e5
commit e4eeafdb55
4 changed files with 95 additions and 1 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/plugin-commands-deploy": patch
"@pnpm/plugin-commands-installation": patch
"@pnpm/core": patch
pnpm: patch
---
Fix a bug causing entries in the `catalogs` section of the `pnpm-lock.yaml` file to be removed when `dedupe-peer-dependents=false` on a filtered install. [#9112](https://github.com/pnpm/pnpm/issues/9112)

View File

@@ -380,6 +380,74 @@ test('lockfile catalog snapshots do not contain stale references on --filter', a
expect(loadJsonFile<ProjectManifest>(pathToIsPositivePkgJson)?.version).toBe('3.1.0')
})
// Regression test for https://github.com/pnpm/pnpm/issues/9112
test('dedupe-peer-dependents=false with --filter does not erase catalog snapshots', async () => {
const { options, projects, readLockfile } = preparePackagesAndReturnObjects([
{
name: 'project1',
dependencies: {},
},
{
name: 'project2',
dependencies: {
'is-positive': 'catalog:',
},
},
])
await mutateModules(installProjects(projects), {
...options,
lockfileOnly: true,
dedupePeerDependents: false,
catalogs: {
default: {
'is-positive': '1.0.0',
},
},
})
expect(readLockfile().catalogs).toStrictEqual({
default: {
'is-positive': { specifier: '1.0.0', version: '1.0.0' },
},
})
// Perform a filtered install with only project 1. The catalog protocol usage
// in project 2 should be retained.
const onlyProject1 = installProjects(projects).slice(0, 1)
expect(onlyProject1).toMatchObject([{ id: 'project1' }])
await mutateModules(onlyProject1, {
...options,
lockfileOnly: true,
dedupePeerDependents: false,
catalogs: {
default: {
// Modify the original specifier above from "1.0.0" to "^1.0.0" in order
// to force a resolution instead of a frozen install.
'is-positive': '^1.0.0',
},
},
})
// The catalogs snapshot section was erased in the bug report from
// https://github.com/pnpm/pnpm/issues/9112 when dedupe-peer-dependents=false.
expect(readLockfile()).toEqual(expect.objectContaining({
catalogs: {
default: {
'is-positive': { specifier: '^1.0.0', version: '1.0.0' },
},
},
importers: expect.objectContaining({
project1: {},
project2: expect.objectContaining({
dependencies: {
'is-positive': { specifier: 'catalog:', version: '1.0.0' },
},
}),
}),
}))
})
// Regression test for https://github.com/pnpm/pnpm/issues/8639
test('--fix-lockfile with --filter does not erase catalog snapshots', async () => {
const { options, projects, readLockfile } = preparePackagesAndReturnObjects([

View File

@@ -219,8 +219,19 @@ when running add/update with the --workspace option')
})
}
const didUserConfigureCatalogs = Object.values(opts.catalogs ?? {})
.some(catalog => Object.keys(catalog ?? {}).length > 0)
// pnpm catalogs and dedupe-peer-dependents are features that require the
// allProjectsGraph to contain all projects to correctly update the wanted
// lockfile. Otherwise the wanted lockfile would be partially updated for
// only the selected projects specified for the filtered install.
//
// This should still be performance since only dependencies for the
// selectedProjectsGraph are installed. The allProjectsGraph is only used
// to compute the wanted lockfile.
let allProjectsGraph!: ProjectsGraph
if (opts.dedupePeerDependents) {
if (didUserConfigureCatalogs || opts.dedupePeerDependents) {
allProjectsGraph = opts.allProjectsGraph ?? createPkgGraph(allProjects, {
linkWorkspacePackages: Boolean(opts.linkWorkspacePackages),
}).graph

View File

@@ -135,6 +135,13 @@ export async function handler (opts: DeployOptions, params: string[]): Promise<v
// doesn't work with filters right now.
// Related issue: https://github.com/pnpm/pnpm/issues/6858
dedupePeerDependents: false,
// If enabled, dedupe-injected-deps will symlink workspace packages in the
// deployed dir to their original (non-deployed) directory in an attempt to
// dedupe workspace packages that don't need to be injected. The deployed
// dir shouldn't have symlinks to the original workspace. Disable
// dedupe-injected-deps to always inject workspace packages since copying is
// desirable.
dedupeInjectedDeps: false,
depth: Infinity,
hooks: {
...opts.hooks,