Skip to content

Commit

Permalink
fix: pnpm update --filter --latest should only change relevant pack…
Browse files Browse the repository at this point in the history
…ages 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
  • Loading branch information
fpapado authored Dec 29, 2024
1 parent e103abe commit c7eefdd
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changeset/forty-yaks-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-installation": patch
"pnpm": patch
---

`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).
6 changes: 6 additions & 0 deletions .changeset/moody-berries-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pnpm/resolve-dependencies": major
"@pnpm/core": major
---

The `updateToLatest` option is now part of projects/importers, instead of an option of the resolution/installation.
1 change: 0 additions & 1 deletion pkg-manager/core/src/install/extendInstallOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ export interface StrictInstallOptions {
unsafePerm: boolean
registries: Registries
tag: string
updateToLatest?: boolean
overrides: Record<string, string>
ownLifecycleHooksStdio: 'inherit' | 'pipe'
// We can automatically calculate these
Expand Down
5 changes: 4 additions & 1 deletion pkg-manager/core/src/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const DEV_PREINSTALL = 'pnpm:devPreinstall'

interface InstallMutationOptions {
update?: boolean
updateToLatest?: boolean
updateMatching?: UpdateMatchingFunction
updatePackageManifest?: boolean
}
Expand Down Expand Up @@ -150,6 +151,7 @@ export async function install (
rootDir,
update: opts.update,
updateMatching: opts.updateMatching,
updateToLatest: opts.updateToLatest,
updatePackageManifest: opts.updatePackageManifest,
},
],
Expand Down Expand Up @@ -197,6 +199,7 @@ export async function mutateModulesInSingleProject (
{
...project,
update: maybeOpts.update,
updateToLatest: maybeOpts.updateToLatest,
updateMatching: maybeOpts.updateMatching,
updatePackageManifest: maybeOpts.updatePackageManifest,
} as MutatedProject,
Expand Down Expand Up @@ -772,6 +775,7 @@ export async function addDependenciesToPackage (
update: opts.update,
updateMatching: opts.updateMatching,
updatePackageManifest: opts.updatePackageManifest,
updateToLatest: opts.updateToLatest,
},
],
{
Expand Down Expand Up @@ -963,7 +967,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
storeController: opts.storeController,
tag: opts.tag,
updateToLatest: opts.updateToLatest,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
wantedLockfile: ctx.wantedLockfile,
Expand Down
2 changes: 2 additions & 0 deletions pkg-manager/plugin-commands-installation/src/recursive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ export async function recursive (
update: opts.update,
updateMatching: opts.updateMatching,
updatePackageManifest: opts.updatePackageManifest,
updateToLatest: opts.latest,
} as MutatedProject)
return
case 'install':
Expand All @@ -244,6 +245,7 @@ export async function recursive (
update: opts.update,
updateMatching: opts.updateMatching,
updatePackageManifest: opts.updatePackageManifest,
updateToLatest: opts.latest,
} as MutatedProject)
}
}))
Expand Down
2 changes: 0 additions & 2 deletions pkg-manager/resolve-dependencies/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ export async function resolveDependencies (
preferredVersions: opts.preferredVersions,
virtualStoreDir: opts.virtualStoreDir,
workspacePackages: opts.workspacePackages,
updateToLatest: opts.updateToLatest,
noDependencySelectors: importers.every(({ wantedDependencies }) => wantedDependencies.length === 0),
injectWorkspacePackages: opts.injectWorkspacePackages,
})
const projectsToResolve = await Promise.all(importers.map(async (project) => _toResolveImporter(project)))
const {
Expand Down
4 changes: 2 additions & 2 deletions pkg-manager/resolve-dependencies/src/resolveDependencyTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface Importer<WantedDepExtraProps> {
export interface ImporterToResolveGeneric<WantedDepExtraProps> extends Importer<WantedDepExtraProps> {
updatePackageManifest: boolean
updateMatching?: (pkgName: string) => boolean
updateToLatest?: boolean
hasRemovedDependencies?: boolean
preferredVersions?: PreferredVersions
wantedDependencies: Array<WantedDepExtraProps & WantedDependency & { updateDepth: number }>
Expand Down Expand Up @@ -127,7 +128,6 @@ export interface ResolveDependenciesOptions {
wantedLockfile: LockfileObject
workspacePackages: WorkspacePackages
supportedArchitectures?: SupportedArchitectures
updateToLatest?: boolean
peersSuffixMaxLength: number
}

Expand Down Expand Up @@ -214,9 +214,9 @@ export async function resolveDependencyTree<T> (
},
updateDepth: -1,
updateMatching: importer.updateMatching,
updateToLatest: importer.updateToLatest,
prefix: importer.rootDir,
supportedArchitectures: opts.supportedArchitectures,
updateToLatest: opts.updateToLatest,
}
return {
updatePackageManifest: importer.updatePackageManifest,
Expand Down
52 changes: 52 additions & 0 deletions pnpm/test/monorepo/dedupePeers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,58 @@ auto-install-peers=false`, 'utf8')
expect(loadJsonFile<any>('project-2/package.json').dependencies['@pnpm.e2e/abc-grand-parent-with-c']).toBe('^1.0.1') // eslint-disable-line
})

// Covers https://github.com/pnpm/pnpm/issues/8877
test('partial update --latest in a workspace should not affect other packages when dedupe-peer-dependents is true', async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '1.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })

preparePackages([
{
location: 'project-1',
package: {
name: 'project-1',

dependencies: {
'@pnpm.e2e/foo': '1.0.0',
'@pnpm.e2e/bar': '100.0.0',
},
},
},
{
location: 'project-2',
package: {
name: 'project-2',

dependencies: {
'@pnpm.e2e/foo': '1.0.0',
},
},
},
])

writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
fs.writeFileSync('.npmrc', `dedupe-peer-dependents=true
auto-install-peers=false`, 'utf8')
await execPnpm(['install'])

await addDistTag({ package: '@pnpm.e2e/foo', version: '2.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.1.0', distTag: 'latest' })

await execPnpm(['update', '--filter', 'project-2', '--latest'])

// project 1's manifest is unaffected, while project 2 has foo updated
expect(loadJsonFile<any>('project-1/package.json').dependencies['@pnpm.e2e/foo']).toBe('1.0.0') // eslint-disable-line
expect(loadJsonFile<any>('project-1/package.json').dependencies['@pnpm.e2e/bar']).toBe('100.0.0') // eslint-disable-line
expect(loadJsonFile<any>('project-2/package.json').dependencies['@pnpm.e2e/foo']).toBe('2.0.0') // eslint-disable-line

// similar for the importers in the lockfile; project 1 is unaffected, while
// project 2 resolves the latest foo
const lockfile = readYamlFile<any>(path.resolve(WANTED_LOCKFILE)) // eslint-disable-line
expect(lockfile.importers['project-1']?.dependencies?.['@pnpm.e2e/foo'].version).toStrictEqual('1.0.0')
expect(lockfile.importers['project-1']?.dependencies?.['@pnpm.e2e/bar'].version).toStrictEqual('100.0.0')
expect(lockfile.importers['project-2']?.dependencies?.['@pnpm.e2e/foo'].version).toStrictEqual('2.0.0')
})

// Covers https://github.com/pnpm/pnpm/issues/6154
test('peer dependents deduplication should not remove peer dependencies', async () => {
await addDistTag({ package: '@pnpm.e2e/peer-a', version: '1.0.0', distTag: 'latest' })
Expand Down

0 comments on commit c7eefdd

Please sign in to comment.