fix: some commands should not fail if a different package manager is set in package.json (#8802)

close #7959
This commit is contained in:
Zoltan Kochan
2024-11-25 10:13:23 +01:00
committed by GitHub
parent 4dd27a894f
commit 39c53852ea
12 changed files with 126 additions and 77 deletions

View File

@@ -0,0 +1,10 @@
---
"@pnpm/plugin-commands-store-inspecting": patch
"@pnpm/plugin-commands-completion": patch
"@pnpm/plugin-commands-script-runners": patch
"@pnpm/plugin-commands-store": patch
"@pnpm/plugin-commands-env": patch
"pnpm": patch
---
Some commands should ignore the `packageManager` field check of `package.json` [#7959](https://github.com/pnpm/pnpm/issues/7959).

View File

@@ -5,6 +5,8 @@ import { getShellFromParams } from './getShell'
export const commandNames = ['completion']
export const skipPackageManagerCheck = true
export const rcOptionsTypes = (): Record<string, unknown> => ({})
export const cliOptionsTypes = (): Record<string, unknown> => ({})

View File

@@ -7,6 +7,8 @@ import { type NvmNodeCommandOptions } from './node'
import { envList } from './envList'
import { envAdd } from './envAdd'
export const skipPackageManagerCheck = true
export function rcOptionsTypes (): Record<string, unknown> {
return {}
}

View File

@@ -16,6 +16,8 @@ import renderHelp from 'render-help'
import symlinkDir from 'symlink-dir'
import { makeEnv } from './makeEnv'
export const skipPackageManagerCheck = true
export const commandNames = ['dlx']
export const shorthands: Record<string, string> = {

View File

@@ -101,6 +101,10 @@ export interface CommandDefinition {
* ```
*/
shorthands?: Record<string, string | string[]>
/**
* If true, this command should not care about what package manager is specified in the "packageManager" field of "package.json".
*/
skipPackageManagerCheck?: boolean
}
const commands: CommandDefinition[] = [
@@ -161,6 +165,7 @@ const aliasToFullName = new Map<string, string>()
const completionByCommandName: Record<string, CompletionFunc> = {}
const shorthandsByCommandName: Record<string, Record<string, string | string[]>> = {}
const rcOptionsTypes: Record<string, unknown> = {}
const skipPackageManagerCheckForCommandArray = ['completion-server']
for (let i = 0; i < commands.length; i++) {
const {
@@ -171,6 +176,7 @@ for (let i = 0; i < commands.length; i++) {
help,
rcOptionsTypes,
shorthands,
skipPackageManagerCheck,
} = commands[i]
if (!commandNames || commandNames.length === 0) {
throw new Error(`The command at index ${i} doesn't have command names`)
@@ -185,6 +191,9 @@ for (let i = 0; i < commands.length; i++) {
}
Object.assign(rcOptionsTypes, rcOptionsTypes())
}
if (skipPackageManagerCheck) {
skipPackageManagerCheckForCommandArray.push(...commandNames)
}
if (commandNames.length > 1) {
const fullName = commandNames[0]
for (let i = 1; i < commandNames.length; i++) {
@@ -210,6 +219,8 @@ function initialCompletion (): Array<{ name: string }> {
export const pnpmCmds = handlerByCommandName
export const skipPackageManagerCheckForCommand = new Set(skipPackageManagerCheckForCommandArray)
export function getCliOptionsTypes (commandName: string): Record<string, unknown> {
return cliOptionsTypesByCommandName[commandName]?.() || {}
}

View File

@@ -24,7 +24,7 @@ import path from 'path'
import isEmpty from 'ramda/src/isEmpty'
import stripAnsi from 'strip-ansi'
import { checkForUpdates } from './checkForUpdates'
import { pnpmCmds, rcOptionsTypes } from './cmd'
import { pnpmCmds, rcOptionsTypes, skipPackageManagerCheckForCommand } from './cmd'
import { formatUnknownOptionsError } from './formatError'
import { parseCliArgs } from './parseCliArgs'
import { initReporter, type ReporterType } from './reporter'
@@ -108,7 +108,7 @@ export async function main (inputArgv: string[]): Promise<void> {
if (!isExecutedByCorepack() && cmd !== 'setup' && config.wantedPackageManager != null) {
if (config.managePackageManagerVersions && config.wantedPackageManager?.name === 'pnpm') {
await switchCliVersion(config)
} else {
} else if (!cmd || !skipPackageManagerCheckForCommand.has(cmd)) {
checkPackageManager(config.wantedPackageManager, config)
}
}

View File

@@ -248,81 +248,6 @@ test('`pnpm -r add` should fail if no package name was provided', () => {
expect(stdout.toString()).toContain('`pnpm add` requires the package name')
})
test('install should fail if the used pnpm version does not satisfy the pnpm version specified in engines', async () => {
prepare({
name: 'project',
version: '1.0.0',
engines: {
pnpm: '99999',
},
})
const { status, stdout } = execPnpmSync(['install'])
expect(status).toBe(1)
expect(stdout.toString()).toContain('Your pnpm version is incompatible with')
})
test('install should not fail if the used pnpm version does not satisfy the pnpm version specified in packageManager', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'pnpm@0.0.0',
})
expect(execPnpmSync(['install', '--config.manage-package-manager-versions=false']).status).toBe(0)
const { status, stderr } = execPnpmSync(['install', '--config.manage-package-manager-versions=false', '--config.package-manager-strict-version=true'])
expect(status).toBe(1)
expect(stderr.toString()).toContain('This project is configured to use v0.0.0 of pnpm. Your current pnpm is')
})
test('install should fail if the project requires a different package manager', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'yarn@4.0.0',
})
const { status, stderr } = execPnpmSync(['install', '--config.manage-package-manager-versions=true'])
expect(status).toBe(1)
expect(stderr.toString()).toContain('This project is configured to use yarn')
expect(execPnpmSync(['install', '--config.package-manager-strict=false']).status).toBe(0)
})
test('install should not fail for packageManager field with hash', async () => {
const versionProcess = execPnpmSync(['--version'])
const pnpmVersion = versionProcess.stdout.toString().trim()
prepare({
name: 'project',
version: '1.0.0',
packageManager: `pnpm@${pnpmVersion}+sha256.123456789`,
})
const { status } = execPnpmSync(['install'])
expect(status).toBe(0)
})
test('install should not fail for packageManager field with url', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'pnpm@https://github.com/pnpm/pnpm',
})
const { status } = execPnpmSync(['install'])
expect(status).toBe(0)
})
test('engine-strict=false: install should not fail if the used Node version does not satisfy the Node version specified in engines', async () => {
prepare({
name: 'project',

View File

@@ -0,0 +1,89 @@
import { prepare } from '@pnpm/prepare'
import { execPnpmSync } from './utils'
test('install should fail if the used pnpm version does not satisfy the pnpm version specified in engines', async () => {
prepare({
name: 'project',
version: '1.0.0',
engines: {
pnpm: '99999',
},
})
const { status, stdout } = execPnpmSync(['install'])
expect(status).toBe(1)
expect(stdout.toString()).toContain('Your pnpm version is incompatible with')
})
test('install should not fail if the used pnpm version does not satisfy the pnpm version specified in packageManager', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'pnpm@0.0.0',
})
expect(execPnpmSync(['install', '--config.manage-package-manager-versions=false']).status).toBe(0)
const { status, stderr } = execPnpmSync(['install', '--config.manage-package-manager-versions=false', '--config.package-manager-strict-version=true'])
expect(status).toBe(1)
expect(stderr.toString()).toContain('This project is configured to use v0.0.0 of pnpm. Your current pnpm is')
})
test('install should fail if the project requires a different package manager', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'yarn@4.0.0',
})
const { status, stderr } = execPnpmSync(['install', '--config.manage-package-manager-versions=true'])
expect(status).toBe(1)
expect(stderr.toString()).toContain('This project is configured to use yarn')
expect(execPnpmSync(['install', '--config.package-manager-strict=false']).status).toBe(0)
})
test('install should not fail for packageManager field with hash', async () => {
const versionProcess = execPnpmSync(['--version'])
const pnpmVersion = versionProcess.stdout.toString().trim()
prepare({
name: 'project',
version: '1.0.0',
packageManager: `pnpm@${pnpmVersion}+sha256.123456789`,
})
const { status } = execPnpmSync(['install'])
expect(status).toBe(0)
})
test('install should not fail for packageManager field with url', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'pnpm@https://github.com/pnpm/pnpm',
})
const { status } = execPnpmSync(['install'])
expect(status).toBe(0)
})
test('some commands should not fail if the required package manager is not pnpm', async () => {
prepare({
name: 'project',
version: '1.0.0',
packageManager: 'yarn@3.0.0',
})
const { status } = execPnpmSync(['store', 'path'])
expect(status).toBe(0)
})

