feat!: remove use-node-version CLI option and pnpm.executionEnv.nodeVersion manifest field (#10373)

This commit is contained in:
Zoltan Kochan
2025-12-27 22:41:53 +01:00
committed by GitHub
parent db72923b5c
commit 71de2b3f2b
40 changed files with 53 additions and 506 deletions

View File

@@ -0,0 +1,16 @@
---
"@pnpm/plugin-commands-installation": major
"@pnpm/plugin-commands-publishing": major
"@pnpm/plugin-commands-script-runners": major
"@pnpm/plugin-commands-deploy": major
"@pnpm/plugin-commands-env": major
"@pnpm/workspace.find-packages": major
"@pnpm/constants": major
"@pnpm/core": major
"@pnpm/lifecycle": major
"@pnpm/types": major
"@pnpm/config": major
"pnpm": major
---
Removed support for the `useNodeVersion` and `executionEnv.nodeVersion` fields. `devEngines.runtime` and `engines.runtime` should be used instead [#10373](https://github.com/pnpm/pnpm/pull/10373).

View File

@@ -98,7 +98,6 @@ export interface Config extends OptionsFromRootManifest {
ignoreCurrentSpecifiers?: boolean
recursive?: boolean
enablePrePostScripts?: boolean
useNodeVersion?: string
useStderr?: boolean
nodeLinker?: 'hoisted' | 'isolated' | 'pnp'
preferSymlinkedExecutables?: boolean

View File

@@ -27,7 +27,7 @@ export type OptionsFromRootManifest = {
peerDependencyRules?: PeerDependencyRules
supportedArchitectures?: SupportedArchitectures
allowBuilds?: Record<string, boolean | string>
} & Pick<PnpmSettings, 'configDependencies' | 'auditConfig' | 'executionEnv' | 'updateConfig'>
} & Pick<PnpmSettings, 'configDependencies' | 'auditConfig' | 'updateConfig'>
export function getOptionsFromRootManifest (manifestDir: string, manifest: ProjectManifest): OptionsFromRootManifest {
const settings: OptionsFromRootManifest = getOptionsFromPnpmSettings(manifestDir, {
@@ -40,8 +40,6 @@ export function getOptionsFromRootManifest (manifestDir: string, manifest: Proje
'auditConfig',
'auditConfig',
'configDependencies',
'executionEnv',
'executionEnv',
'ignorePatchFailures',
'ignoredBuiltDependencies',
'ignoredOptionalDependencies',

View File

@@ -1332,7 +1332,6 @@ test('loads setting from environment variable pnpm_config_*', async () => {
})
expect(config.fetchRetries).toBe(100)
expect(config.hoistPattern).toStrictEqual(['react', 'react-dom'])
expect(config.useNodeVersion).toBe('22.0.0')
expect(config.onlyBuiltDependencies).toStrictEqual(['is-number', 'is-positive', 'is-negative'])
expect(config.registry).toBe('https://registry.example.com/')
expect(config.registries.default).toBe('https://registry.example.com/')
@@ -1342,10 +1341,10 @@ test('environment variable pnpm_config_* should override pnpm-workspace.yaml', a
prepareEmpty()
writeYamlFile('pnpm-workspace.yaml', {
useNodeVersion: '20.0.0',
fetchRetries: 5,
})
async function getConfigValue (env: NodeJS.ProcessEnv): Promise<string | undefined> {
async function getConfigValue (env: NodeJS.ProcessEnv): Promise<number | undefined> {
const { config } = await getConfig({
cliOptions: {},
env,
@@ -1355,23 +1354,23 @@ test('environment variable pnpm_config_* should override pnpm-workspace.yaml', a
},
workspaceDir: process.cwd(),
})
return config.useNodeVersion
return config.fetchRetries
}
expect(await getConfigValue({})).toBe('20.0.0')
expect(await getConfigValue({})).toBe(5)
expect(await getConfigValue({
pnpm_config_use_node_version: '22.0.0',
})).toBe('22.0.0')
pnpm_config_fetch_retries: '10',
})).toBe(10)
})
test('CLI should override environment variable pnpm_config_*', async () => {
prepareEmpty()
async function getConfigValue (cliOptions: Record<string, unknown>): Promise<string | undefined> {
async function getConfigValue (cliOptions: Record<string, unknown>): Promise<number | undefined> {
const { config } = await getConfig({
cliOptions,
env: {
pnpm_config_use_node_version: '18.0.0',
pnpm_config_fetch_retries: '5',
},
packageManager: {
name: 'pnpm',
@@ -1379,16 +1378,16 @@ test('CLI should override environment variable pnpm_config_*', async () => {
},
workspaceDir: process.cwd(),
})
return config.useNodeVersion
return config.fetchRetries
}
expect(await getConfigValue({})).toBe('18.0.0')
expect(await getConfigValue({})).toBe(5)
expect(await getConfigValue({
useNodeVersion: '22.0.0',
})).toBe('22.0.0')
fetchRetries: 10,
})).toBe(10)
expect(await getConfigValue({
'use-node-version': '22.0.0',
})).toBe('22.0.0')
'fetch-retries': 10,
})).toBe(10)
})
describe('global config.yaml', () => {

View File

@@ -1,4 +1,3 @@
import * as env from './env.js'
import { prepareExecutionEnv } from './node.js'
export { env, prepareExecutionEnv }
export { env }

View File

@@ -2,13 +2,11 @@ import fs from 'fs'
import path from 'path'
import util from 'util'
import { type Config } from '@pnpm/config'
import { getSystemNodeVersion } from '@pnpm/env.system-node-version'
import { createFetchFromRegistry, type FetchFromRegistry } from '@pnpm/fetch'
import { globalInfo, globalWarn } from '@pnpm/logger'
import { fetchNode } from '@pnpm/node.fetcher'
import { getNodeMirror } from '@pnpm/node.resolver'
import { getStorePath } from '@pnpm/store-path'
import { type PrepareExecutionEnvOptions, type PrepareExecutionEnvResult } from '@pnpm/types'
import { loadJsonFile } from 'load-json-file'
import { writeJsonFile } from 'write-json-file'
import { isValidVersion, parseNodeSpecifier } from './parseNodeSpecifier.js'
@@ -32,31 +30,10 @@ export type NvmNodeCommandOptions = Pick<Config,
| 'rawConfig'
| 'strictSsl'
| 'storeDir'
| 'useNodeVersion'
| 'pnpmHomeDir'
> & Partial<Pick<Config, 'configDir' | 'cliOptions' | 'sslConfigs'>> & {
remote?: boolean
}
const nodeFetchPromises: Record<string, Promise<string>> = {}
export async function prepareExecutionEnv (config: NvmNodeCommandOptions, { extraBinPaths, executionEnv }: PrepareExecutionEnvOptions): Promise<PrepareExecutionEnvResult> {
if (!executionEnv?.nodeVersion || `v${executionEnv.nodeVersion}` === getSystemNodeVersion()) {
return { extraBinPaths: extraBinPaths ?? [] }
}
let nodePathPromise = nodeFetchPromises[executionEnv.nodeVersion]
if (!nodePathPromise) {
nodePathPromise = getNodeBinDir({
...config,
useNodeVersion: executionEnv.nodeVersion,
})
nodeFetchPromises[executionEnv.nodeVersion] = nodePathPromise
}
return {
extraBinPaths: [await nodePathPromise, ...extraBinPaths ?? []],
}
useNodeVersion?: string
}
export async function getNodeBinDir (opts: NvmNodeCommandOptions): Promise<string> {

View File

@@ -60,7 +60,6 @@ const {
getNodeDir,
getNodeBinDir,
getNodeVersionsBaseDir,
prepareExecutionEnv,
} = await import('../lib/node.js')
beforeEach(() => {
@@ -145,19 +144,3 @@ test('specified an invalid Node.js via use-node-version should not cause pnpm it
const calls = jest.mocked(globalWarn).mock.calls
expect(calls[calls.length - 1][0]).toContain('"22.14" is not a valid Node.js version.')
})
describe('prepareExecutionEnv', () => {
test('should not proceed to fetch Node.js if the process is already running in wanted node version', async () => {
fetchMock.mockImplementationOnce(() => {
throw new Error('prepareExecutionEnv should not proceed to fetch Node.js when wanted version is running')
})
await prepareExecutionEnv({
bin: '',
pnpmHomeDir: process.cwd(),
rawConfig: {},
}, {
executionEnv: { nodeVersion: process.versions.node },
})
})
})

View File

@@ -2,7 +2,7 @@ import path from 'path'
import { lifecycleLogger } from '@pnpm/core-loggers'
import { globalWarn } from '@pnpm/logger'
import lifecycle from '@pnpm/npm-lifecycle'
import { type DependencyManifest, type ProjectManifest, type PrepareExecutionEnv, type PackageScripts } from '@pnpm/types'
import { type DependencyManifest, type ProjectManifest, type PackageScripts } from '@pnpm/types'
import { PnpmError } from '@pnpm/error'
import { existsSync } from 'fs'
import isWindows from 'is-windows'
@@ -26,7 +26,6 @@ export interface RunLifecycleHookOptions {
shellEmulator?: boolean
stdio?: string
unsafePerm: boolean
prepareExecutionEnv?: PrepareExecutionEnv
}
export async function runLifecycleHook (
@@ -109,17 +108,13 @@ Please unset the scriptShell option, or configure it to a .exe instead.
const logLevel = (opts.stdio !== 'inherit' || opts.silent)
? 'silent'
: undefined
const extraBinPaths = (await opts.prepareExecutionEnv?.({
extraBinPaths: opts.extraBinPaths,
executionEnv: (manifest as ProjectManifest).pnpm?.executionEnv,
}))?.extraBinPaths ?? opts.extraBinPaths
await lifecycle(m, stage, opts.pkgRoot, {
config: {
...opts.rawConfig,
'frozen-lockfile': false,
},
dir: opts.rootModulesDir,
extraBinPaths,
extraBinPaths: opts.extraBinPaths,
extraEnv: {
...opts.extraEnv,
INIT_CWD: opts.initCwd ?? process.cwd(),

View File

@@ -33,7 +33,6 @@ export function rcOptionsTypes (): Record<string, unknown> {
'cpu',
'libc',
'os',
'use-node-version',
], types),
'shell-mode': Boolean,
}
@@ -78,7 +77,7 @@ export type DlxCommandOptions = {
package?: string[]
shellMode?: boolean
allowBuild?: string[]
} & Pick<Config, 'extraBinPaths' | 'registries' | 'reporter' | 'userAgent' | 'cacheDir' | 'dlxCacheMaxAge' | 'useNodeVersion' | 'symlink'> & Omit<add.AddCommandOptions, 'rootProjectManifestDir'> & PnpmSettings
} & Pick<Config, 'extraBinPaths' | 'registries' | 'reporter' | 'userAgent' | 'cacheDir' | 'dlxCacheMaxAge' | 'symlink'> & Omit<add.AddCommandOptions, 'rootProjectManifestDir'> & PnpmSettings
export async function handler (
opts: DlxCommandOptions,

View File

@@ -7,7 +7,6 @@ import { type CheckDepsStatusOptions } from '@pnpm/deps.status'
import { makeNodeRequireOption } from '@pnpm/lifecycle'
import { logger } from '@pnpm/logger'
import { tryReadProjectManifest } from '@pnpm/read-project-manifest'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { sortPackages } from '@pnpm/sort-packages'
import { type Project, type ProjectsGraph, type ProjectRootDir, type ProjectRootDirRealPath } from '@pnpm/types'
import execa from 'execa'
@@ -41,7 +40,6 @@ export function rcOptionsTypes (): Record<string, unknown> {
...pick([
'bail',
'sort',
'use-node-version',
'unsafe-perm',
'workspace-concurrency',
'reporter-hide-prefix',
@@ -222,23 +220,15 @@ export async function handler (
const workspacePnpPath = opts.workspaceDir && existsPnp(opts.workspaceDir)
let exitCode = 0
const mapPrefixToPrependPaths: Record<ProjectRootDir, string[]> = {}
await Promise.all(chunks.flat().map(async prefix => {
const executionEnv = await prepareExecutionEnv(opts, {
extraBinPaths: opts.extraBinPaths,
executionEnv: opts.selectedProjectsGraph[prefix]?.package.manifest.pnpm?.executionEnv,
})
mapPrefixToPrependPaths[prefix] = [
'./node_modules/.bin',
...executionEnv.extraBinPaths,
]
}))
const prependPaths = [
'./node_modules/.bin',
...(opts.extraBinPaths ?? []),
]
const reporterShowPrefix = opts.recursive && opts.reporterHidePrefix === false
for (const chunk of chunks) {
// eslint-disable-next-line no-await-in-loop
await Promise.all(chunk.map(async (prefix) =>
limitRun(async () => {
const prependPaths = mapPrefixToPrependPaths[prefix]
result[prefix].status = 'running'
const startTime = process.hrtime()
try {

View File

@@ -6,7 +6,6 @@ import {
tryReadProjectManifest,
} from '@pnpm/cli-utils'
import { type CompletionFunc } from '@pnpm/command'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { type Config, types as allTypes, getWorkspaceConcurrency } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
@@ -86,7 +85,6 @@ export function rcOptionsTypes (): Record<string, unknown> {
return {
...pick([
'npm-path',
'use-node-version',
], allTypes),
}
}
@@ -97,7 +95,6 @@ export function cliOptionsTypes (): Record<string, unknown> {
'bail',
'sort',
'unsafe-perm',
'use-node-version',
'workspace-concurrency',
'scripts-prepend-node-path',
], allTypes),
@@ -165,7 +162,6 @@ export type RunOpts =
| 'dir'
| 'enablePrePostScripts'
| 'engineStrict'
| 'executionEnv'
| 'extraBinPaths'
| 'extraEnv'
| 'nodeOptions'
@@ -280,9 +276,6 @@ so you may run "pnpm -w run ${scriptName}"`,
stdio: (specifiedScripts.length > 1 && concurrency > 1) ? 'pipe' : 'inherit',
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
}
if (opts.executionEnv != null) {
lifecycleOpts.extraBinPaths = (await prepareExecutionEnv(opts, { executionEnv: opts.executionEnv })).extraBinPaths
}
const existsPnp = existsInDir.bind(null, '.pnp.cjs')
const pnpPath = (opts.workspaceDir && existsPnp(opts.workspaceDir)) ?? existsPnp(dir)
if (pnpPath) {

View File

@@ -3,7 +3,6 @@ import path from 'path'
import util from 'util'
import { throwOnCommandFail } from '@pnpm/cli-utils'
import { type Config, getWorkspaceConcurrency } from '@pnpm/config'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { PnpmError } from '@pnpm/error'
import {
makeNodeRequireOption,
@@ -127,10 +126,6 @@ export async function runRecursive (
stdio,
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
}
const { executionEnv } = pkg.package.manifest.pnpm ?? {}
if (executionEnv != null) {
lifecycleOpts.extraBinPaths = (await prepareExecutionEnv(opts, { executionEnv })).extraBinPaths
}
const pnpPath = workspacePnpPath ?? existsPnp(prefix)
if (pnpPath) {
lifecycleOpts.extraEnv = {

View File

@@ -756,25 +756,3 @@ test('pnpm run without node version', async () => {
workspaceConcurrency: 1,
}, ['assert-node-version'])
})
test('pnpm run with node version', async () => {
prepare({
scripts: {
'assert-node-version': 'node -e "assert.equal(process.version, \'v20.0.0\')"',
},
})
await run.handler({
...DEFAULT_OPTS,
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: process.cwd(),
rawConfig: {},
workspaceConcurrency: 1,
executionEnv: {
nodeVersion: '20.0.0',
},
}, ['assert-node-version'])
})

View File

@@ -18,8 +18,6 @@ export const ABBREVIATED_META_DIR = 'metadata-v1.3'
export const FULL_META_DIR = 'metadata-full-v1.3' // This is currently not used at all
export const FULL_FILTERED_META_DIR = 'metadata-ff-v1.3'
export const USEFUL_NON_ROOT_PNPM_FIELDS = ['executionEnv'] as const
export function getNodeBinLocationForCurrentOS (platform: string = process.platform): string {
return platform === 'win32' ? 'node.exe' : 'bin/node'
}

View File

@@ -1,14 +0,0 @@
export interface PrepareExecutionEnvOptions {
extraBinPaths?: string[]
executionEnv: ExecutionEnv | undefined
}
export interface PrepareExecutionEnvResult {
extraBinPaths: string[]
}
export type PrepareExecutionEnv = (options: PrepareExecutionEnvOptions) => Promise<PrepareExecutionEnvResult>
export interface ExecutionEnv {
nodeVersion?: string
}

View File

@@ -1,5 +1,4 @@
export * from './config.js'
export * from './env.js'
export * from './misc.js'
export * from './options.js'
export * from './package.js'

View File

@@ -1,5 +1,3 @@
import { type ExecutionEnv } from './env.js'
export type Dependencies = Record<string, string>
export type PackageBin = string | { [commandName: string]: string }
@@ -173,7 +171,6 @@ export interface PnpmSettings {
auditConfig?: AuditConfig
requiredScripts?: string[]
supportedArchitectures?: SupportedArchitectures
executionEnv?: ExecutionEnv
}
export interface ProjectManifest extends BaseManifest {

View File

@@ -17,7 +17,6 @@ import {
type PeerDependencyRules,
type ReadPackageHook,
type Registries,
type PrepareExecutionEnv,
type TrustPolicy,
} from '@pnpm/types'
import { type CustomResolver, type CustomFetcher, type PreResolutionHookContext } from '@pnpm/hooks.types'
@@ -165,7 +164,6 @@ export interface StrictInstallOptions {
hoistWorkspacePackages?: boolean
virtualStoreDirMaxLength: number
peersSuffixMaxLength: number
prepareExecutionEnv?: PrepareExecutionEnv
returnListOfDepsRequiringBuild?: boolean
injectWorkspacePackages?: boolean
ci?: boolean

View File

@@ -410,7 +410,6 @@ export async function mutateModules (
stdio: opts.ownLifecycleHooksStdio,
storeController: opts.storeController,
unsafePerm: opts.unsafePerm || false,
prepareExecutionEnv: opts.prepareExecutionEnv,
}
if (!opts.ignoreScripts && !opts.ignorePackageManifest && rootProjectManifest?.scripts?.[DEV_PREINSTALL]) {

View File

@@ -56,7 +56,6 @@
"@pnpm/package-store": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/pick-registry-for-package": "workspace:*",
"@pnpm/plugin-commands-env": "workspace:*",
"@pnpm/plugin-commands-rebuild": "workspace:*",
"@pnpm/pnpmfile": "workspace:*",
"@pnpm/read-modules-dir": "workspace:*",

View File

@@ -3,7 +3,6 @@ import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-
import { types as allTypes } from '@pnpm/config'
import { resolveConfigDeps } from '@pnpm/config.deps-installer'
import { PnpmError } from '@pnpm/error'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { createOrConnectStoreController } from '@pnpm/store-connection-manager'
import { pick } from 'ramda'
import renderHelp from 'render-help'
@@ -291,6 +290,5 @@ export async function handler (
fetchFullMetadata: getFetchFullMetadata(opts),
include,
includeDirect: include,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, params)
}

View File

@@ -1,7 +1,6 @@
import { docsUrl } from '@pnpm/cli-utils'
import { OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { dedupeDiffCheck } from '@pnpm/dedupe.check'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import renderHelp from 'render-help'
import { type InstallCommandOptions, rcOptionsTypes as installCommandRcOptionsTypes } from './install.js'
import { installDeps } from './installDeps.js'
@@ -64,6 +63,5 @@ export async function handler (opts: DedupeCommandOptions): Promise<void> {
include,
includeDirect: include,
lockfileCheck: opts.check ? dedupeDiffCheck : undefined,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, [])
}

View File

@@ -2,7 +2,6 @@ import { docsUrl } from '@pnpm/cli-utils'
import { FILTERING, OPTIONS, OUTPUT_OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { type Config, types as allTypes } from '@pnpm/config'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { type CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { pick } from 'ramda'
import renderHelp from 'render-help'
@@ -367,7 +366,6 @@ export async function handler (opts: InstallCommandOptions): Promise<void> {
),
include,
includeDirect: include,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
fetchFullMetadata: getFetchFullMetadata(opts),
}
if (opts.resolutionOnly) {

View File

@@ -13,7 +13,7 @@ import { findWorkspacePackages } from '@pnpm/workspace.find-packages'
import { type LockfileObject } from '@pnpm/lockfile.types'
import { rebuildProjects } from '@pnpm/plugin-commands-rebuild'
import { createOrConnectStoreController, type CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
import { type IncludedDependencies, type Project, type ProjectsGraph, type ProjectRootDir, type PrepareExecutionEnv } from '@pnpm/types'
import { type IncludedDependencies, type Project, type ProjectsGraph, type ProjectRootDir } from '@pnpm/types'
import {
IgnoredBuildsError,
install,
@@ -134,7 +134,6 @@ export type InstallDepsOptions = Pick<Config,
dedupe?: boolean
workspace?: boolean
includeOnlyPackageFiles?: boolean
prepareExecutionEnv: PrepareExecutionEnv
fetchFullMetadata?: boolean
pruneLockfileImporters?: boolean
pnpmfile: string[]

View File

@@ -10,7 +10,6 @@ import { globalInfo } from '@pnpm/logger'
import { createMatcher } from '@pnpm/matcher'
import { outdatedDepsOfProjects } from '@pnpm/outdated'
import { PnpmError } from '@pnpm/error'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { type IncludedDependencies, type ProjectRootDir } from '@pnpm/types'
import enquirer from 'enquirer'
import chalk from 'chalk'
@@ -305,7 +304,6 @@ async function update (
: undefined,
updatePackageManifest: opts.save !== false,
resolutionMode: opts.save === false ? 'highest' : opts.resolutionMode,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, dependencies)
}

View File

@@ -54,9 +54,6 @@
{
"path": "../../deps/status"
},
{
"path": "../../env/plugin-commands-env"
},
{
"path": "../../exec/plugin-commands-rebuild"
},

3
pnpm-lock.yaml generated
View File

@@ -5925,9 +5925,6 @@ importers:
'@pnpm/pick-registry-for-package':
specifier: workspace:*
version: link:../../config/pick-registry-for-package
'@pnpm/plugin-commands-env':
specifier: workspace:*
version: link:../../env/plugin-commands-env
'@pnpm/plugin-commands-rebuild':
specifier: workspace:*
version: link:../../exec/plugin-commands-rebuild

View File

@@ -16,7 +16,6 @@ import { PnpmError } from '@pnpm/error'
import { filterPackagesFromDir } from '@pnpm/filter-workspace-packages'
import { globalWarn, logger } from '@pnpm/logger'
import { type ParsedCliArgs } from '@pnpm/parse-cli-args'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { finishWorkers } from '@pnpm/worker'
import chalk from 'chalk'
import path from 'path'
@@ -215,7 +214,7 @@ export async function main (inputArgv: string[]): Promise<void> {
const filterResults = await filterPackagesFromDir(wsDir, filters, {
engineStrict: config.engineStrict,
nodeVersion: config.nodeVersion ?? config.useNodeVersion,
nodeVersion: config.nodeVersion,
patterns: config.workspacePackagePatterns,
linkWorkspacePackages: !!config.linkWorkspacePackages,
prefix: process.cwd(),
@@ -291,22 +290,6 @@ export async function main (inputArgv: string[]): Promise<void> {
),
...(workspaceDir ? { workspacePrefix: workspaceDir } : {}),
})
if (config.useNodeVersion != null) {
if ('webcontainer' in process.versions) {
globalWarn('Automatic installation of different Node.js versions is not supported in WebContainer')
} else {
config.extraBinPaths = (
await prepareExecutionEnv(config, {
extraBinPaths: config.extraBinPaths,
executionEnv: {
nodeVersion: config.useNodeVersion,
},
})
).extraBinPaths
config.nodeVersion = config.useNodeVersion
}
}
let result = pnpmCmds[cmd ?? 'help'](
// TypeScript doesn't currently infer that the type of config
// is `Omit<typeof config, 'reporter'>` after the `delete config.reporter` statement

View File

@@ -6,7 +6,6 @@ import { fixtures } from '@pnpm/test-fixtures'
import { sync as rimraf } from '@zkochan/rimraf'
import execa from 'execa'
import isWindows from 'is-windows'
import { sync as writeYamlFile } from 'write-yaml-file'
import {
execPnpm,
execPnpmSync,
@@ -142,23 +141,6 @@ test('exit code from plugin is used to end the process', () => {
expect(result.stdout.toString()).toMatch(/is-positive/)
})
test('use the specified Node.js version for running scripts', async () => {
prepare({
scripts: {
test: "node -e \"require('fs').writeFileSync('version',process.version,'utf8')\"",
},
})
writeYamlFile('pnpm-workspace.yaml', {
useNodeVersion: '14.0.0',
})
await execPnpm(['run', 'test'], {
env: {
PNPM_HOME: path.resolve('pnpm_home'),
},
})
expect(fs.readFileSync('version', 'utf8')).toBe('v14.0.0')
})
test('if an unknown command is executed, run it', async () => {
prepare({})
await execPnpm(['node', '-e', "require('fs').writeFileSync('foo','','utf8')"])

View File

@@ -261,17 +261,18 @@ test('dlx read registry from .npmrc in the current directory', async () => {
expect(execResult.stdout.toString().trim()).toBe('hello from @pnpm.e2e/needs-auth')
})
test('dlx uses the node version specified by --use-node-version', async () => {
test('dlx uses the node version specified by --package=node@runtime:<version>', async () => {
prepareEmpty()
const pnpmHome = path.resolve('home')
const execResult = execPnpmSync([
'--use-node-version=20.0.0',
'--package=node@runtime:20.0.0',
'--package=@pnpm.e2e/print-node-info',
`--config.store-dir=${path.resolve('store')}`,
`--config.cache-dir=${path.resolve('cache')}`,
'dlx',
'@pnpm.e2e/print-node-info',
'print-node-info',
], {
env: {
PNPM_HOME: pnpmHome,
@@ -289,14 +290,8 @@ test('dlx uses the node version specified by --use-node-version', async () => {
throw err
}
expect(nodeInfo).toMatchObject({
versions: {
node: '20.0.0',
},
execPath: process.platform === 'win32'
? path.join(pnpmHome, 'nodejs', '20.0.0', 'node.exe')
: path.join(pnpmHome, 'nodejs', '20.0.0', 'bin', 'node'),
})
expect(nodeInfo.versions.node).toBe('20.0.0')
expect(nodeInfo.execPath).toContain(path.normalize('links/@/node/20.0.0'))
})
describeOnLinuxOnly('dlx with supportedArchitectures CLI options', () => {

View File

@@ -1,73 +0,0 @@
import { prepare, preparePackages } from '@pnpm/prepare'
import { execPnpmSync } from './utils/index.js'
test('exec with executionEnv', async () => {
prepare({
name: 'test',
version: '0.0.0',
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
})
const output = execPnpmSync(['exec', 'node', '--version']).stdout.toString().trim()
expect(output).toBe('v18.0.0')
})
test('recursive exec when some packages define different executionEnv', async () => {
preparePackages([
{
name: 'node-version-unset',
version: '0.0.0',
},
{
name: 'node-version-18',
version: '0.0.0',
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
{
name: 'node-version-20',
version: '0.0.0',
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
])
const execNodePrintVersion = (extraOptions: string[]) =>
execPnpmSync([
...extraOptions,
'--recursive',
'--reporter-hide-prefix',
'exec',
'node',
'--print',
'">>> " + require("./package.json").name + ": " + process.version',
])
.stdout
.toString()
.trim()
.split('\n')
.filter(x => x.startsWith('>>> '))
.sort()
expect(execNodePrintVersion([])).toStrictEqual([
'>>> node-version-18: v18.0.0',
'>>> node-version-20: v20.0.0',
`>>> node-version-unset: ${process.version}`,
])
expect(execNodePrintVersion(['--use-node-version=19.0.0'])).toStrictEqual([
'>>> node-version-18: v18.0.0',
'>>> node-version-20: v20.0.0',
'>>> node-version-unset: v19.0.0',
])
})

View File

@@ -1,12 +1,12 @@
import fs from 'fs'
import path from 'path'
import { prepare, preparePackages } from '@pnpm/prepare'
import { prepare } from '@pnpm/prepare'
import { type PackageManifest, type ProjectManifest } from '@pnpm/types'
import { sync as rimraf } from '@zkochan/rimraf'
import PATH from 'path-name'
import { loadJsonFileSync } from 'load-json-file'
import writeYamlFile from 'write-yaml-file'
import { execPnpm, execPnpmSync, pnpmBinLocation } from '../utils/index.js'
import { execPnpmSync, pnpmBinLocation } from '../utils/index.js'
import { getIntegrity } from '@pnpm/registry-mock'
import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest'
@@ -218,96 +218,6 @@ test('selectively allow scripts in some dependencies by --allow-build flag overl
expect(result.stdout.toString()).toContain('The following dependencies are ignored by the root project, but are allowed to be built by the current command: @pnpm.e2e/install-script-example')
})
test('use node versions specified by pnpm.executionEnv.nodeVersion in workspace packages', async () => {
const projects = preparePackages([
{
location: '.',
package: {
name: 'root',
version: '1.0.0',
private: true,
},
},
{
name: 'node-version-unset',
version: '1.0.0',
scripts: {
test: 'node -v > node-version.txt',
},
},
{
name: 'node-version-18',
version: '1.0.0',
scripts: {
test: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
{
name: 'node-version-20',
version: '1.0.0',
scripts: {
test: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
])
await writeYamlFile(path.resolve('pnpm-workspace.yaml'), {
packages: ['*'],
})
execPnpmSync(['-r', 'test'])
expect(
['node-version-unset', 'node-version-18', 'node-version-20'].map(name => {
const filePath = path.join(projects[name].dir(), 'node-version.txt')
return fs.readFileSync(filePath, 'utf-8').trim()
})
).toStrictEqual([process.version, 'v18.0.0', 'v20.0.0'])
execPnpmSync(['--config.use-node-version=19.0.0', '-r', 'test'])
expect(
['node-version-unset', 'node-version-18', 'node-version-20'].map(name => {
const filePath = path.join(projects[name].dir(), 'node-version.txt')
return fs.readFileSync(filePath, 'utf-8').trim()
})
).toStrictEqual(['v19.0.0', 'v18.0.0', 'v20.0.0'])
})
test('ignores pnpm.executionEnv specified by dependencies', async () => {
prepare({
name: 'ignores-pnpm-use-node-version-from-dependencies',
version: '1.0.0',
dependencies: {
// this package's package.json has pnpm.executionEnv.nodeVersion = '20.0.0'
'@pnpm.e2e/has-execution-env': '1.0.0',
},
pnpm: {
neverBuiltDependencies: [],
},
})
await execPnpm(['install'])
const nodeInfoFile = path.resolve('node_modules', '@pnpm.e2e', 'has-execution-env', 'node-info.json')
const nodeInfoJson = fs.readFileSync(nodeInfoFile, 'utf-8')
const nodeInfo = JSON.parse(nodeInfoJson)
// pnpm should still use system's Node.js to execute the install script despite pnpm.executionEnv.nodeVersion specified by the dependency
expect(nodeInfo).toMatchObject({
execPath: process.execPath,
versions: process.versions,
})
})
test('preinstall script does not trigger verify-deps-before-run (#8954)', async () => {
const pnpm = `${process.execPath} ${pnpmBinLocation}` // this would fail if either paths happen to contain spaces

View File

@@ -231,61 +231,3 @@ test('--reporter-hide-prefix should hide workspace prefix', async () => {
expect(output).toContain('2')
expect(output).not.toContain('script2: 2')
})
test('recursive run when some packages define different node versions', async () => {
preparePackages([
{
name: 'node-version-unset',
scripts: {
'print-node-version': 'node -v',
},
},
{
name: 'node-version-18',
scripts: {
'print-node-version': 'node -v',
},
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
{
name: 'node-version-20',
scripts: {
'print-node-version': 'node -v',
},
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
])
const runPrintNodeVersion = (args: string[]) =>
execPnpmSync(args)
.stdout
.toString()
.trim()
.split('\n')
.filter(x => /print-node-version.*v\d+\.\d+\.\d+/.test(x))
.sort()
expect(
runPrintNodeVersion(['run', '-r', 'print-node-version'])
).toStrictEqual([
'node-version-18 print-node-version: v18.0.0',
'node-version-20 print-node-version: v20.0.0',
`node-version-unset print-node-version: ${process.version}`,
])
expect(
runPrintNodeVersion(['run', '-r', '--use-node-version=19.0.0', 'print-node-version'])
).toStrictEqual([
'node-version-18 print-node-version: v18.0.0',
'node-version-20 print-node-version: v20.0.0',
'node-version-unset print-node-version: v19.0.0',
])
})

View File

@@ -1,8 +1,6 @@
import path from 'path'
import url from 'url'
import normalizePath from 'normalize-path'
import { pick } from 'ramda'
import { USEFUL_NON_ROOT_PNPM_FIELDS } from '@pnpm/constants'
import * as dp from '@pnpm/dependency-path'
import {
type DirectoryResolution,
@@ -139,7 +137,6 @@ export function createDeployFiles ({
optionalDependencies: targetSnapshot.optionalDependencies,
pnpm: {
...rootProjectManifest?.pnpm,
...pick(USEFUL_NON_ROOT_PNPM_FIELDS, selectedProjectManifest.pnpm ?? {}),
overrides: undefined, // the effects of the overrides should already be part of the package snapshots
patchedDependencies: undefined,
packageExtensions: undefined, // the effects of the package extensions should already be part of the package snapshots

View File

@@ -282,9 +282,6 @@ test('the deploy manifest should inherit some fields from the pnpm object from t
overrides: {
'is-positive': '2.0.0',
},
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
'project-0': {
@@ -299,9 +296,6 @@ test('the deploy manifest should inherit some fields from the pnpm object from t
overrides: {
'is-positive': '=1.0.0',
},
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
}
@@ -351,7 +345,6 @@ test('the deploy manifest should inherit some fields from the pnpm object from t
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest.pnpm).toStrictEqual({
onlyBuiltDependencies: preparedManifests.root.pnpm!.onlyBuiltDependencies,
executionEnv: preparedManifests['project-0'].pnpm!.executionEnv,
} as ProjectManifest['pnpm'])
expect(readPackageJson('deploy/node_modules/is-positive/')).toHaveProperty(['version'], preparedManifests.root.pnpm!.overrides!['is-positive'])

View File

@@ -9,7 +9,6 @@ import { runNpm } from '@pnpm/run-npm'
import { type ProjectManifest } from '@pnpm/types'
import { getCurrentBranch, isGitRepo, isRemoteHistoryClean, isWorkingTreeClean } from '@pnpm/git-utils'
import { loadToken } from '@pnpm/network.auth-header'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import enquirer from 'enquirer'
import rimraf from '@zkochan/rimraf'
import { pick } from 'ramda'
@@ -252,7 +251,6 @@ Do you want to continue?`,
rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')),
stdio: 'inherit',
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
})
const { manifest } = await readProjectManifest(dir, opts)
// Unfortunately, we cannot support postpack at the moment

View File

@@ -305,57 +305,3 @@ test('when publish some package throws an error, exit code should be non-zero',
expect(result?.exitCode).toBe(1)
})
test('recursive publish runs script with Node.js version specified by pnpm.executionEnv.nodeVersion', async () => {
preparePackages([
{
name: 'test-publish-node-version-unset',
version: '1.0.0',
scripts: {
prepublishOnly: 'node -v > node-version.txt',
},
},
{
name: 'test-publish-node-version-18',
version: '1.0.0',
scripts: {
prepublishOnly: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
{
name: 'test-publish-node-version-20',
version: '1.0.0',
scripts: {
prepublishOnly: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
])
fs.writeFileSync('.npmrc', CREDENTIALS, 'utf8')
await publish.handler({
...DEFAULT_OPTS,
...await filterPackagesFromDir(process.cwd(), []),
dir: process.cwd(),
dryRun: true,
pnpmHomeDir: process.cwd(),
recursive: true,
}, [])
expect(
['unset', '18', '20']
.map(suffix => `test-publish-node-version-${suffix}`)
.map(name => path.resolve(name, 'node-version.txt'))
.map(nodeVersionFile => fs.readFileSync(nodeVersionFile, 'utf-8').trim())
).toStrictEqual([process.version, 'v18.0.0', 'v20.0.0'])
})

View File

@@ -1,5 +1,4 @@
import { packageIsInstallable } from '@pnpm/cli-utils'
import { USEFUL_NON_ROOT_PNPM_FIELDS } from '@pnpm/constants'
import { type ProjectManifest, type Project, type SupportedArchitectures } from '@pnpm/types'
import { lexCompare } from '@pnpm/util.lex-comparator'
import { findPackages } from '@pnpm/fs.find-packages'
@@ -64,9 +63,6 @@ export async function findWorkspacePackagesNoCheck (workspaceRoot: string, opts?
const uselessNonRootManifestFields: Array<keyof ProjectManifest> = ['resolutions']
type ProjectManifestPnpm = Required<ProjectManifest>['pnpm']
const usefulNonRootPnpmFields: ReadonlyArray<keyof ProjectManifestPnpm> = USEFUL_NON_ROOT_PNPM_FIELDS
function checkNonRootProjectManifest ({ manifest, rootDir }: Project): void {
const warn = printNonRootFieldWarning.bind(null, rootDir)
for (const field of uselessNonRootManifestFields) {
@@ -75,9 +71,7 @@ function checkNonRootProjectManifest ({ manifest, rootDir }: Project): void {
}
}
for (const field in manifest.pnpm) {
if (!usefulNonRootPnpmFields.includes(field as keyof ProjectManifestPnpm)) {
warn(`pnpm.${field}`)
}
warn(`pnpm.${field}`)
}
}

View File

@@ -2,8 +2,7 @@
"name": "bar",
"version": "1.0.0",
"pnpm": {
"overrides": {},
"executionEnv": {}
"overrides": {}
},
"resolutions": {}
}

View File

@@ -2,7 +2,6 @@
"name": "foo",
"version": "1.0.0",
"pnpm": {
"overrides": {},
"executionEnv": {}
"overrides": {}
}
}