fix: pnpm runtime set defaults to devEngines (#11951)

* fix: pnpm runtime set defaults to devEngines

Previously `pnpm runtime set <name> <version>` wrote to `engines.runtime`
because it ran `pnpm add` with the default `--save-prod`. Default to
`--save-dev` so the runtime lands in `devEngines.runtime`; pass
`--save-prod` (or `-P`) to opt back into `engines.runtime`.

Closes #11948

* fix: honor --save-dev precedence in pnpm runtime set

When both `--save-dev` and `--save-prod` are passed, prefer `--save-dev`
to match `getSaveType`'s precedence elsewhere in pnpm. Also makes the
explicit `--save-dev` flag actually consulted instead of relying solely
on the default branch.

* ci: trigger
This commit is contained in:
Zoltan Kochan
2026-05-26 16:04:10 +02:00
committed by GitHub
parent 26a7d633bf
commit a662de44dd
3 changed files with 80 additions and 5 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/engine.runtime.commands": minor
"pnpm": minor
---
`pnpm runtime set <name> <version>` now saves the runtime to `devEngines.runtime` by default instead of `engines.runtime`. Pass `--save-prod` (or `-P`) to save it to `engines.runtime` instead [#11948](https://github.com/pnpm/pnpm/issues/11948).

View File

@@ -12,6 +12,8 @@ export type RuntimeCommandOptions = Pick<Config,
> & Partial<Pick<Config,
| 'storeDir'
| 'cacheDir'
| 'saveDev'
| 'saveProd'
>>
export const skipPackageManagerCheck = true
@@ -23,6 +25,8 @@ export function rcOptionsTypes (): Record<string, unknown> {
export function cliOptionsTypes (): Record<string, unknown> {
return {
global: Boolean,
'save-dev': Boolean,
'save-prod': Boolean,
}
}
@@ -49,11 +53,22 @@ export function help (): string {
name: '--global',
shortAlias: '-g',
},
{
description: 'Save the runtime to `devEngines.runtime`. This is the default',
name: '--save-dev',
shortAlias: '-D',
},
{
description: 'Save the runtime to `engines.runtime`',
name: '--save-prod',
shortAlias: '-P',
},
],
},
],
url: docsUrl('runtime'),
usages: [
'pnpm runtime set node 22',
'pnpm runtime set node 22 -g',
'pnpm runtime set node lts -g',
'pnpm runtime set node rc/22 -g',
@@ -91,6 +106,14 @@ function runtimeSet (opts: RuntimeCommandOptions, params: string[]): void {
const versionSpec = params[1]?.trim()
const args = ['add', `${runtimeName}@runtime:${versionSpec ?? ''}`]
// Default to `devEngines.runtime`; the manifest writer maps a
// `devDependencies.<runtime>: runtime:<version>` entry to it.
// `saveDev` wins over `saveProd` to match `getSaveType` precedence.
if (opts.saveDev || !opts.saveProd) {
args.push('--save-dev')
} else {
args.push('--save-prod')
}
if (opts.global) {
args.push('--global')
if (opts.bin) args.push('--global-bin-dir', opts.bin)

View File

@@ -23,12 +23,12 @@ test('runtime set calls pnpm add with the correct arguments globally', async ()
}, ['set', 'node', '22'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:22', '--global', '--global-bin-dir', '/usr/local/bin', '--store-dir', '/tmp/store', '--cache-dir', '/tmp/cache'],
['add', 'node@runtime:22', '--save-dev', '--global', '--global-bin-dir', '/usr/local/bin', '--store-dir', '/tmp/store', '--cache-dir', '/tmp/cache'],
{ cwd: '/tmp/pnpm-home' }
)
})
test('runtime set uses project dir when not global', async () => {
test('runtime set defaults to --save-dev so the runtime lands in devEngines', async () => {
await runtime.handler({
bin: '/usr/local/bin',
dir: '/tmp/project',
@@ -37,7 +37,53 @@ test('runtime set uses project dir when not global', async () => {
}, ['set', 'node', '22'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:22', '--ignore-workspace-root-check'],
['add', 'node@runtime:22', '--save-dev', '--ignore-workspace-root-check'],
{ cwd: '/tmp/project' }
)
})
test('runtime set with --save-prod saves the runtime under engines', async () => {
await runtime.handler({
bin: '/usr/local/bin',
dir: '/tmp/project',
global: false,
pnpmHomeDir: '/tmp/pnpm-home',
saveProd: true,
}, ['set', 'node', '22'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:22', '--save-prod', '--ignore-workspace-root-check'],
{ cwd: '/tmp/project' }
)
})
test('runtime set with --save-dev keeps the runtime under devEngines (matches the default)', async () => {
await runtime.handler({
bin: '/usr/local/bin',
dir: '/tmp/project',
global: false,
pnpmHomeDir: '/tmp/pnpm-home',
saveDev: true,
}, ['set', 'node', '22'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:22', '--save-dev', '--ignore-workspace-root-check'],
{ cwd: '/tmp/project' }
)
})
test('runtime set with both --save-dev and --save-prod prefers --save-dev (matches getSaveType precedence)', async () => {
await runtime.handler({
bin: '/usr/local/bin',
dir: '/tmp/project',
global: false,
pnpmHomeDir: '/tmp/pnpm-home',
saveDev: true,
saveProd: true,
}, ['set', 'node', '22'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:22', '--save-dev', '--ignore-workspace-root-check'],
{ cwd: '/tmp/project' }
)
})
@@ -51,7 +97,7 @@ test('runtime set without version spec', async () => {
}, ['set', 'node'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'node@runtime:', '--global', '--global-bin-dir', '/usr/local/bin'],
['add', 'node@runtime:', '--save-dev', '--global', '--global-bin-dir', '/usr/local/bin'],
{ cwd: '/tmp/pnpm-home' }
)
})
@@ -65,7 +111,7 @@ test('runtime set works with deno', async () => {
}, ['set', 'deno', '2'])
expect(mockRunPnpmCli).toHaveBeenCalledWith(
['add', 'deno@runtime:2', '--global', '--global-bin-dir', '/usr/local/bin'],
['add', 'deno@runtime:2', '--save-dev', '--global', '--global-bin-dir', '/usr/local/bin'],
{ cwd: '/tmp/pnpm-home' }
)
})