View File

@@ -10,6 +10,8 @@ import renderHelp from 'render-help'
const INTEGRITY_REGEX: RegExp = /^([^-]+)-([A-Za-z0-9+/=]+)$/
export const skipPackageManagerCheck = true
export const commandNames = ['cat-file']
export const rcOptionsTypes = cliOptionsTypes

View File

@@ -12,6 +12,8 @@ import sortKeys from 'sort-keys'
import loadJsonFile from 'load-json-file'
import renderHelp from 'render-help'
export const skipPackageManagerCheck = true
export const commandNames = ['cat-index']
export const rcOptionsTypes = cliOptionsTypes

View File

@@ -13,6 +13,8 @@ import renderHelp from 'render-help'
export const PACKAGE_INFO_CLR = chalk.greenBright
export const INDEX_PATH_CLR = chalk.hex('#078487')
export const skipPackageManagerCheck = true
export const commandNames = ['find-hash']
export const rcOptionsTypes = cliOptionsTypes

View File

@@ -10,6 +10,8 @@ import { storeAdd } from './storeAdd'
import { storePrune } from './storePrune'
import { storeStatus } from './storeStatus'
export const skipPackageManagerCheck = true
export const rcOptionsTypes = cliOptionsTypes
export function cliOptionsTypes (): Record<string, unknown> {