Skip to content

Commit c7eefdd

Browse files
authored
fix: pnpm update --filter --latest should only change relevant packages and projects, with dedupe-peer-dependents=true (#8905)
* test(update): add failing tests for update with dedupe-peer-dependents=true Relates to #8877 * fix: update --filter --latest should work with dedupe-peer-dependents Fixes #8877, whereby `update --filter --latest` with `dedupe-peer-dependents` would end up updating all available dependencies for all projects. * test(pnpm): more accurate dedupePeers filtered install case * docs: add changeset for updateToLatest moving to projects/importers * docs: add changesets for pnpm and plugin-commands-installation * chore: fix tsc issue by removing unknown bound resolver property This unknown property was accepted by tsc prior to adding updateToLatest in toResovleImporter options, but now it was erroring out. This is likely a tsc quirk about the shape of the object; regardless that property is not defined, and should not be present. * test: keep only pnpm/test/monorepo/dedupePeers.test.ts There was duplicate coverage of the pnpm update --filter --latest command between two tests, so this keeps only the one dedicated to testing the dedupe-peer-dependents feature. * chore: fix unused import error
1 parent e103abe commit c7eefdd

File tree

8 files changed

+72
-6
lines changed

8 files changed

+72
-6
lines changed

.changeset/forty-yaks-jog.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@pnpm/plugin-commands-installation": patch
3+
"pnpm": patch
4+
---
5+
6+
`pnpm update --filter <pattern> --latest <pkg>` should only change the specified package for the specified workspace, when `dedupe-peer-dependents` is set to `true` [#8877](https://github.com/pnpm/pnpm/issues/8877).

.changeset/moody-berries-design.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@pnpm/resolve-dependencies": major
3+
"@pnpm/core": major
4+
---
5+
6+
The `updateToLatest` option is now part of projects/importers, instead of an option of the resolution/installation.

pkg-manager/core/src/install/extendInstallOptions.ts

-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ export interface StrictInstallOptions {
9898
unsafePerm: boolean
9999
registries: Registries
100100
tag: string
101-
updateToLatest?: boolean
102101
overrides: Record<string, string>
103102
ownLifecycleHooksStdio: 'inherit' | 'pipe'
104103
// We can automatically calculate these

pkg-manager/core/src/install/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const DEV_PREINSTALL = 'pnpm:devPreinstall'
104104

105105
interface InstallMutationOptions {
106106
update?: boolean
107+
updateToLatest?: boolean
107108
updateMatching?: UpdateMatchingFunction
108109
updatePackageManifest?: boolean
109110
}
@@ -150,6 +151,7 @@ export async function install (
150151
rootDir,
151152
update: opts.update,
152153
updateMatching: opts.updateMatching,
154+
updateToLatest: opts.updateToLatest,
153155
updatePackageManifest: opts.updatePackageManifest,
154156
},
155157
],
@@ -197,6 +199,7 @@ export async function mutateModulesInSingleProject (
197199
{
198200
...project,
199201
update: maybeOpts.update,
202+
updateToLatest: maybeOpts.updateToLatest,
200203
updateMatching: maybeOpts.updateMatching,
201204
updatePackageManifest: maybeOpts.updatePackageManifest,
202205
} as MutatedProject,
@@ -772,6 +775,7 @@ export async function addDependenciesToPackage (
772775
update: opts.update,
773776
updateMatching: opts.updateMatching,
774777
updatePackageManifest: opts.updatePackageManifest,
778+
updateToLatest: opts.updateToLatest,
775779
},
776780
],
777781
{
@@ -963,7 +967,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
963967
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
964968
storeController: opts.storeController,
965969
tag: opts.tag,
966-
updateToLatest: opts.updateToLatest,
967970
virtualStoreDir: ctx.virtualStoreDir,
968971
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
969972
wantedLockfile: ctx.wantedLockfile,

pkg-manager/plugin-commands-installation/src/recursive.ts

+2
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export async function recursive (
233233
update: opts.update,
234234
updateMatching: opts.updateMatching,
235235
updatePackageManifest: opts.updatePackageManifest,
236+
updateToLatest: opts.latest,
236237
} as MutatedProject)
237238
return
238239
case 'install':
@@ -244,6 +245,7 @@ export async function recursive (
244245
update: opts.update,
245246
updateMatching: opts.updateMatching,
246247
updatePackageManifest: opts.updatePackageManifest,
248+
updateToLatest: opts.latest,
247249
} as MutatedProject)
248250
}
249251
}))

pkg-manager/resolve-dependencies/src/index.ts

