feat: update a specified dependency in subdeps

ref #2222
PR #2692
This commit is contained in:
Zoltan Kochan
2020-07-26 04:58:02 +03:00
committed by GitHub
parent e154729b10
commit 8c1cf25b7d
12 changed files with 90 additions and 17 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": minor
---
Allow to update specific packages up until a specified depth. For instance, `pnpm update @types/* --depth Infinity`.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/resolve-dependencies": minor
"supi": minor
---
New option added: updateMatching. updateMatching is a function that accepts a package name. It returns `true` if the specified package should be updated.

View File

@@ -243,6 +243,7 @@ export type InstallCommandOptions = Pick<Config,
| 'bail'
| 'bin'
| 'cliOptions'
| 'depth'
| 'dev'
| 'engineStrict'
| 'global'

View File

@@ -31,6 +31,7 @@ export type InstallDepsOptions = Pick<Config,
| 'bail'
| 'bin'
| 'cliOptions'
| 'depth'
| 'dev'
| 'engineStrict'
| 'global'
@@ -71,6 +72,7 @@ export type InstallDepsOptions = Pick<Config,
includeDirect?: IncludedDependencies,
latest?: boolean,
update?: boolean,
updateMatching?: (pkgName: string) => boolean,
updatePackageManifest?: boolean,
useBetaCli?: boolean,
recursive?: boolean,

View File

