mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-12 01:54:53 -04:00
fix(cli): honor --pm-on-fail when combined with --help / --version (#11489)
The CLI argument parser short-circuits `--help` and `--version` and was discarding every other parsed option in the process — including universal flags like `--pm-on-fail`. So `pnpm audit --pm-on-fail=ignore --help` and `pnpm --pm-on-fail=ignore --version` failed with the strict `packageManager` mismatch error instead of doing what was asked. Users had no documented way out: the suggested escape hatch in the error message itself didn't work. The fix plucks universal options back out of the exploratory `nopt` parse and surfaces them through both short-circuits. They were already typed correctly there; only the regular per-command parse adds command-specific options. Command-specific options (e.g. `--frozen-lockfile`) stay dropped, since the matching command isn't being executed. Closes [#11487](https://github.com/pnpm/pnpm/issues/11487).
This commit is contained in:
@@ -185,6 +185,64 @@ test('no command', async () => {
|
||||
expect(cmd).toBeNull()
|
||||
})
|
||||
|
||||
// Regression for #11487 — --pm-on-fail must reach the consumer even when
|
||||
// short-circuited by --help, otherwise users can't bypass the
|
||||
// packageManager check just to read help text for a stale-pinned project.
|
||||
test('universal options typed in the exploratory parse survive the --help short-circuit', async () => {
|
||||
const { cmd, options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
universalOptionsTypes: { 'pm-on-fail': ['ignore', 'warn', 'error'] },
|
||||
}, ['install', '--pm-on-fail=ignore', '--help'])
|
||||
expect(cmd).toBe('help')
|
||||
expect(options).toMatchObject({ 'pm-on-fail': 'ignore' })
|
||||
})
|
||||
|
||||
test('universal options typed in the exploratory parse survive the --version short-circuit', async () => {
|
||||
const { cmd, options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
universalOptionsTypes: { 'pm-on-fail': ['ignore', 'warn', 'error'] },
|
||||
}, ['--pm-on-fail=ignore', '--version'])
|
||||
expect(cmd).toBeNull()
|
||||
expect(options).toMatchObject({ version: true, 'pm-on-fail': 'ignore' })
|
||||
})
|
||||
|
||||
test('command-specific options do NOT leak through the --help short-circuit', async () => {
|
||||
// We're not executing the command, so its options shouldn't appear in
|
||||
// cliOptions and accidentally influence config (e.g. --frozen-lockfile
|
||||
// shouldn't bleed into the help path).
|
||||
const { cmd, options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
getTypesByCommandName: (name) => name === 'install' ? { 'frozen-lockfile': Boolean } : {},
|
||||
}, ['install', '--frozen-lockfile', '--help'])
|
||||
expect(cmd).toBe('help')
|
||||
expect(options).not.toHaveProperty(['frozen-lockfile'])
|
||||
})
|
||||
|
||||
// renamedOptions (e.g. pnpm's --prefix → dir) must be applied in the
|
||||
// short-circuit too, otherwise consumers downstream receive inconsistent
|
||||
// keys depending on whether --help/--version was the entry path.
|
||||
test('renamedOptions are applied to picked universal options in --help short-circuit', async () => {
|
||||
const { cmd, options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
universalOptionsTypes: { prefix: String },
|
||||
renamedOptions: { prefix: 'dir' },
|
||||
}, ['install', '--prefix=/foo', '--help'])
|
||||
expect(cmd).toBe('help')
|
||||
expect(options).toMatchObject({ dir: '/foo' })
|
||||
expect(options).not.toHaveProperty(['prefix'])
|
||||
})
|
||||
|
||||
test('renamedOptions are applied to picked universal options in --version short-circuit', async () => {
|
||||
const { cmd, options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
universalOptionsTypes: { prefix: String },
|
||||
renamedOptions: { prefix: 'dir' },
|
||||
}, ['--prefix=/foo', '--version'])
|
||||
expect(cmd).toBeNull()
|
||||
expect(options).toMatchObject({ version: true, dir: '/foo' })
|
||||
expect(options).not.toHaveProperty(['prefix'])
|
||||
})
|
||||
|
||||
test('use command-specific shorthands', async () => {
|
||||
const { options } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
|
||||
Reference in New Issue
Block a user