-2
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ export async function resolveDependencies (
125125
preferredVersions: opts.preferredVersions,
126126
virtualStoreDir: opts.virtualStoreDir,
127127
workspacePackages: opts.workspacePackages,
128-
updateToLatest: opts.updateToLatest,
129128
noDependencySelectors: importers.every(({ wantedDependencies }) => wantedDependencies.length === 0),
130-
injectWorkspacePackages: opts.injectWorkspacePackages,
131129
})
132130
const projectsToResolve = await Promise.all(importers.map(async (project) => _toResolveImporter(project)))
133131
const {

pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export interface Importer<WantedDepExtraProps> {
8888
export interface ImporterToResolveGeneric<WantedDepExtraProps> extends Importer<WantedDepExtraProps> {
8989
updatePackageManifest: boolean
9090
updateMatching?: (pkgName: string) => boolean
91+
updateToLatest?: boolean
9192
hasRemovedDependencies?: boolean
9293
preferredVersions?: PreferredVersions
9394
wantedDependencies: Array<WantedDepExtraProps & WantedDependency & { updateDepth: number }>
@@ -127,7 +128,6 @@ export interface ResolveDependenciesOptions {
127128
wantedLockfile: LockfileObject
128129
workspacePackages: WorkspacePackages
129130
supportedArchitectures?: SupportedArchitectures
130-
updateToLatest?: boolean
131131
peersSuffixMaxLength: number
132132
}
133133

@@ -214,9 +214,9 @@ export async function resolveDependencyTree<T> (
214214
},
215215
updateDepth: -1,
216216
updateMatching: importer.updateMatching,
217+
updateToLatest: importer.updateToLatest,
217218
prefix: importer.rootDir,
218219
supportedArchitectures: opts.supportedArchitectures,
219-
updateToLatest: opts.updateToLatest,
220220
}
221221
return {
222222
updatePackageManifest: importer.updatePackageManifest,

pnpm/test/monorepo/dedupePeers.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,58 @@ auto-install-peers=false`, 'utf8')
9191
expect(loadJsonFile<any>('project-2/package.json').dependencies['@pnpm.e2e/abc-grand-parent-with-c']).toBe('^1.0.1') // eslint-disable-line
9292
})
9393

94+
// Covers https://github.com/pnpm/pnpm/issues/8877
95+
test('partial update --latest in a workspace should not affect other packages when dedupe-peer-dependents is true', async () => {
96+
await addDistTag({ package: '@pnpm.e2e/foo', version: '1.0.0', distTag: 'latest' })
97+
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })
98+
99+
preparePackages([
100+
{
101+
location: 'project-1',
102+
package: {
103+
name: 'project-1',
104+
105+
dependencies: {
106+
'@pnpm.e2e/foo': '1.0.0',
107+
'@pnpm.e2e/bar': '100.0.0',
108+
},
109+
},
110+
},
111+
{
112+
location: 'project-2',
113+
package: {
114+
name: 'project-2',
115+
116+
dependencies: {
117+
'@pnpm.e2e/foo': '1.0.0',
118+
},
119+
},
120+
},
121+
])
122+
123+
writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
124+
fs.writeFileSync('.npmrc', `dedupe-peer-dependents=true
125+
auto-install-peers=false`, 'utf8')
126+
await execPnpm(['install'])
127+
128+
await addDistTag({ package: '@pnpm.e2e/foo', version: '2.0.0', distTag: 'latest' })
129+
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.1.0', distTag: 'latest' })
130+
131+
await execPnpm(['update', '--filter', 'project-2', '--latest'])
132+
133+
// project 1's manifest is unaffected, while project 2 has foo updated
134+
expect(loadJsonFile<any>('project-1/package.json').dependencies['@pnpm.e2e/foo']).toBe('1.0.0') // eslint-disable-line
135+
expect(loadJsonFile<any>('project-1/package.json').dependencies['@pnpm.e2e/bar']).toBe('100.0.0') // eslint-disable-line
136+
expect(loadJsonFile<any>('project-2/package.json').dependencies['@pnpm.e2e/foo']).toBe('2.0.0') // eslint-disable-line
137+
138+
// similar for the importers in the lockfile; project 1 is unaffected, while
139+
// project 2 resolves the latest foo
140+
const lockfile = readYamlFile<any>(path.resolve(WANTED_LOCKFILE)) // eslint-disable-line
141+
expect(lockfile.importers['project-1']?.dependencies?.['@pnpm.e2e/foo'].version).toStrictEqual('1.0.0')
142+
expect(lockfile.importers['project-1']?.dependencies?.['@pnpm.e2e/bar'].version).toStrictEqual('100.0.0')
143+
expect(lockfile.importers['project-2']?.dependencies?.['@pnpm.e2e/foo'].version).toStrictEqual('2.0.0')
144+
})
145+
94146
// Covers https://github.com/pnpm/pnpm/issues/6154
95147
test('peer dependents deduplication should not remove peer dependencies', async () => {
96148
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.0', distTag: 'latest' })

0 commit comments

Comments
 (0)