fix(update): stop downgrading prereleases (#7466)

close #7436

---------

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Khải
2024-01-27 20:41:58 +07:00
committed by GitHub
parent 10a0896551
commit 0ec0a44da7
37 changed files with 341 additions and 504 deletions

View File

@@ -0,0 +1,12 @@
---
"@pnpm/plugin-commands-installation": minor
"@pnpm/resolve-dependencies": minor
"@pnpm/package-requester": patch
"@pnpm/store-controller-types": minor
"@pnpm/resolver-base": minor
"@pnpm/npm-resolver": minor
"@pnpm/core": minor
"pnpm": patch
---
Running `pnpm update -r --latest` will no longer downgrade prerelease dependencies [#7436](https://github.com/pnpm/pnpm/issues/7436).

View File

@@ -44,7 +44,7 @@
"@pnpm/constants": "workspace:*",
"@pnpm/lockfile-types": "workspace:*",
"@pnpm/modules-yaml": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/types": "workspace:*",
"is-windows": "^1.0.2",
"isexe": "2.0.0",

View File

@@ -40,7 +40,7 @@
"test": "pnpm pretest && pnpm run compile && jest"
},
"dependencies": {
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/store.cafs": "workspace:*",
"path-exists": "^4.0.0"
},

View File

@@ -35,7 +35,7 @@
"@pnpm/filter-workspace-packages": "workspace:*",
"@pnpm/plugin-commands-rebuild": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@pnpm/test-ipc-server": "workspace:*",
"@types/ramda": "0.28.20",

View File

@@ -34,7 +34,7 @@
"@pnpm/filter-workspace-packages": "workspace:*",
"@pnpm/plugin-commands-script-runners": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-ipc-server": "workspace:*",
"@types/is-windows": "^1.0.2",
"@types/ramda": "0.28.20",

View File

@@ -39,7 +39,7 @@
"@commitlint/prompt-cli": "^17.8.1",
"@pnpm/eslint-config": "workspace:*",
"@pnpm/meta-updater": "1.0.0",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/tsconfig": "workspace:*",
"@pnpm/worker": "workspace:*",
"@types/jest": "^29.5.8",

View File

@@ -34,7 +34,7 @@
"@pnpm/filter-workspace-packages": "workspace:*",
"@pnpm/plugin-commands-patching": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/normalize-path": "^3.0.2",
"@types/ramda": "0.28.20",

View File

@@ -82,7 +82,7 @@
"@pnpm/lockfile-types": "workspace:*",
"@pnpm/package-store": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/store-path": "workspace:*",
"@pnpm/store.cafs": "workspace:*",
"@pnpm/test-fixtures": "workspace:*",

View File

@@ -91,6 +91,7 @@ export interface StrictInstallOptions {
unsafePerm: boolean
registries: Registries
tag: string
updateToLatest?: boolean
overrides: Record<string, string>
ownLifecycleHooksStdio: 'inherit' | 'pipe'
workspacePackages: WorkspacePackages

View File

@@ -1032,6 +1032,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
storeController: opts.storeController,
tag: opts.tag,
updateToLatest: opts.updateToLatest,
virtualStoreDir: ctx.virtualStoreDir,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages,

View File

@@ -23,7 +23,7 @@
"@pnpm/package-store": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/read-projects-context": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/store-path": "workspace:*",
"@pnpm/store.cafs": "workspace:*",
"@pnpm/test-fixtures": "workspace:*",

View File

@@ -61,7 +61,7 @@
"@pnpm/client": "workspace:*",
"@pnpm/create-cafs-store": "workspace:*",
"@pnpm/package-requester": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/normalize-path": "^3.0.2",
"@types/ramda": "0.28.20",

View File

@@ -184,6 +184,7 @@ async function resolveAndFetch (
projectDir: options.projectDir,
registry: options.registry,
workspacePackages: options.workspacePackages,
updateToLatest: options.updateToLatest,
}), { priority: options.downloadPriority })
manifest = resolveResult.manifest

View File

