fix: use devEngines.runtime node version for engine checks (#10954)

When devEngines.runtime (or engines.runtime) specifies a Node.js version
with onFail: "download", use that version for engine strictness checks
instead of the system Node.js version.

close #10033
This commit is contained in:
Zoltan Kochan
2026-03-14 20:00:40 +01:00
committed by GitHub
parent 784cdcd419
commit da2429d354
3 changed files with 75 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/config": patch
"pnpm": patch
---
Engine validation now uses the Node.js version from `devEngines.runtime` (or `engines.runtime`) instead of the system Node.js version [#10033](https://github.com/pnpm/pnpm/issues/10033).

View File

@@ -402,6 +402,9 @@ export async function getConfig (opts: {
if (pnpmConfig.rootProjectManifest) {
Object.assign(pnpmConfig, getOptionsFromRootManifest(pnpmConfig.rootProjectManifestDir, pnpmConfig.rootProjectManifest))
}
if (pnpmConfig.nodeVersion == null) {
pnpmConfig.nodeVersion = getNodeVersionFromEnginesRuntime(pnpmConfig.rootProjectManifest)
}
}
if (pnpmConfig.workspaceDir != null) {
@@ -721,6 +724,21 @@ function parseDevEnginesPackageManager (devEngines?: DevEngines): EngineDependen
}
}
function getNodeVersionFromEnginesRuntime (manifest: ProjectManifest): string | undefined {
for (const enginesFieldName of ['devEngines', 'engines'] as const) {
const enginesRuntime = manifest[enginesFieldName]?.runtime
if (enginesRuntime == null) continue
const runtimes: EngineDependency[] = Array.isArray(enginesRuntime) ? enginesRuntime : [enginesRuntime]
const nodeRuntime = runtimes.find((r) => r.name === 'node')
if (nodeRuntime?.version == null) continue
const minVersion = semver.minVersion(nodeRuntime.version)
if (minVersion != null) {
return minVersion.version
}
}
return undefined
}
function addSettingsFromWorkspaceManifestToConfig (pnpmConfig: Config, {
configFromCliOpts,
projectManifest,

View File

@@ -56,6 +56,57 @@ test('getConfig()', async () => {
expect(config.nodeVersion).toBeUndefined()
})
test.each([
{ field: 'devEngines' as const, version: '22.20.0', onFail: 'download' as const, expected: '22.20.0' },
{ field: 'devEngines' as const, version: '22.20.0', onFail: 'error' as const, expected: '22.20.0' },
{ field: 'devEngines' as const, version: '^22.0.0', onFail: 'download' as const, expected: '22.0.0' },
{ field: 'engines' as const, version: '22.20.0', onFail: 'download' as const, expected: '22.20.0' },
])('when $field is $version and onFail is $onFail, nodeVersion is set to $expected', async ({ field, version, onFail, expected }) => {
prepare({
[field]: {
runtime: {
name: 'node',
version,
onFail,
},
},
})
const { config } = await getConfig({
cliOptions: {},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.nodeVersion).toBe(expected)
})
test('nodeVersion from config takes priority over devEngines.runtime', async () => {
prepare({
devEngines: {
runtime: {
name: 'node',
version: '22.20.0',
onFail: 'download',
},
},
})
const { config } = await getConfig({
cliOptions: {
'node-version': '20.0.0',
},
packageManager: {
name: 'pnpm',
version: '1.0.0',
},
})
expect(config.nodeVersion).toBe('20.0.0')
})
test('throw error if --link-workspace-packages is used with --global', async () => {
await expect(getConfig({
cliOptions: {