@@ -42,6 +42,7 @@ import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './u
type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
| 'bail'
| 'depth'
| 'globalPnpmfile'
| 'hoistPattern'
| 'ignorePnpmfile'
@@ -178,7 +179,7 @@ export default async function recursive (
let currentInput = [...params]
if (updateMatch) {
currentInput = matchDependencies(updateMatch, manifest, includeDirect)
if (!currentInput.length) {
if (!currentInput.length && (typeof opts.depth === 'undefined' || opts.depth <= 0)) {
installOpts.pruneLockfileImporters = false
return
}

View File

@@ -6,6 +6,7 @@ import {
import { CompletionFunc } from '@pnpm/command'
import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { types as allTypes } from '@pnpm/config'
import matcher from '@pnpm/matcher'
import { outdatedDepsOfProjects } from '@pnpm/outdated'
import chalk = require('chalk')
import { prompt } from 'enquirer'
@@ -234,6 +235,8 @@ async function update (
allowNew: false,
includeDirect,
update: true,
updateMatching: dependencies.every(dep => !dep.substring(1).includes('@')) && opts.depth && opts.depth > 0 && !opts.latest
? matcher(dependencies) : undefined,
updatePackageManifest: opts.save !== false,
}, dependencies)
}

View File

@@ -58,6 +58,7 @@ export default async function (
nodeVersion: string,
registries: Registries,
pnpmVersion: string,
updateMatching?: (pkgName: string) => boolean,
linkWorkspacePackagesDepth?: number,
lockfileDir: string,
storeController: StoreController,
@@ -91,6 +92,7 @@ export default async function (
resolvedPackagesByPackageId: {} as ResolvedPackagesByPackageId,
skipped: wantedToBeSkippedPackageIds,
storeController: opts.storeController,
updateMatching: opts.updateMatching,
virtualStoreDir: opts.virtualStoreDir,
wantedLockfile: opts.wantedLockfile,
}

View File

@@ -144,6 +144,7 @@ export interface ResolutionContext {
pnpmVersion: string,
registries: Registries,
virtualStoreDir: string,
updateMatching?: (pkgName: string) => boolean,
}
export type PkgAddress = {
@@ -240,11 +241,17 @@ export default async function resolveDependencies (
.map(async (extendedWantedDep) => {
const updateDepth = typeof extendedWantedDep.wantedDependency.updateDepth === 'number'
? extendedWantedDep.wantedDependency.updateDepth : options.updateDepth
const updateShouldContinue = options.currentDepth <= updateDepth
const update = updateShouldContinue && (
!ctx.updateMatching ||
!extendedWantedDep.infoFromLockfile?.dependencyLockfile ||
ctx.updateMatching(extendedWantedDep.infoFromLockfile.dependencyLockfile.name ?? extendedWantedDep.wantedDependency.alias)
)
const resolveDependencyOpts: ResolveDependencyOptions = {
...resolveDepOpts,
currentPkg: extendedWantedDep.infoFromLockfile ?? undefined,
proceed: extendedWantedDep.proceed,
update: options.currentDepth <= updateDepth,
proceed: extendedWantedDep.proceed || updateShouldContinue,
update,
updateDepth,
}
const resolveDependencyResult = await resolveDependency(extendedWantedDep.wantedDependency, ctx, resolveDependencyOpts)
@@ -430,13 +437,13 @@ function getInfoFromLockfile (
lockfile: Lockfile,
registries: Registries,
reference: string | undefined,
pkgName: string | undefined
alias: string | undefined
) {
if (!reference || !pkgName) {
if (!reference || !alias) {
return null
}
const depPath = dp.refToRelative(reference, pkgName)
const depPath = dp.refToRelative(reference, alias)
if (!depPath) {
return null
@@ -602,6 +609,10 @@ async function resolveDependency (
let useManifestInfoFromLockfile = false
let prepare!: boolean
let hasBin!: boolean
pkg = ctx.readPackageHook
? ctx.readPackageHook(pkgResponse.body.manifest || await pkgResponse.bundledManifest!())
: pkgResponse.body.manifest || await pkgResponse.bundledManifest!()
if (
!options.update && currentPkg.dependencyLockfile && currentPkg.depPath &&
!pkgResponse.body.updated &&
@@ -613,16 +624,13 @@ async function resolveDependency (
useManifestInfoFromLockfile = true
prepare = currentPkg.dependencyLockfile.prepare === true
hasBin = currentPkg.dependencyLockfile.hasBin === true
pkg = Object.assign(
nameVerFromPkgSnapshot(currentPkg.depPath, currentPkg.dependencyLockfile),
currentPkg.dependencyLockfile
)
pkg = {
...nameVerFromPkgSnapshot(currentPkg.depPath, currentPkg.dependencyLockfile),
...currentPkg.dependencyLockfile,
...pkg,
}
} else {
// tslint:disable:no-string-literal
pkg = ctx.readPackageHook
? ctx.readPackageHook(pkgResponse.body.manifest || await pkgResponse.bundledManifest!())
: pkgResponse.body.manifest || await pkgResponse.bundledManifest!()
prepare = Boolean(
pkgResponse.body.resolvedVia === 'git-repository' &&
typeof pkg.scripts?.prepare === 'string'

View File

@@ -29,6 +29,7 @@ export interface StrictInstallOptions {
reporter: ReporterFunction,
force: boolean,
update: boolean,
updateMatching?: (pkgName: string) => boolean,
updatePackageManifest: boolean,
depth: number,
lockfileDir: string,

View File

@@ -597,10 +597,11 @@ async function installInContext (
|| !opts.currentLockfileIsUpToDate
|| opts.force
const _toResolveImporter = toResolveImporter.bind(null, {
defaultUpdateDepth: opts.update ? opts.depth : -1,
defaultUpdateDepth: (opts.update || opts.updateMatching) ? opts.depth : -1,
lockfileOnly: opts.lockfileOnly,
preferredVersions,
storeDir: ctx.storeDir,
updateAll: Boolean(opts.updateMatching),
virtualStoreDir: ctx.virtualStoreDir,
workspacePackages: opts.workspacePackages,
})
@@ -627,6 +628,7 @@ async function installInContext (
registries: opts.registries,
storeController: opts.storeController,
tag: opts.tag,
updateMatching: opts.updateMatching,
virtualStoreDir: ctx.virtualStoreDir,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages,
@@ -839,6 +841,7 @@ async function toResolveImporter (
lockfileOnly: boolean,
preferredVersions?: PreferredVersions,
storeDir: string,
updateAll: boolean,
virtualStoreDir: string,
workspacePackages: WorkspacePackages,
},
@@ -870,7 +873,8 @@ async function toResolveImporter (
// so their update depth should be at least 0
const updateLocalTarballs = (dep: WantedDependency) => ({
...dep,
updateDepth: prefIsLocalTarball(dep.pref) ? 0 : -1,
updateDepth: opts.updateAll ?
opts.defaultUpdateDepth : (prefIsLocalTarball(dep.pref) ? 0 : -1),
})
wantedDependencies = [
...project.wantedDependencies.map(

View File

@@ -990,7 +990,7 @@ test('all the subdeps of dependencies are linked when a node_modules is partiall
'bar',
'foo',
'foobarqar',
'is-positive',
'qar',
]
)
})

View File

@@ -180,3 +180,43 @@ test('update only the packages that were requested to be updated when hoisting i
const lockfile = await project.readLockfile()
t.deepEqual(Object.keys(lockfile.packages), ['/bar/100.0.0', '/foo/100.1.0'])
})
test('update only the specified package', async (t: tape.Test) => {
const project = prepareEmpty(t)
await Promise.all([
addDistTag('abc-grand-parent-with-c', '1.0.0', 'latest'),
addDistTag('abc-parent-with-ab', '1.0.0', 'latest'),
addDistTag('bar', '100.0.0', 'latest'),
addDistTag('foo', '100.0.0', 'latest'),
addDistTag('foobarqar', '1.0.0', 'latest'),
addDistTag('peer-c', '1.0.0', 'latest'),
])
const manifest = await addDependenciesToPackage({}, ['foobarqar', 'abc-grand-parent-with-c'], await testDefaults())
await Promise.all([
addDistTag('abc-grand-parent-with-c', '1.0.1', 'latest'),
addDistTag('abc-parent-with-ab', '1.0.1', 'latest'),
addDistTag('bar', '100.1.0', 'latest'),
addDistTag('foo', '100.1.0', 'latest'),
addDistTag('foobarqar', '1.0.1', 'latest'),
])
await install(manifest, await testDefaults({
depth: Infinity,
update: true,
updateMatching: (pkgName: string) => pkgName === 'foo',
}))
const lockfile = await project.readLockfile()
t.ok(lockfile.packages)
t.ok(lockfile.packages['/abc-parent-with-ab/1.0.0_peer-c@1.0.0'], 'preserve version of package that has resolved peer deps')
t.ok(lockfile.packages['/foobarqar/1.0.0'])
t.deepEqual(lockfile.packages['/foobarqar/1.0.0'].dependencies, {
bar: '100.0.0',
foo: '100.1.0',
'is-positive': '3.1.0',
})
})