mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-24 15:48:06 -05:00
feat(cli): forbid combining specs and --latest (#7567)
--------- Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
6
.changeset/strange-paws-drive.md
Normal file
6
.changeset/strange-paws-drive.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-installation": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
Throw an error if `pnpm update --latest` runs with arguments containing versions specs. For instance, `pnpm update --latest foo@next` is not allowed [#7567](https://github.com/pnpm/pnpm/pull/7567).
|
||||
@@ -480,14 +480,9 @@ export function createMatcher (params: string[]): UpdateDepsMatcher {
|
||||
const patterns: string[] = []
|
||||
const specs: string[] = []
|
||||
for (const param of params) {
|
||||
const atIndex = param.indexOf('@', param[0] === '!' ? 2 : 1)
|
||||
if (atIndex === -1) {
|
||||
patterns.push(param)
|
||||
specs.push('')
|
||||
} else {
|
||||
patterns.push(param.slice(0, atIndex))
|
||||
specs.push(param.slice(atIndex + 1))
|
||||
}
|
||||
const { pattern, versionSpec } = parseUpdateParam(param)
|
||||
patterns.push(pattern)
|
||||
specs.push(versionSpec ?? '')
|
||||
}
|
||||
const matcher = createMatcherWithIndex(patterns)
|
||||
return (depName: string) => {
|
||||
@@ -497,6 +492,20 @@ export function createMatcher (params: string[]): UpdateDepsMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseUpdateParam (param: string): { pattern: string, versionSpec: string | undefined } {
|
||||
const atIndex = param.indexOf('@', param[0] === '!' ? 2 : 1)
|
||||
if (atIndex === -1) {
|
||||
return {
|
||||
pattern: param,
|
||||
versionSpec: undefined,
|
||||
}
|
||||
}
|
||||
return {
|
||||
pattern: param.slice(0, atIndex),
|
||||
versionSpec: param.slice(atIndex + 1),
|
||||
}
|
||||
}
|
||||
|
||||
export function makeIgnorePatterns (ignoredDependencies: string[]): string[] {
|
||||
return ignoredDependencies.map(depName => `!${depName}`)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { types as allTypes } from '@pnpm/config'
|
||||
import { globalInfo } from '@pnpm/logger'
|
||||
import { createMatcher } from '@pnpm/matcher'
|
||||
import { outdatedDepsOfProjects } from '@pnpm/outdated'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { prompt } from 'enquirer'
|
||||
import chalk from 'chalk'
|
||||
import pick from 'ramda/src/pick'
|
||||
@@ -18,6 +19,7 @@ import renderHelp from 'render-help'
|
||||
import { type InstallCommandOptions } from '../install'
|
||||
import { installDeps } from '../installDeps'
|
||||
import { type ChoiceRow, getUpdateChoices } from './getUpdateChoices'
|
||||
import { parseUpdateParam } from '../recursive'
|
||||
export function rcOptionsTypes () {
|
||||
return pick([
|
||||
'cache-dir',
|
||||
@@ -275,6 +277,12 @@ async function update (
|
||||
dependencies: string[],
|
||||
opts: UpdateCommandOptions
|
||||
) {
|
||||
if (opts.latest) {
|
||||
const dependenciesWithTags = dependencies.filter((name) => parseUpdateParam(name).versionSpec != null)
|
||||
if (dependenciesWithTags.length) {
|
||||
throw new PnpmError('LATEST_WITH_SPEC', `Specs are not allowed to be used with --latest (${dependenciesWithTags.join(', ')})`)
|
||||
}
|
||||
}
|
||||
const includeDirect = makeIncludeDependenciesFromCLI(opts.cliOptions)
|
||||
const include = {
|
||||
dependencies: opts.rawConfig.production !== false,
|
||||
|
||||
@@ -311,7 +311,7 @@ test('recursive update --latest foo should only update projects that have foo',
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['@zkochan/async-regex-replace', '@pnpm.e2e/foo', '@pnpm.e2e/qar@100.1.0'])
|
||||
}, ['@zkochan/async-regex-replace', '@pnpm.e2e/foo'])
|
||||
|
||||
const lockfile = await readYamlFile<Lockfile>('./pnpm-lock.yaml')
|
||||
|
||||
@@ -319,7 +319,7 @@ test('recursive update --latest foo should only update projects that have foo',
|
||||
'/@zkochan/async-regex-replace@0.2.0',
|
||||
'/@pnpm.e2e/bar@100.0.0',
|
||||
'/@pnpm.e2e/foo@100.1.0',
|
||||
'/@pnpm.e2e/qar@100.1.0',
|
||||
'/@pnpm.e2e/qar@100.0.0',
|
||||
].sort())
|
||||
})
|
||||
|
||||
@@ -369,12 +369,12 @@ test('recursive update --latest foo should only update packages that have foo',
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['@pnpm.e2e/foo', '@pnpm.e2e/qar@100.1.0'])
|
||||
}, ['@pnpm.e2e/foo'])
|
||||
|
||||
{
|
||||
const lockfile = await projects['project-1'].readLockfile()
|
||||
|
||||
expect(Object.keys(lockfile.packages ?? {})).toStrictEqual(['/@pnpm.e2e/foo@100.1.0', '/@pnpm.e2e/qar@100.1.0'])
|
||||
expect(Object.keys(lockfile.packages ?? {})).toStrictEqual(['/@pnpm.e2e/foo@100.1.0', '/@pnpm.e2e/qar@100.0.0'])
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -99,6 +99,24 @@ test('update: fail when both "latest" and "workspace" are true', async () => {
|
||||
expect(err.message).toBe('Cannot use --latest with --workspace simultaneously')
|
||||
})
|
||||
|
||||
test('update --latest forbids specs', async () => {
|
||||
prepare()
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await update.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
latest: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, ['foo@latest', 'bar@next', 'baz'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err = _err
|
||||
}
|
||||
expect(err.code).toBe('ERR_PNPM_LATEST_WITH_SPEC')
|
||||
expect(err.message).toBe('Specs are not allowed to be used with --latest (foo@latest, bar@next)')
|
||||
})
|
||||
|
||||
describe('update by package name', () => {
|
||||
beforeAll(async () => {
|
||||
prepare({
|
||||
|
||||
Reference in New Issue
Block a user