@@ -34,7 +34,7 @@
"@pnpm/modules-yaml": "workspace:*",
"@pnpm/plugin-commands-installation": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@pnpm/test-ipc-server": "workspace:*",
"@types/proxyquire": "^1.3.31",

View File

@@ -6,6 +6,7 @@ import {
import { type Config, getOptionsFromRootManifest } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
import { filterDependenciesByType } from '@pnpm/manifest-utils'
import { arrayOfWorkspacePackagesToMap, findWorkspacePackages } from '@pnpm/workspace.find-packages'
import { type Lockfile } from '@pnpm/lockfile-types'
import { rebuildProjects } from '@pnpm/plugin-commands-rebuild'
@@ -25,7 +26,6 @@ import { getPinnedVersion } from './getPinnedVersion'
import { getSaveType } from './getSaveType'
import { getNodeExecPath } from './nodeExecPath'
import { recursive, createMatcher, matchDependencies, makeIgnorePatterns, type UpdateDepsMatcher } from './recursive'
import { updateToLatestSpecsFromManifest, createLatestSpecs } from './updateToLatestSpecsFromManifest'
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
const OVERWRITE_UPDATE_OPTIONS = {
@@ -105,6 +105,7 @@ export type InstallDepsOptions = Pick<Config,
*/
lockfileCheck?: (prev: Lockfile, next: Lockfile) => void
update?: boolean
updateToLatest?: boolean
updateMatching?: (pkgName: string) => boolean
updatePackageManifest?: boolean
useBetaCli?: boolean
@@ -265,12 +266,8 @@ when running add/update with the --workspace option')
}
}
if (opts.update && opts.latest) {
if (!params || (params.length === 0)) {
params = updateToLatestSpecsFromManifest(manifest, includeDirect)
} else {
params = createLatestSpecs(params, manifest)
}
if (opts.update && opts.latest && (!params || (params.length === 0))) {
params = Object.keys(filterDependenciesByType(manifest, includeDirect))
}
if (opts.workspace) {
if (!params || (params.length === 0)) {

View File

@@ -36,7 +36,6 @@ import mem from 'mem'
import pFilter from 'p-filter'
import pLimit from 'p-limit'
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
import { updateToLatestSpecsFromManifest, createLatestSpecs } from './updateToLatestSpecsFromManifest'
import { getSaveType } from './getSaveType'
import { getPinnedVersion } from './getPinnedVersion'
import { type PreferredVersions } from '@pnpm/resolver-base'
@@ -197,16 +196,8 @@ export async function recursive (
return
}
}
if (updateToLatest) {
if (!params || (params.length === 0)) {
currentInput = updateToLatestSpecsFromManifest(manifest, includeDirect)
} else {
currentInput = createLatestSpecs(currentInput, manifest)
if (currentInput.length === 0) {
installOpts.pruneLockfileImporters = false
return
}
}
if (updateToLatest && (!params || (params.length === 0))) {
currentInput = Object.keys(filterDependenciesByType(manifest, includeDirect))
}
if (opts.workspace) {
if (!currentInput || (currentInput.length === 0)) {
@@ -319,13 +310,8 @@ export async function recursive (
currentInput = matchDependencies(updateMatch, manifest, includeDirect)
if (currentInput.length === 0) return
}
if (updateToLatest) {
if (!params || (params.length === 0)) {
currentInput = updateToLatestSpecsFromManifest(manifest, includeDirect)
} else {
currentInput = createLatestSpecs(currentInput, manifest)
if (currentInput.length === 0) return
}
if (updateToLatest && (!params || (params.length === 0))) {
currentInput = Object.keys(filterDependenciesByType(manifest, includeDirect))
}
if (opts.workspace) {
if (!currentInput || (currentInput.length === 0)) {

View File

@@ -290,6 +290,7 @@ async function update (
includeDirect,
include,
update: true,
updateToLatest: opts.latest,
updateMatching: (dependencies.length > 0) && dependencies.every(dep => !dep.substring(1).includes('@')) && depth > 0 && !opts.latest
? createMatcher(dependencies)
: undefined,

View File

@@ -1,43 +0,0 @@
import { filterDependenciesByType, getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
import { type IncludedDependencies, type ProjectManifest } from '@pnpm/types'
import getVerSelType from 'version-selector-type'
export function updateToLatestSpecsFromManifest (manifest: ProjectManifest, include: IncludedDependencies) {
const allDeps = filterDependenciesByType(manifest, include)
const updateSpecs = []
for (const [depName, depVersion] of Object.entries(allDeps)) {
if (depVersion.startsWith('npm:')) {
updateSpecs.push(`${depName}@${removeVersionFromSpec(depVersion)}@latest`)
} else {
const selector = getVerSelType(depVersion)
if (selector == null) continue
updateSpecs.push(`${depName}@latest`)
}
}
return updateSpecs
}
export function createLatestSpecs (specs: string[], manifest: ProjectManifest) {
const allDeps = getAllDependenciesFromManifest(manifest)
return specs
.filter((selector) => selector.includes('@', 1)
? allDeps[selector.slice(0, selector.indexOf('@', 1))]
: allDeps[selector]
)
.map((selector) => {
if (selector.includes('@', 1)) {
return selector
}
if (allDeps[selector].startsWith('npm:')) {
return `${selector}@${removeVersionFromSpec(allDeps[selector])}@latest`
}
if (getVerSelType(allDeps[selector]) == null) {
return selector
}
return `${selector}@latest`
})
}
function removeVersionFromSpec (spec: string) {
return spec.substring(0, spec.lastIndexOf('@'))
}

View File

@@ -1,42 +0,0 @@
import { updateToLatestSpecsFromManifest, createLatestSpecs } from '../lib/updateToLatestSpecsFromManifest'
const MANIFEST = {
dependencies: {
'@pnpm.e2e/dep-of-pkg-with-1-dep': 'npm:@pnpm.e2e/pkg-with-1-dep@1.0.0',
},
devDependencies: {
foo: 'github:pnpm/foo',
},
optionalDependencies: {
'is-positive': '^1.0.0',
},
}
test('updateToLatestSpecsFromManifest()', () => {
const updateResult1 = expect(updateToLatestSpecsFromManifest(MANIFEST, {
optionalDependencies: true,
dependencies: true,
devDependencies: true,
}))
updateResult1.toHaveLength(2)
updateResult1.toContain('@pnpm.e2e/dep-of-pkg-with-1-dep@npm:@pnpm.e2e/pkg-with-1-dep@latest')
updateResult1.toContain('is-positive@latest')
const updateResult2 = expect(updateToLatestSpecsFromManifest(MANIFEST, {
optionalDependencies: false,
dependencies: true,
devDependencies: false,
}))
updateResult2.toHaveLength(1)
updateResult2.toStrictEqual(['@pnpm.e2e/dep-of-pkg-with-1-dep@npm:@pnpm.e2e/pkg-with-1-dep@latest'])
})
test('createLatestSpecs()', () => {
expect(
createLatestSpecs(['@pnpm.e2e/dep-of-pkg-with-1-dep', 'is-positive@2.0.0', 'foo'], MANIFEST)
).toStrictEqual(['@pnpm.e2e/dep-of-pkg-with-1-dep@npm:@pnpm.e2e/pkg-with-1-dep@latest', 'is-positive@2.0.0', 'foo'])
expect(
createLatestSpecs(['@pnpm.e2e/dep-of-pkg-with-1-dep', 'is-positive', 'foo', 'bar'], MANIFEST)
).toStrictEqual(['@pnpm.e2e/dep-of-pkg-with-1-dep@npm:@pnpm.e2e/pkg-with-1-dep@latest', 'is-positive@latest', 'foo'])
})

View File

@@ -17,6 +17,7 @@ export interface WantedDependency {
raw: string
pinnedVersion?: PinnedVersion
nodeExecPath?: string
updateSpec?: boolean
}
export function getWantedDependencies (

View File

@@ -109,6 +109,8 @@ export async function resolveDependencies (
preferredVersions: opts.preferredVersions,
virtualStoreDir: opts.virtualStoreDir,
workspacePackages: opts.workspacePackages,
updateToLatest: opts.updateToLatest,
noDependencySelectors: importers.every(({ wantedDependencies }) => wantedDependencies.length === 0),
})
const projectsToResolve = await Promise.all(importers.map(async (project) => _toResolveImporter(project)))
const {

View File

@@ -255,6 +255,7 @@ interface ResolvedDependenciesOptions {
updateDepth: number
prefix: string
supportedArchitectures?: SupportedArchitectures
updateToLatest?: boolean
}
interface PostponedResolutionOpts {
@@ -677,6 +678,7 @@ async function resolveDependenciesOfDependency (
updateDepth,
updateMatching: options.updateMatching,
supportedArchitectures: options.supportedArchitectures,
updateToLatest: options.updateToLatest,
}
const resolveDependencyResult = await resolveDependency(extendedWantedDep.wantedDependency, ctx, resolveDependencyOpts)
@@ -713,6 +715,7 @@ async function resolveDependenciesOfDependency (
prefix: options.prefix,
updateMatching: options.updateMatching,
supportedArchitectures: options.supportedArchitectures,
updateToLatest: options.updateToLatest,
})
return {
resolveDependencyResult,
@@ -769,6 +772,7 @@ async function resolveChildren (
prefix: string
updateMatching?: UpdateMatchingFunction
supportedArchitectures?: SupportedArchitectures
updateToLatest?: boolean
},
{
parentPkgAliases,
@@ -1019,6 +1023,7 @@ interface ResolveDependencyOptions {
updateDepth: number
updateMatching?: UpdateMatchingFunction
supportedArchitectures?: SupportedArchitectures
updateToLatest?: boolean
}
type ResolveDependencyResult = PkgAddress | LinkedDependency | null
@@ -1098,6 +1103,7 @@ async function resolveDependency (
err.pkgsStack = nodeIdToParents(options.parentPkg.nodeId, ctx.resolvedPackagesByDepPath)
return err
},
updateToLatest: options.updateToLatest,
})
} catch (err: any) { // eslint-disable-line
if (wantedDependency.optional) {

View File

@@ -99,6 +99,7 @@ export interface ResolveDependenciesOptions {
wantedLockfile: Lockfile
workspacePackages: WorkspacePackages
supportedArchitectures?: SupportedArchitectures
updateToLatest?: boolean
}
export async function resolveDependencyTree<T> (
@@ -166,6 +167,7 @@ export async function resolveDependencyTree<T> (
updateMatching: importer.updateMatching,
prefix: importer.rootDir,
supportedArchitectures: opts.supportedArchitectures,
updateToLatest: opts.updateToLatest,
}
return {
updatePackageManifest: importer.updatePackageManifest,

View File

@@ -17,6 +17,8 @@ export async function toResolveImporter (
preferredVersions?: PreferredVersions
virtualStoreDir: string
workspacePackages: WorkspacePackages
updateToLatest?: boolean
noDependencySelectors: boolean
},
project: ImporterToResolve
) {
@@ -31,6 +33,11 @@ export async function toResolveImporter (
const defaultUpdateDepth = (project.update === true || (project.updateMatching != null)) ? opts.defaultUpdateDepth : -1
const existingDeps = nonLinkedDependencies
.filter(({ alias }) => !project.wantedDependencies.some((wantedDep) => wantedDep.alias === alias))
if (opts.updateToLatest && opts.noDependencySelectors) {
for (const dep of existingDeps) {
dep.updateSpec = true
}
}
let wantedDependencies!: Array<WantedDependency & { isNew?: boolean, updateDepth: number }>
if (!project.manifest) {
wantedDependencies = [
@@ -48,14 +55,18 @@ export async function toResolveImporter (
...dep,
updateDepth: project.updateMatching != null
? defaultUpdateDepth
: (prefIsLocalTarball(dep.pref) ? 0 : -1),
: (prefIsLocalTarball(dep.pref) ? 0 : defaultUpdateDepth),
})
wantedDependencies = [
...project.wantedDependencies.map(
defaultUpdateDepth < 0
? updateLocalTarballs
: (dep) => ({ ...dep, updateDepth: defaultUpdateDepth })),
...existingDeps.map(updateLocalTarballs),
...existingDeps.map(
opts.noDependencySelectors && project.updateMatching != null
? updateLocalTarballs
: (dep) => ({ ...dep, updateDepth: -1 })
),
]
}
return {

408
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -63,7 +63,7 @@
"@pnpm/prepare": "workspace:*",
"@pnpm/read-package-json": "workspace:*",
"@pnpm/read-project-manifest": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/run-npm": "workspace:*",
"@pnpm/tabtab": "^0.1.2",
"@pnpm/test-fixtures": "workspace:*",

View File

@@ -201,9 +201,9 @@ test('update --latest specific dependency', async function () {
addDistTag('@pnpm.e2e/qar', '100.1.0', 'latest'),
])
await execPnpm(['add', '@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', '@pnpm.e2e/bar@^100.0.0', '@pnpm.e2e/foo@100.1.0', 'alias@npm:@pnpm.e2e/qar@^100.0.0', 'kevva/is-negative'])
await execPnpm(['add', '@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', '@pnpm.e2e/bar@^100.0.0', '@pnpm.e2e/foo@100.0.0', 'alias@npm:@pnpm.e2e/qar@^100.0.0', 'kevva/is-negative'])
await execPnpm(['update', '-L', '@pnpm.e2e/bar', '@pnpm.e2e/foo@100.0.0', 'alias', 'is-negative'])
await execPnpm(['update', '-L', '@pnpm.e2e/bar', 'alias', 'is-negative'])
const lockfile = await project.readLockfile()
expect(lockfile.dependencies['@pnpm.e2e/dep-of-pkg-with-1-dep'].version).toBe('100.0.0')
@@ -378,7 +378,7 @@ test('recursive update --latest specific dependency on projects that do not shar
dependencies: {
alias: 'npm:@pnpm.e2e/qar@100.0.0',
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
'@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0',
'@pnpm.e2e/foo': '^100.0.0',
},
},
@@ -395,7 +395,7 @@ test('recursive update --latest specific dependency on projects that do not shar
await execPnpm(['-r', 'install'])
await execPnpm(['-r', 'update', '--latest', '@pnpm.e2e/foo', '@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', 'alias'])
await execPnpm(['-r', 'update', '--latest', '@pnpm.e2e/foo', 'alias'])
const manifest1 = await readPackageJsonFromDir(path.resolve('project-1'))
expect(manifest1.dependencies).toStrictEqual({
@@ -552,7 +552,7 @@ test('recursive update --latest specific dependency on projects with a shared a
dependencies: {
alias: 'npm:@pnpm.e2e/qar@100.0.0',
'@pnpm.e2e/dep-of-pkg-with-1-dep': '101.0.0',
'@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0',
'@pnpm.e2e/foo': '100.0.0',
},
},
@@ -570,7 +570,7 @@ test('recursive update --latest specific dependency on projects with a shared a
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
await execPnpm(['recursive', 'install'])
await execPnpm(['recursive', 'update', '--latest', '@pnpm.e2e/foo', '@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0', 'alias'])
await execPnpm(['recursive', 'update', '--latest', '@pnpm.e2e/foo', 'alias'])
const manifest1 = await readPackageJsonFromDir(path.resolve('project-1'))
expect(manifest1.dependencies).toStrictEqual({
@@ -608,3 +608,210 @@ test('deep update', async function () {
await project.storeHas('@pnpm.e2e/dep-of-pkg-with-1-dep', '100.1.0')
})
test('update to latest without downgrading already defined prerelease (#7436)', async function () {
prepare()
await addDistTag('@pnpm.e2e/has-prerelease', '2.0.0', 'latest')
await execPnpm(['add', '@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
const manifest1 = await readPackageJsonFromDir('.')
expect(manifest1).toMatchObject({
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
})
const lockfile1 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
expect(lockfile1).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
await execPnpm(['update', '--latest'])
const manifest2 = await readPackageJsonFromDir('.')
expect(manifest2).toMatchObject({
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
})
const lockfile2 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
expect(lockfile2).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
await execPnpm(['update', '-r', '--latest'])
const manifest3 = await readPackageJsonFromDir('.')
expect(manifest3).toMatchObject({
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
})
const lockfile3 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile3).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
expect(lockfile3).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
})
test('update with tag @latest will downgrade prerelease', async function () {
prepare()
await addDistTag('@pnpm.e2e/has-prerelease', '2.0.0', 'latest')
await execPnpm(['add', '@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
const manifest1 = await readPackageJsonFromDir('.')
expect(manifest1).toMatchObject({
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
})
const lockfile1 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
expect(lockfile1).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
await execPnpm(['update', '@pnpm.e2e/has-prerelease@latest'])
const manifest2 = await readPackageJsonFromDir('.')
expect(manifest2).toMatchObject({
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
})
const lockfile2 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile2).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
})
test('update to latest recursive workspace (outdated, updated, prerelease, outdated)', async function () {
await addDistTag('@pnpm.e2e/has-prerelease', '2.0.0', 'latest')
preparePackages([
{
name: 'project-1',
dependencies: {
'@pnpm.e2e/has-prerelease': '1.0.0',
},
},
{
name: 'project-2',
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
},
{
name: 'project-3',
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
},
{
name: 'project-4',
dependencies: {
'@pnpm.e2e/has-prerelease': '1.0.0',
},
},
])
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
await execPnpm(['install', '-r'])
const lockfile1 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@1.0.0'])
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
await execPnpm(['update', '-r', '--latest'])
const manifests = {
'project-1': await readPackageJsonFromDir('project-1'),
'project-2': await readPackageJsonFromDir('project-2'),
'project-3': await readPackageJsonFromDir('project-3'),
'project-4': await readPackageJsonFromDir('project-4'),
}
expect(manifests).toMatchObject({
'project-1': {
name: 'project-1',
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
},
'project-2': {
name: 'project-2',
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
},
'project-3': {
name: 'project-3',
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
},
'project-4': {
name: 'project-4',
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
},
})
const lockfile2 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile2).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@1.0.0'])
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
})
test('update to latest recursive workspace (prerelease, outdated)', async function () {
await addDistTag('@pnpm.e2e/has-prerelease', '2.0.0', 'latest')
preparePackages([
{
name: 'project-1',
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
},
{
name: 'project-2',
dependencies: {
'@pnpm.e2e/has-prerelease': '1.0.0',
},
},
])
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
await execPnpm(['install', '-r'])
const lockfile1 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@1.0.0'])
expect(lockfile1).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
expect(lockfile1).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
await execPnpm(['update', '-r', '--latest'])
const manifests = {
'project-1': await readPackageJsonFromDir('project-1'),
'project-2': await readPackageJsonFromDir('project-2'),
}
expect(manifests).toMatchObject({
'project-1': {
name: 'project-1',
dependencies: {
'@pnpm.e2e/has-prerelease': '3.0.0-rc.0',
},
},
'project-2': {
name: 'project-2',
dependencies: {
'@pnpm.e2e/has-prerelease': '2.0.0',
},
},
})
const lockfile2 = await readYamlFile('pnpm-lock.yaml')
expect(lockfile2).not.toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@1.0.0'])
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@2.0.0'])
expect(lockfile2).toHaveProperty(['packages', '/@pnpm.e2e/has-prerelease@3.0.0-rc.0'])
})

View File

@@ -39,7 +39,7 @@
"@pnpm/lockfile-types": "workspace:*",
"@pnpm/plugin-commands-deploy": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1"
"@pnpm/registry-mock": "3.18.0"
},
"dependencies": {
"@pnpm/cli-utils": "workspace:*",

View File

@@ -35,7 +35,7 @@
"@pnpm/filter-workspace-packages": "workspace:*",
"@pnpm/plugin-commands-publishing": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-ipc-server": "workspace:*",
"@types/cross-spawn": "^6.0.5",
"@types/is-windows": "^1.0.2",

View File

@@ -114,6 +114,7 @@ export type ResolveFromNpmOptions = {
registry: string
preferredVersions?: PreferredVersions
preferWorkspacePackages?: boolean
updateToLatest?: boolean
} & ({
projectDir?: string
workspacePackages?: undefined
@@ -160,6 +161,7 @@ async function resolveNpm (
dryRun: opts.dryRun === true,
preferredVersionSelectors: opts.preferredVersions?.[spec.name],
registry: opts.registry,
updateToLatest: opts.updateToLatest,
})
} catch (err: any) { // eslint-disable-line
if ((workspacePackages != null) && opts.projectDir) {

View File

@@ -11,6 +11,7 @@ import loadJsonFile from 'load-json-file'
import pLimit from 'p-limit'
import { fastPathTemp as pathTemp } from 'path-temp'
import pick from 'ramda/src/pick'
import semver from 'semver'
import renameOverwrite from 'rename-overwrite'
import { toRaw } from './toRaw'
import { pickPackageFromMeta, pickVersionByVersionRange, pickLowestVersionByVersionRange } from './pickPackageFromMeta'
@@ -85,6 +86,7 @@ export interface PickPackageOptions {
pickLowestVersion?: boolean
registry: string
dryRun: boolean
updateToLatest?: boolean
}
function pickPackageFromMetaUsingTime (
@@ -112,11 +114,25 @@ export async function pickPackage (
opts: PickPackageOptions
): Promise<{ meta: PackageMeta, pickedPackage: PackageInRegistry | null }> {
opts = opts || {}
const _pickPackageFromMeta =
let _pickPackageFromMeta =
opts.publishedBy
? pickPackageFromMetaUsingTime
: (pickPackageFromMeta.bind(null, opts.pickLowestVersion ? pickLowestVersionByVersionRange : pickVersionByVersionRange))
if (opts.updateToLatest) {
const _pickPackageBase = _pickPackageFromMeta
_pickPackageFromMeta = (spec, ...rest) => {
const latestStableSpec: RegistryPackageSpec = { ...spec, type: 'tag', fetchSpec: 'latest' }
const latestStable = _pickPackageBase(latestStableSpec, ...rest)
const current = _pickPackageBase(spec, ...rest)
if (!latestStable) return current
if (!current) return latestStable
if (semver.lt(latestStable.version, current.version)) return current
return latestStable
}
}
validatePackageName(spec.name)
const cachedMeta = ctx.metaCache.get(spec.name)
@@ -155,7 +171,7 @@ export async function pickPackage (
}
}
if (spec.type === 'version') {
if (!opts.updateToLatest && spec.type === 'version') {
metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror))
// use the cached meta only if it has the required package version
// otherwise it is probably out of date

View File

@@ -80,6 +80,7 @@ export interface ResolveOptions {
preferWorkspacePackages?: boolean
registry: string
workspacePackages?: WorkspacePackages
updateToLatest?: boolean
}
export type WantedDependency = {

View File

@@ -36,7 +36,7 @@
"@pnpm/plugin-commands-licenses": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/read-package-json": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.28.20",
"@types/semver": "7.5.3",

View File

@@ -34,7 +34,7 @@
"@pnpm/plugin-commands-installation": "workspace:*",
"@pnpm/plugin-commands-listing": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@types/ramda": "0.28.20",
"execa": "npm:safe-execa@0.1.2",
"strip-ansi": "^6.0.1",

View File

@@ -35,7 +35,7 @@
"@pnpm/plugin-commands-installation": "workspace:*",
"@pnpm/plugin-commands-outdated": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.28.20",
"@types/wrap-ansi": "8.0.2",

View File

@@ -34,7 +34,7 @@
"@pnpm/lockfile-file": "workspace:*",
"@pnpm/plugin-commands-store": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.17.1",
"@pnpm/registry-mock": "3.18.0",
"@types/archy": "0.0.33",
"@types/ramda": "0.28.20",
"@types/ssri": "^7.1.4",

View File

@@ -134,6 +134,7 @@ export interface RequestPackageOptions {
forceResolve?: boolean
supportedArchitectures?: SupportedArchitectures
onFetchError?: OnFetchError
updateToLatest?: boolean
}
export type BundledManifestFunction = () => Promise<BundledManifest | undefined>