feat: per-package node version (#8277)

close #6720
This commit is contained in:
Khải
2024-07-13 19:08:57 +07:00
committed by GitHub
parent a2e9bdcc02
commit 0ef168b77d
35 changed files with 446 additions and 53 deletions

View File

@@ -0,0 +1,12 @@
---
"@pnpm/plugin-commands-installation": major
"@pnpm/plugin-commands-publishing": major
"@pnpm/plugin-commands-script-runners": major
"@pnpm/plugin-commands-env": minor
"@pnpm/core": minor
"@pnpm/lifecycle": minor
"@pnpm/types": minor
"pnpm": minor
---
Support specifying node version (via `pnpm.executionEnv.nodeVersion` in `package.json`) for running lifecycle scripts per each package in a workspace [#6720](https://github.com/pnpm/pnpm/issues/6720).

View File

@@ -41,6 +41,7 @@
"@pnpm/node.resolver": "workspace:*",
"@pnpm/remove-bins": "workspace:*",
"@pnpm/store-path": "workspace:*",
"@pnpm/types": "workspace:*",
"@zkochan/cmd-shim": "catalog:",
"@zkochan/rimraf": "catalog:",
"graceful-fs": "catalog:",

View File

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

View File

@@ -6,6 +6,7 @@ import { createFetchFromRegistry, type FetchFromRegistry } from '@pnpm/fetch'
import { globalInfo } from '@pnpm/logger'
import { fetchNode } from '@pnpm/node.fetcher'
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 { getNodeMirror } from './getNodeMirror'
@@ -36,6 +37,25 @@ export type NvmNodeCommandOptions = Pick<Config,
remote?: boolean
}
const nodeFetchPromises: Record<string, Promise<string>> = {}
export async function prepareExecutionEnv (config: NvmNodeCommandOptions, { extraBinPaths, executionEnv }: PrepareExecutionEnvOptions): Promise<PrepareExecutionEnvResult> {
if (!executionEnv?.nodeVersion) 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 ?? []],
}
}
export async function getNodeBinDir (opts: NvmNodeCommandOptions): Promise<string> {
const fetch = createFetchFromRegistry(opts)
const nodesDir = getNodeVersionsBaseDir(opts.pnpmHomeDir)

View File

@@ -2,7 +2,7 @@ import AdmZip from 'adm-zip'
import { Response } from 'node-fetch'
import path from 'path'
import { Readable } from 'stream'
import { node } from '@pnpm/plugin-commands-env'
import { getNodeDir, getNodeBinDir, getNodeVersionsBaseDir, type NvmNodeCommandOptions } from '../lib/node'
import { tempDir } from '@pnpm/prepare'
const fetchMock = jest.fn(async (url: string) => {
@@ -28,7 +28,7 @@ beforeEach(() => {
})
test('check API (placeholder test)', async () => {
expect(typeof node.getNodeDir).toBe('function')
expect(typeof getNodeDir).toBe('function')
})
// TODO: unskip. The mock function should return a valid tarball
@@ -37,7 +37,7 @@ test.skip('install Node uses node-mirror:release option', async () => {
const configDir = path.resolve('config')
const nodeMirrorRelease = 'https://pnpm-node-mirror-test.localhost/download/release'
const opts: node.NvmNodeCommandOptions = {
const opts: NvmNodeCommandOptions = {
bin: process.cwd(),
configDir,
global: true,
@@ -48,7 +48,7 @@ test.skip('install Node uses node-mirror:release option', async () => {
useNodeVersion: '16.4.0',
}
await node.getNodeBinDir(opts)
await getNodeBinDir(opts)
for (const call of fetchMock.mock.calls) {
expect(call[0]).toMatch(nodeMirrorRelease)
@@ -60,7 +60,7 @@ test.skip('install an rc version of Node.js', async () => {
tempDir()
const configDir = path.resolve('config')
const opts: node.NvmNodeCommandOptions = {
const opts: NvmNodeCommandOptions = {
bin: process.cwd(),
configDir,
global: true,
@@ -69,7 +69,7 @@ test.skip('install an rc version of Node.js', async () => {
useNodeVersion: 'rc/18.0.0-rc.3',
}
await node.getNodeBinDir(opts)
await getNodeBinDir(opts)
const platform = process.platform === 'win32' ? 'win' : process.platform
const arch = process.arch
@@ -80,7 +80,7 @@ test.skip('install an rc version of Node.js', async () => {
})
test('get node version base dir', async () => {
expect(typeof node.getNodeVersionsBaseDir).toBe('function')
const versionDir = node.getNodeVersionsBaseDir(process.cwd())
expect(typeof getNodeVersionsBaseDir).toBe('function')
const versionDir = getNodeVersionsBaseDir(process.cwd())
expect(versionDir).toBe(path.resolve(process.cwd(), 'nodejs'))
})

View File

@@ -24,6 +24,9 @@
{
"path": "../../packages/error"
},
{
"path": "../../packages/types"
},
{
"path": "../../pkg-manager/remove-bins"
},

View File

@@ -1,7 +1,7 @@
import { lifecycleLogger } from '@pnpm/core-loggers'
import { globalWarn } from '@pnpm/logger'
import lifecycle from '@pnpm/npm-lifecycle'
import { type DependencyManifest, type ProjectManifest } from '@pnpm/types'
import { type DependencyManifest, type ProjectManifest, type PrepareExecutionEnv } from '@pnpm/types'
import { PnpmError } from '@pnpm/error'
import { existsSync } from 'fs'
import isWindows from 'is-windows'
@@ -24,6 +24,7 @@ export interface RunLifecycleHookOptions {
shellEmulator?: boolean
stdio?: string
unsafePerm: boolean
prepareExecutionEnv?: PrepareExecutionEnv
}
export async function runLifecycleHook (
@@ -94,13 +95,17 @@ Please unset the script-shell 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: opts.extraBinPaths ?? [],
extraBinPaths,
extraEnv: {
...opts.extraEnv,
INIT_CWD: opts.initCwd ?? process.cwd(),

View File

@@ -57,7 +57,7 @@ export async function runLifecycleHooksConcurrently (
logger.warn({ message, prefix: rootDir })
},
})
const runLifecycleHookOpts = {
const runLifecycleHookOpts: RunLifecycleHookOptions = {
...opts,
depPath: rootDir,
pkgRoot: rootDir,

View File

@@ -6,7 +6,6 @@ import { OUTPUT_OPTIONS } from '@pnpm/common-cli-options-help'
import { type Config, types } from '@pnpm/config'
import { createBase32Hash } from '@pnpm/crypto.base32-hash'
import { PnpmError } from '@pnpm/error'
import { node } from '@pnpm/plugin-commands-env'
import { add } from '@pnpm/plugin-commands-installation'
import { readPackageJsonFromDir } from '@pnpm/read-package-json'
import { getBinsFromPackageManifest } from '@pnpm/package-bins'
@@ -65,7 +64,7 @@ export function help (): string {
export type DlxCommandOptions = {
package?: string[]
shellMode?: boolean
} & Pick<Config, 'reporter' | 'userAgent' | 'cacheDir' | 'dlxCacheMaxAge' | 'useNodeVersion' > & add.AddCommandOptions
} & Pick<Config, 'extraBinPaths' | 'reporter' | 'userAgent' | 'cacheDir' | 'dlxCacheMaxAge' | 'useNodeVersion' > & add.AddCommandOptions
export async function handler (
opts: DlxCommandOptions,
@@ -95,12 +94,10 @@ export async function handler (
}
const modulesDir = path.join(cacheLink, 'node_modules')
const binsDir = path.join(modulesDir, '.bin')
const prependPaths = [binsDir]
if (opts.useNodeVersion) {
const nodePath = await node.getNodeBinDir(opts)
prependPaths.push(nodePath)
}
const env = makeEnv({ userAgent: opts.userAgent, prependPaths })
const env = makeEnv({
userAgent: opts.userAgent,
prependPaths: [binsDir, ...opts.extraBinPaths],
})
const binName = opts.package
? command
: await getBinName(modulesDir, await getPkgName(cacheLink))

View File

@@ -6,6 +6,7 @@ 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 } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
@@ -155,7 +156,7 @@ For options that may be used with `-r`, see "pnpm help recursive"',
export type RunOpts =
& Omit<RecursiveRunOpts, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>
& { recursive?: boolean }
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts' | 'userAgent' | 'extraEnv' | 'nodeOptions'>
& Pick<Config, 'bin' | 'dir' | 'engineStrict' | 'extraBinPaths' | 'pnpmHomeDir' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts' | 'userAgent' | 'extraEnv' | 'nodeOptions'>
& (
& { recursive?: false }
& Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
@@ -250,6 +251,10 @@ 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.
}
const executionEnv = manifest.pnpm?.executionEnv
if (executionEnv != null) {
lifecycleOpts.extraBinPaths = (await prepareExecutionEnv(opts, { executionEnv })).extraBinPaths
}
const existsPnp = existsInDir.bind(null, '.pnp.cjs')
const pnpPath = (opts.workspaceDir && existsPnp(opts.workspaceDir)) ?? existsPnp(dir)
if (pnpPath) {

View File

@@ -3,6 +3,7 @@ import path from 'path'
import util from 'util'
import { throwOnCommandFail } from '@pnpm/cli-utils'
import { type Config } from '@pnpm/config'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { PnpmError } from '@pnpm/error'
import {
makeNodeRequireOption,
@@ -20,8 +21,10 @@ import { tryBuildRegExpFromCommand } from './regexpCommand'
import { type PackageScripts, type ProjectRootDir } from '@pnpm/types'
export type RecursiveRunOpts = Pick<Config,
| 'bin'
| 'enablePrePostScripts'
| 'unsafePerm'
| 'pnpmHomeDir'
| 'rawConfig'
| 'rootProjectManifest'
| 'scriptsPrependNodePath'
@@ -121,6 +124,10 @@ 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

@@ -29,18 +29,22 @@ test('pnpm run: returns correct exit code', async () => {
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['exit0'])
let err!: Error & { errno: number }
try {
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['exit1'])
} catch (_err: any) { // eslint-disable-line
@@ -59,10 +63,12 @@ test('pnpm run --no-bail never fails', async () => {
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await run.handler({
bin: 'node_modules/.bin',
bail: false,
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['exit1'])
@@ -84,9 +90,11 @@ test('run: pass the args to the command that is specified in the build script',
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
@@ -106,9 +114,11 @@ test('run: pass the args to the command that is specified in the build script of
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
@@ -128,9 +138,11 @@ test('test: pass the args to the command that is specified in the build script o
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await testCommand.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['arg', '--flag=true', '--help', '-h'])
@@ -150,9 +162,11 @@ test('run start: pass the args to the command that is specified in the build scr
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['start', 'arg', '--flag=true', '--help', '-h'])
@@ -172,9 +186,11 @@ test('run stop: pass the args to the command that is specified in the build scri
fs.writeFileSync('recordArgs.js', RECORD_ARGS_FILE, 'utf8')
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['stop', 'arg', '--flag=true', '--help', '-h'])
@@ -202,9 +218,11 @@ test('restart: run stop, restart and start', async () => {
})
await restart.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, [])
@@ -235,10 +253,12 @@ test('restart: run stop, restart and start and all the pre/post scripts', async
})
await restart.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
enablePrePostScripts: true,
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, [])
@@ -264,9 +284,11 @@ test('"pnpm run" prints the list of available commands', async () => {
})
const output = await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, [])
@@ -310,9 +332,11 @@ test('"pnpm run" prints the list of available commands, including commands of th
process.chdir('foo')
const output = await run.handler({
allProjects,
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
selectedProjectsGraph,
workspaceDir,
@@ -336,10 +360,12 @@ Commands of the root workspace project (to run them, use "pnpm -w run"):
{
process.chdir('..')
const output = await run.handler({
bin: 'node_modules/.bin',
allProjects,
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
selectedProjectsGraph,
workspaceDir,
@@ -360,10 +386,12 @@ test('pnpm run does not fail with --if-present even if the wanted script is not
prepare({})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
ifPresent: true,
pnpmHomeDir: '',
rawConfig: {},
}, ['build'])
})
@@ -427,9 +455,11 @@ test('scripts work with PnP', async () => {
},
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['foo'])
@@ -461,9 +491,11 @@ skipOnWindows('pnpm run with custom shell', async () => {
])
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
scriptShell: path.resolve('node_modules/.bin/shell-mock'),
}, ['build'])
@@ -490,9 +522,11 @@ onlyOnWindows('pnpm shows error if script-shell is .cmd', async () => {
async function runScript () {
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
scriptShell: path.resolve('node_modules/.bin/shell-mock.cmd'),
}, ['build'])
@@ -518,9 +552,11 @@ test('pnpm run with RegExp script selector should work', async () => {
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['/^(lint|build):.*/'])
@@ -542,9 +578,11 @@ test('pnpm run with RegExp script selector should work also for pre/post script'
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
enablePrePostScripts: true,
}, ['/build:.*/'])
@@ -565,9 +603,11 @@ test('pnpm run with RegExp script selector should work parallel as a default beh
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
}, ['/build:.*/'])
@@ -589,9 +629,11 @@ test('pnpm run with RegExp script selector should work sequentially with --works
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
workspaceConcurrency: 1,
}, ['/build:.*/'])
@@ -616,9 +658,11 @@ test('pnpm run with RegExp script selector with flag should throw error', async
let err!: Error
try {
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
workspaceConcurrency: 1,
}, ['/build:.*/i'])
@@ -637,9 +681,11 @@ test('pnpm run with slightly incorrect command suggests correct one', async () =
// cspell:ignore buil
await expect(run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
workspaceConcurrency: 1,
}, ['buil'])).rejects.toEqual(expect.objectContaining({
@@ -656,11 +702,54 @@ test('pnpm run with custom node-options', async () => {
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: '',
rawConfig: {},
nodeOptions: '--max-old-space-size=1200',
workspaceConcurrency: 1,
}, ['build'])
})
test('pnpm run without node version', async () => {
prepare({
scripts: {
'assert-node-version': `node -e "assert.equal(process.version, '${process.version}')"`,
},
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: process.cwd(),
rawConfig: {},
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\')"',
},
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
})
await run.handler({
bin: 'node_modules/.bin',
dir: process.cwd(),
extraBinPaths: [],
extraEnv: {},
pnpmHomeDir: process.cwd(),
rawConfig: {},
workspaceConcurrency: 1,
}, ['assert-node-version'])
})

View File

@@ -10,6 +10,7 @@ export const DEFAULT_OPTS = {
original: [],
},
bail: false,
bin: 'node_modules/.bin',
ca: undefined,
cacheDir: '../cache',
cert: undefined,
@@ -66,6 +67,7 @@ export const DLX_DEFAULT_OPTS = {
bin: 'node_modules/.bin',
cacheDir: path.join(tmp, 'cache'),
extraEnv: {},
extraBinPaths: [],
cliOptions: {},
dlxCacheMaxAge: Infinity,
include: {

14
packages/types/src/env.ts Normal file
View File

@@ -0,0 +1,14 @@
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,3 +1,4 @@
export * from './env'
export * from './misc'
export * from './options'
export * from './package'

View File

@@ -1,3 +1,5 @@
import { type ExecutionEnv } from './env'
export type Dependencies = Record<string, string>
export type PackageBin = string | { [commandName: string]: string }
@@ -146,6 +148,7 @@ export interface ProjectManifest extends BaseManifest {
}
requiredScripts?: string[]
supportedArchitectures?: SupportedArchitectures
executionEnv?: ExecutionEnv
}
private?: boolean
resolutions?: Record<string, string>

View File

@@ -15,6 +15,7 @@ import {
type PackageExtension,
type ReadPackageHook,
type Registries,
type PrepareExecutionEnv,
} from '@pnpm/types'
import { pnpmPkgJson } from '../pnpmPkgJson'
import { type ReporterFunction } from '../types'
@@ -151,6 +152,7 @@ export interface StrictInstallOptions {
hoistWorkspacePackages?: boolean
virtualStoreDirMaxLength: number
peersSuffixMaxLength: number
prepareExecutionEnv?: PrepareExecutionEnv
}
export type InstallOptions =

View File

@@ -311,6 +311,7 @@ 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

@@ -76,6 +76,7 @@
"@pnpm/outdated": "workspace:*",
"@pnpm/package-store": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/plugin-commands-env": "workspace:*",
"@pnpm/plugin-commands-rebuild": "workspace:*",
"@pnpm/pnpmfile": "workspace:*",
"@pnpm/read-project-manifest": "workspace:*",

View File

@@ -2,6 +2,7 @@ import { docsUrl } from '@pnpm/cli-utils'
import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { types as allTypes } from '@pnpm/config'
import { PnpmError } from '@pnpm/error'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import pick from 'ramda/src/pick'
import renderHelp from 'render-help'
import { type InstallCommandOptions } from './install'
@@ -207,5 +208,6 @@ export async function handler (
...opts,
include,
includeDirect: include,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, params)
}

View File

@@ -1,6 +1,7 @@
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'
import { installDeps } from './installDeps'
@@ -63,5 +64,6 @@ export async function handler (opts: DedupeCommandOptions): Promise<void> {
include,
includeDirect: include,
lockfileCheck: opts.check ? dedupeDiffCheck : undefined,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, [])
}

View File

@@ -2,6 +2,7 @@ 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 { isCI } from 'ci-info'
import pick from 'ramda/src/pick'
@@ -329,6 +330,7 @@ export async function handler (opts: InstallCommandOptions): Promise<void> {
typeof opts.rawLocalConfig['prefer-frozen-lockfile'] === 'undefined',
include,
includeDirect: include,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}
if (opts.resolutionOnly) {
installDepsOptions.lockfileOnly = true

View File

@@ -12,7 +12,7 @@ import { findWorkspacePackages } from '@pnpm/workspace.find-packages'
import { type Lockfile } 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 } from '@pnpm/types'
import { type IncludedDependencies, type Project, type ProjectsGraph, type ProjectRootDir, type PrepareExecutionEnv } from '@pnpm/types'
import {
install,
mutateModulesInSingleProject,
@@ -115,6 +115,7 @@ export type InstallDepsOptions = Pick<Config,
dedupe?: boolean
workspace?: boolean
includeOnlyPackageFiles?: boolean
prepareExecutionEnv: PrepareExecutionEnv
} & Partial<Pick<Config, 'pnpmHomeDir'>>
export async function installDeps (

View File

@@ -10,6 +10,7 @@ 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 { prompt } from 'enquirer'
import chalk from 'chalk'
@@ -304,6 +305,7 @@ async function update (
: undefined,
updatePackageManifest: opts.save !== false,
resolutionMode: opts.save === false ? 'highest' : opts.resolutionMode,
prepareExecutionEnv: prepareExecutionEnv.bind(null, opts),
}, dependencies)
}

View File

@@ -39,6 +39,9 @@
{
"path": "../../dedupe/check"
},
{
"path": "../../env/plugin-commands-env"
},
{
"path": "../../exec/plugin-commands-rebuild"
},

53
pnpm-lock.yaml generated
View File

@@ -55,8 +55,8 @@ catalogs:
specifier: 0.0.0
version: 0.0.0
'@pnpm/registry-mock':
specifier: 3.34.0
version: 3.34.0
specifier: 3.36.1
version: 3.36.1
'@pnpm/semver-diff':
specifier: ^1.1.0
version: 1.1.0
@@ -709,7 +709,7 @@ importers:
version: 1.0.0
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/tsconfig':
specifier: workspace:*
version: link:__utils__/tsconfig
@@ -821,7 +821,7 @@ importers:
version: link:../../pkg-manager/modules-yaml
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
@@ -855,7 +855,7 @@ importers:
dependencies:
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/store.cafs':
specifier: workspace:*
version: link:../../store/cafs
@@ -1711,6 +1711,9 @@ importers:
'@pnpm/store-path':
specifier: workspace:*
version: link:../../store/store-path
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
'@zkochan/cmd-shim':
specifier: 'catalog:'
version: 6.0.0
@@ -2018,7 +2021,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -2148,7 +2151,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-ipc-server':
specifier: workspace:*
version: link:../../__utils__/test-ipc-server
@@ -3684,7 +3687,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -3962,7 +3965,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/store-path':
specifier: workspace:*
version: link:../../store/store-path
@@ -4247,7 +4250,7 @@ importers:
version: link:../read-projects-context
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/store-path':
specifier: workspace:*
version: link:../../store/store-path
@@ -4607,7 +4610,7 @@ importers:
version: 'link:'
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -4701,6 +4704,9 @@ importers:
'@pnpm/parse-wanted-dependency':
specifier: workspace:*
version: link:../../packages/parse-wanted-dependency
'@pnpm/plugin-commands-env':
specifier: workspace:*
version: link:../../env/plugin-commands-env
'@pnpm/plugin-commands-rebuild':
specifier: workspace:*
version: link:../../exec/plugin-commands-rebuild
@@ -4800,7 +4806,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -5393,7 +5399,7 @@ importers:
version: link:../pkg-manifest/read-project-manifest
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/run-npm':
specifier: workspace:*
version: link:../exec/run-npm
@@ -5659,7 +5665,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/workspace.filter-packages-from-dir':
specifier: workspace:*
version: link:../../workspace/filter-packages-from-dir
@@ -5708,6 +5714,9 @@ 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/resolver-base':
specifier: workspace:*
version: link:../../resolving/resolver-base
@@ -5765,7 +5774,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-ipc-server':
specifier: workspace:*
version: link:../../__utils__/test-ipc-server
@@ -6334,7 +6343,7 @@ importers:
version: link:../../pkg-manifest/read-package-json
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -6401,7 +6410,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/workspace.filter-packages-from-dir':
specifier: workspace:*
version: link:../../workspace/filter-packages-from-dir
@@ -6492,7 +6501,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
@@ -6830,7 +6839,7 @@ importers:
version: link:../../__utils__/prepare
'@pnpm/registry-mock':
specifier: 'catalog:'
version: 3.34.0(encoding@0.1.13)(typanion@3.14.0)
version: 3.36.1(encoding@0.1.13)(typanion@3.14.0)
'@types/archy':
specifier: 'catalog:'
version: 0.0.33
@@ -8411,8 +8420,8 @@ packages:
resolution: {integrity: sha512-MDXuQpYFbabSXzAnqP7VIQqBx5Z1fzOhzB/3YmIXJ+tE7Wue//IR3itMSYlWeaFLo1G5PCJklM2zBdvggRw1nw==}
engines: {node: '>=16.14'}
'@pnpm/registry-mock@3.34.0':
resolution: {integrity: sha512-/wANbfGLun8u/ojrvzDJEJjROBcB/ZQMhrufmvV96Yk0VjR2RQ9dXoNrw0BlPyNRYUgG8Hj3Xl9RdZGQW31DYA==}
'@pnpm/registry-mock@3.36.1':
resolution: {integrity: sha512-WYhOXo3y+YmgzigBeFhcW5VPdUZGfFp/7Ilz0NQ8Vi3xweL8glc6/rDTqvcr0VpckBulq/WKAB3wivoAGnS/tA==}
engines: {node: '>=10.13'}
hasBin: true
@@ -15236,7 +15245,7 @@ snapshots:
sort-keys: 4.2.0
strip-bom: 4.0.0
'@pnpm/registry-mock@3.34.0(encoding@0.1.13)(typanion@3.14.0)':
'@pnpm/registry-mock@3.36.1(encoding@0.1.13)(typanion@3.14.0)':
dependencies:
anonymous-npm-registry-client: 0.2.0
execa: 5.1.1

View File

@@ -51,7 +51,7 @@ catalog:
"@pnpm/npm-package-arg": ^1.0.0
"@pnpm/os.env.path-extender": ^2.0.0
"@pnpm/patch-package": 0.0.0
"@pnpm/registry-mock": 3.34.0
"@pnpm/registry-mock": 3.36.1
"@pnpm/semver-diff": ^1.1.0
"@pnpm/tabtab": ^0.5.4
"@pnpm/util.lex-comparator": 3.0.0

View File

@@ -12,7 +12,7 @@ import { executionTimeLogger, scopeLogger } from '@pnpm/core-loggers'
import { filterPackagesFromDir } from '@pnpm/filter-workspace-packages'
import { globalWarn, logger } from '@pnpm/logger'
import { type ParsedCliArgs } from '@pnpm/parse-cli-args'
import { node } from '@pnpm/plugin-commands-env'
import { prepareExecutionEnv } from '@pnpm/plugin-commands-env'
import { finishWorkers } from '@pnpm/worker'
import chalk from 'chalk'
import { checkForUpdates } from './checkForUpdates'
@@ -274,8 +274,9 @@ export async function main (inputArgv: string[]): Promise<void> {
if ('webcontainer' in process.versions) {
globalWarn('Automatic installation of different Node.js versions is not supported in WebContainer')
} else {
const nodePath = await node.getNodeBinDir(config)
config.extraBinPaths.push(nodePath)
config.extraBinPaths = (
await prepareExecutionEnv(config, { executionEnv: { nodeVersion: config.useNodeVersion } })
).extraBinPaths
config.nodeVersion = config.useNodeVersion
}
}

View File

@@ -1,11 +1,12 @@
import fs from 'fs'
import path from 'path'
import { prepare } from '@pnpm/prepare'
import { prepare, preparePackages } from '@pnpm/prepare'
import { type PackageManifest } from '@pnpm/types'
import { sync as rimraf } from '@zkochan/rimraf'
import PATH from 'path-name'
import loadJsonFile from 'load-json-file'
import { execPnpmSync } from '../utils'
import writeYamlFile from 'write-yaml-file'
import { execPnpm, execPnpmSync } from '../utils'
const pkgRoot = path.join(__dirname, '..', '..')
const pnpmPkg = loadJsonFile.sync<PackageManifest>(path.join(pkgRoot, 'package.json'))
@@ -163,3 +164,90 @@ test('selectively allow scripts in some dependencies by onlyBuiltDependenciesFil
expect(fs.existsSync('node_modules/@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-postinstall.js')).toBeFalsy()
expect(fs.existsSync('node_modules/@pnpm.e2e/install-script-example/generated-by-install.js')).toBeTruthy()
})
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-undefined',
version: '1.0.0',
scripts: {
install: 'node -v > node-version.txt',
},
},
{
name: 'node-version-18',
version: '1.0.0',
scripts: {
install: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '18.0.0',
},
},
},
{
name: 'node-version-20',
version: '1.0.0',
scripts: {
install: 'node -v > node-version.txt',
},
pnpm: {
executionEnv: {
nodeVersion: '20.0.0',
},
},
},
])
await writeYamlFile(path.resolve('pnpm-workspace.yaml'), {
packages: ['*'],
})
execPnpmSync(['install'])
expect(
['node-version-undefined', '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', 'install'])
expect(
['node-version-undefined', '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',
},
})
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,
})
})

View File

@@ -232,3 +232,61 @@ 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-undefined',
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 => /v[0-9]+\.[0-9]+\.[0-9]+/.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-undefined 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-undefined print-node-version: v19.0.0',
])
})

View File

@@ -66,6 +66,7 @@
"@pnpm/network.auth-header": "workspace:*",
"@pnpm/package-bins": "workspace:*",
"@pnpm/pick-registry-for-package": "workspace:*",
"@pnpm/plugin-commands-env": "workspace:*",
"@pnpm/resolver-base": "workspace:*",
"@pnpm/run-npm": "workspace:*",
"@pnpm/sort-packages": "workspace:*",

View File

@@ -9,6 +9,7 @@ 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 { prompt } from 'enquirer'
import rimraf from '@zkochan/rimraf'
import pick from 'ramda/src/pick'
@@ -119,7 +120,7 @@ export async function handler (
engineStrict?: boolean
recursive?: boolean
workspaceDir?: string
} & Pick<Config, 'allProjects' | 'gitChecks' | 'ignoreScripts' | 'publishBranch' | 'embedReadme'>,
} & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme'>,
params: string[]
): Promise<{ exitCode?: number } | undefined> {
const result = await publish(opts, params)
@@ -140,7 +141,7 @@ export async function publish (
engineStrict?: boolean
recursive?: boolean
workspaceDir?: string
} & Pick<Config, 'allProjects' | 'gitChecks' | 'ignoreScripts' | 'publishBranch' | 'embedReadme' | 'packGzipLevel'>,
} & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme' | 'packGzipLevel'>,
params: string[]
): Promise<PublishResult> {
if (opts.gitChecks !== false && await isGitRepo()) {
@@ -220,6 +221,7 @@ 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

@@ -12,9 +12,11 @@ import writeJsonFile from 'write-json-file'
import { publish } from './publish'
export type PublishRecursiveOpts = Required<Pick<Config,
| 'bin'
| 'cacheDir'
| 'cliOptions'
| 'dir'
| 'pnpmHomeDir'
| 'rawConfig'
| 'registries'
| 'workspaceDir'

View File

@@ -310,3 +310,57 @@ 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-undefined',
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(
['undefined', '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

@@ -33,6 +33,9 @@
{
"path": "../../config/pick-registry-for-package"
},
{
"path": "../../env/plugin-commands-env"
},
{
"path": "../../exec/lifecycle"
},