feat: install js runtime as prod dependency (#10141)

This commit is contained in:
Zoltan Kochan
2025-10-31 17:12:50 +01:00
committed by GitHub
parent d4bf2d0e60
commit efb48dcab5
22 changed files with 197 additions and 59 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/manifest-utils": minor
---
Added convertEnginesRuntimeToDependencies.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/build-modules": major
---
Replaced fetchingBundledManifest with fetching.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/types": major
---
DevEngineDependency renamed to EngineDependency.

View File

@@ -0,0 +1,27 @@
---
"@pnpm/read-project-manifest": minor
"@pnpm/resolve-dependencies": minor
"@pnpm/link-bins": minor
"@pnpm/types": minor
"pnpm": minor
---
**Node.js Runtime Installation for Dependencies.** Added support for automatic Node.js runtime installation for dependencies. pnpm will now install the Node.js version required by a dependency if that dependency declares a Node.js runtime in the "engines" field. For example:
```json
{
"engines": {
"runtime": {
"name": "node",
"version": "^24.11.0",
"onFail": "download"
}
}
}
```
If the package with the Node.js runtime dependency is a CLI app, pnpm will bind the CLI app to the required Node.js version. This ensures that, regardless of the globally installed Node.js instance, the CLI will use the compatible version of Node.js.
If the package has a `postinstall` script, that script will be executed using the specified Node.js version.
Related PR: [#10141](https://github.com/pnpm/pnpm/pull/10141)

View File

@@ -0,0 +1,5 @@
---
"@pnpm/manifest-utils": major
---
Added `@pnpm/logger` to peer dependencies.

View File

@@ -1,6 +1,7 @@
import { graphSequencer } from '@pnpm/deps.graph-sequencer' import { graphSequencer } from '@pnpm/deps.graph-sequencer'
import { type PatchInfo } from '@pnpm/patching.types' import { type PatchInfo } from '@pnpm/patching.types'
import { type PkgIdWithPatchHash, type DepPath, type PackageManifest } from '@pnpm/types' import { type PkgRequestFetchResult } from '@pnpm/store-controller-types'
import { type PkgIdWithPatchHash, type DepPath } from '@pnpm/types'
import { filter } from 'ramda' import { filter } from 'ramda'
export interface DependenciesGraphNode<T extends string> { export interface DependenciesGraphNode<T extends string> {
@@ -10,7 +11,7 @@ export interface DependenciesGraphNode<T extends string> {
name: string name: string
version: string version: string
dir: string dir: string
fetchingBundledManifest?: () => Promise<PackageManifest | undefined> fetching?: () => Promise<PkgRequestFetchResult>
filesIndexFile?: string filesIndexFile?: string
hasBin: boolean hasBin: boolean
hasBundledDependencies: boolean hasBundledDependencies: boolean

View File

@@ -252,7 +252,7 @@ export async function linkBinsOfDependencies<T extends string> (
const pkgs = await Promise.all(pkgNodes const pkgs = await Promise.all(pkgNodes
.map(async (dep) => ({ .map(async (dep) => ({
location: dep.dir, location: dep.dir,
manifest: await dep.fetchingBundledManifest?.() ?? (await safeReadPackageJsonFromDir(dep.dir) as DependencyManifest) ?? {}, manifest: (await dep.fetching?.())?.bundledManifest ?? (await safeReadPackageJsonFromDir(dep.dir) as DependencyManifest) ?? {},
})) }))
) )

View File

@@ -7,7 +7,7 @@ import { jest } from '@jest/globals'
const debug = jest.fn() const debug = jest.fn()
jest.unstable_mockModule('@pnpm/logger', () => { jest.unstable_mockModule('@pnpm/logger', () => {
return ({ debug, logger: () => ({ debug }) }) return ({ globalWarn: jest.fn(), debug, logger: () => ({ debug }) })
}) })
const { createDirectoryFetcher } = await import('@pnpm/directory-fetcher') const { createDirectoryFetcher } = await import('@pnpm/directory-fetcher')

View File

@@ -54,19 +54,15 @@ export interface DependenciesMeta {
} }
} }
export interface DevEngineDependency { export interface EngineDependency {
name: string name: string
version?: string version?: string
onFail?: 'ignore' | 'warn' | 'error' | 'download' onFail?: 'ignore' | 'warn' | 'error' | 'download'
} }
export interface DevEngines { type DevEngineKey = 'os' | 'cpu' | 'libc' | 'runtime' | 'packageManager'
os?: DevEngineDependency | DevEngineDependency[]
cpu?: DevEngineDependency | DevEngineDependency[] export type DevEngines = Partial<Record<DevEngineKey, EngineDependency | EngineDependency[]>>
libc?: DevEngineDependency | DevEngineDependency[]
runtime?: DevEngineDependency | DevEngineDependency[]
packageManager?: DevEngineDependency | DevEngineDependency[]
}
export interface PublishConfig extends Record<string, unknown> { export interface PublishConfig extends Record<string, unknown> {
directory?: string directory?: string
@@ -114,7 +110,7 @@ export interface BaseManifest {
node?: string node?: string
npm?: string npm?: string
pnpm?: string pnpm?: string
} } & Pick<DevEngines, 'runtime'>
devEngines?: DevEngines devEngines?: DevEngines
cpu?: string[] cpu?: string[]
os?: string[] os?: string[]

View File

@@ -1,3 +1,4 @@
import fs from 'fs'
import { LOCKFILE_VERSION, WANTED_LOCKFILE } from '@pnpm/constants' import { LOCKFILE_VERSION, WANTED_LOCKFILE } from '@pnpm/constants'
import { prepareEmpty } from '@pnpm/prepare' import { prepareEmpty } from '@pnpm/prepare'
import { addDependenciesToPackage, install } from '@pnpm/core' import { addDependenciesToPackage, install } from '@pnpm/core'
@@ -353,3 +354,17 @@ test('installing Node.js runtime for the given supported architecture', async ()
await install(manifest, testDefaults({ frozenLockfile: true, supportedArchitectures })) await install(manifest, testDefaults({ frozenLockfile: true, supportedArchitectures }))
project.has(expectedBinLocation) project.has(expectedBinLocation)
}) })
test('installing Node.js runtime, when it is set via the engines field of a dependency', async () => {
prepareEmpty()
await addDependenciesToPackage(
{},
['@pnpm.e2e/cli-with-node-engine@1.0.0'],
testDefaults({
fastUnpack: false,
onlyBuiltDependencies: ['@pnpm.e2e/cli-with-node-engine'],
neverBuiltDependencies: undefined,
})
)
expect(fs.readFileSync('node_modules/@pnpm.e2e/cli-with-node-engine/node-version', 'utf8')).toBe('v22.19.0')
})

View File

@@ -1,5 +1,5 @@
import { promises as fs, existsSync } from 'fs' import { promises as fs, existsSync } from 'fs'
import Module from 'module' import Module, { createRequire } from 'module'
import path from 'path' import path from 'path'
import { getNodeBinLocationForCurrentOS, getDenoBinLocationForCurrentOS, getBunBinLocationForCurrentOS } from '@pnpm/constants' import { getNodeBinLocationForCurrentOS, getDenoBinLocationForCurrentOS, getBunBinLocationForCurrentOS } from '@pnpm/constants'
import { PnpmError } from '@pnpm/error' import { PnpmError } from '@pnpm/error'
@@ -9,7 +9,7 @@ import { type Command, getBinsFromPackageManifest } from '@pnpm/package-bins'
import { readModulesDir } from '@pnpm/read-modules-dir' import { readModulesDir } from '@pnpm/read-modules-dir'
import { readPackageJsonFromDir } from '@pnpm/read-package-json' import { readPackageJsonFromDir } from '@pnpm/read-package-json'
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest' import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
import { type DependencyManifest, type ProjectManifest } from '@pnpm/types' import { type EngineDependency, type DependencyManifest, type ProjectManifest } from '@pnpm/types'
import cmdShim from '@zkochan/cmd-shim' import cmdShim from '@zkochan/cmd-shim'
import rimraf from '@zkochan/rimraf' import rimraf from '@zkochan/rimraf'
import isSubdir from 'is-subdir' import isSubdir from 'is-subdir'
@@ -251,6 +251,17 @@ async function getPackageBins (
async function getPackageBinsFromManifest (manifest: DependencyManifest, pkgDir: string, nodeExecPath?: string): Promise<CommandInfo[]> { async function getPackageBinsFromManifest (manifest: DependencyManifest, pkgDir: string, nodeExecPath?: string): Promise<CommandInfo[]> {
const cmds = await getBinsFromPackageManifest(manifest, pkgDir) const cmds = await getBinsFromPackageManifest(manifest, pkgDir)
if (manifest.engines?.runtime && runtimeHasNodeDownloaded(manifest.engines.runtime) && !nodeExecPath) {
const require = createRequire(import.meta.dirname)
// Using Node.js resolution algorithm is the most reliable way to find the Node.js
// package that comes from this CLI's dependencies, because the layout of node_modules can vary.
// In an isolated layout, it will be located in the same node_modules directory as the CLI.
// In a hoisted layout, it may be in one of the parent node_modules directories.
const nodeDir = path.dirname(require.resolve('node/CHANGELOG.md', { paths: [pkgDir] }))
if (nodeDir) {
nodeExecPath = path.join(nodeDir, IS_WINDOWS ? 'node.exe' : 'bin/node')
}
}
return cmds.map((cmd) => ({ return cmds.map((cmd) => ({
...cmd, ...cmd,
ownName: cmd.name === manifest.name, ownName: cmd.name === manifest.name,
@@ -261,6 +272,13 @@ async function getPackageBinsFromManifest (manifest: DependencyManifest, pkgDir:
})) }))
} }
function runtimeHasNodeDownloaded (runtime: EngineDependency | EngineDependency[]): boolean {
if (!Array.isArray(runtime)) {
return runtime.name === 'node' && runtime.onFail === 'download'
}
return runtime.find(({ name }) => name === 'node')?.onFail === 'download'
}
export interface LinkBinOptions { export interface LinkBinOptions {
extraNodePaths?: string[] extraNodePaths?: string[]
preferSymlinkedExecutables?: boolean preferSymlinkedExecutables?: boolean

View File

@@ -43,6 +43,7 @@ import {
} from '@pnpm/types' } from '@pnpm/types'
import * as dp from '@pnpm/dependency-path' import * as dp from '@pnpm/dependency-path'
import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions' import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions'
import { convertEnginesRuntimeToDependencies } from '@pnpm/manifest-utils'
import { type PatchInfo } from '@pnpm/patching.types' import { type PatchInfo } from '@pnpm/patching.types'
import normalizePath from 'normalize-path' import normalizePath from 'normalize-path'
import { pathExists } from 'path-exists' import { pathExists } from 'path-exists'
@@ -1436,6 +1437,9 @@ async function resolveDependency (
} }
} }
} }
if (pkg.engines?.runtime != null) {
convertEnginesRuntimeToDependencies(pkg, 'engines', 'dependencies')
}
if (!pkg.name) { // TODO: don't fail on optional dependencies if (!pkg.name) { // TODO: don't fail on optional dependencies
throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.bareSpecifier}: Missing package name`) throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.bareSpecifier}: Missing package name`)
} }

View File

@@ -36,7 +36,11 @@
"@pnpm/error": "workspace:*", "@pnpm/error": "workspace:*",
"@pnpm/types": "workspace:*" "@pnpm/types": "workspace:*"
}, },
"peerDependencies": {
"@pnpm/logger": "catalog:"
},
"devDependencies": { "devDependencies": {
"@pnpm/logger": "workspace:*",
"@pnpm/manifest-utils": "workspace:*" "@pnpm/manifest-utils": "workspace:*"
}, },
"engines": { "engines": {

View File

@@ -0,0 +1,30 @@
import { globalWarn } from '@pnpm/logger'
import {
type DependenciesField,
type EngineDependency,
type ProjectManifest,
} from '@pnpm/types'
export function convertEnginesRuntimeToDependencies (
manifest: ProjectManifest,
enginesFieldName: 'devEngines' | 'engines',
dependenciesFieldName: DependenciesField
): void {
for (const runtimeName of ['node', 'deno', 'bun']) {
const enginesFieldRuntime = manifest[enginesFieldName]?.runtime
if (enginesFieldRuntime == null || manifest[dependenciesFieldName]?.[runtimeName]) {
continue
}
const runtimes: EngineDependency[] = Array.isArray(enginesFieldRuntime) ? enginesFieldRuntime : [enginesFieldRuntime]
const runtime = runtimes.find((runtime) => runtime.name === runtimeName)
if (runtime?.onFail !== 'download') {
continue
}
if ('webcontainer' in process.versions) {
globalWarn(`Installation of ${runtimeName} versions is not supported in WebContainer`)
} else {
manifest[dependenciesFieldName] ??= {}
manifest[dependenciesFieldName]![runtimeName] = `runtime:${runtime.version}`
}
}
}

View File

@@ -6,6 +6,7 @@ import {
import { getAllUniqueSpecs } from './getAllUniqueSpecs.js' import { getAllUniqueSpecs } from './getAllUniqueSpecs.js'
import { getSpecFromPackageManifest } from './getSpecFromPackageManifest.js' import { getSpecFromPackageManifest } from './getSpecFromPackageManifest.js'
export * from './convertEnginesRuntimeToDependencies.js'
export * from './updateProjectManifestObject.js' export * from './updateProjectManifestObject.js'
export * from './getDependencyTypeFromManifest.js' export * from './getDependencyTypeFromManifest.js'

View File

@@ -15,6 +15,9 @@
{ {
"path": "../../packages/error" "path": "../../packages/error"
}, },
{
"path": "../../packages/logger"
},
{ {
"path": "../../packages/types" "path": "../../packages/types"
} }

View File

@@ -33,6 +33,7 @@
"dependencies": { "dependencies": {
"@pnpm/error": "workspace:*", "@pnpm/error": "workspace:*",
"@pnpm/graceful-fs": "workspace:*", "@pnpm/graceful-fs": "workspace:*",
"@pnpm/manifest-utils": "workspace:*",
"@pnpm/text.comments-parser": "workspace:*", "@pnpm/text.comments-parser": "workspace:*",
"@pnpm/types": "workspace:*", "@pnpm/types": "workspace:*",
"@pnpm/write-project-manifest": "workspace:*", "@pnpm/write-project-manifest": "workspace:*",

View File

@@ -1,8 +1,8 @@
import { promises as fs, type Stats } from 'fs' import { promises as fs, type Stats } from 'fs'
import path from 'path' import path from 'path'
import { PnpmError } from '@pnpm/error' import { PnpmError } from '@pnpm/error'
import { globalWarn } from '@pnpm/logger' import { type ProjectManifest, type EngineDependency } from '@pnpm/types'
import { type ProjectManifest, type DevEngineDependency } from '@pnpm/types' import { convertEnginesRuntimeToDependencies } from '@pnpm/manifest-utils'
import { extractComments, type CommentSpecifier } from '@pnpm/text.comments-parser' import { extractComments, type CommentSpecifier } from '@pnpm/text.comments-parser'
import { writeProjectManifest } from '@pnpm/write-project-manifest' import { writeProjectManifest } from '@pnpm/write-project-manifest'
import readYamlFile from 'read-yaml-file' import readYamlFile from 'read-yaml-file'
@@ -223,20 +223,8 @@ function createManifestWriter (
} }
function convertManifestAfterRead (manifest: ProjectManifest): ProjectManifest { function convertManifestAfterRead (manifest: ProjectManifest): ProjectManifest {
for (const runtimeName of ['node', 'deno', 'bun']) { convertEnginesRuntimeToDependencies(manifest, 'devEngines', 'devDependencies')
if (manifest.devEngines?.runtime && !manifest.devDependencies?.[runtimeName]) { convertEnginesRuntimeToDependencies(manifest, 'engines', 'dependencies')
const runtimes = Array.isArray(manifest.devEngines.runtime) ? manifest.devEngines.runtime : [manifest.devEngines.runtime]
const runtime = runtimes.find((runtime) => runtime.name === runtimeName)
if (runtime && runtime.onFail === 'download') {
if ('webcontainer' in process.versions) {
globalWarn(`Installation of ${runtimeName} versions is not supported in WebContainer`)
} else {
manifest.devDependencies ??= {}
manifest.devDependencies[runtimeName] = `runtime:${runtime.version}`
}
}
}
}
return manifest return manifest
} }
@@ -247,7 +235,7 @@ function convertManifestBeforeWrite (manifest: ProjectManifest): ProjectManifest
const version = nodeDep.replace(/^runtime:/, '') const version = nodeDep.replace(/^runtime:/, '')
manifest.devEngines ??= {} manifest.devEngines ??= {}
const nodeRuntimeEntry: DevEngineDependency = { const nodeRuntimeEntry: EngineDependency = {
name: runtimeName, name: runtimeName,
version, version,
onFail: 'download', onFail: 'download',

View File

@@ -24,6 +24,9 @@
{ {
"path": "../../text/comments-parser" "path": "../../text/comments-parser"
}, },
{
"path": "../manifest-utils"
},
{ {
"path": "../write-project-manifest" "path": "../write-project-manifest"
} }

68
pnpm-lock.yaml generated
View File

@@ -85,8 +85,8 @@ catalogs:
specifier: 0.0.1 specifier: 0.0.1
version: 0.0.1 version: 0.0.1
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 5.0.0 specifier: 5.1.0
version: 5.0.0 version: 5.1.0
'@pnpm/semver-diff': '@pnpm/semver-diff':
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
@@ -998,7 +998,7 @@ importers:
version: link:../../pkg-manager/modules-yaml version: link:../../pkg-manager/modules-yaml
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/types': '@pnpm/types':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/types version: link:../../packages/types
@@ -1032,7 +1032,7 @@ importers:
dependencies: dependencies:
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/store.cafs': '@pnpm/store.cafs':
specifier: workspace:* specifier: workspace:*
version: link:../../store/cafs version: link:../../store/cafs
@@ -1110,7 +1110,7 @@ importers:
dependencies: dependencies:
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/worker': '@pnpm/worker':
specifier: workspace:* specifier: workspace:*
version: link:../../worker version: link:../../worker
@@ -1312,7 +1312,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@types/ramda': '@types/ramda':
specifier: 'catalog:' specifier: 'catalog:'
version: 0.29.12 version: 0.29.12
@@ -1821,7 +1821,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/testing.temp-store': '@pnpm/testing.temp-store':
specifier: workspace:* specifier: workspace:*
version: link:../../testing/temp-store version: link:../../testing/temp-store
@@ -2525,7 +2525,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/types': '@pnpm/types':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/types version: link:../../packages/types
@@ -2811,7 +2811,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -2968,7 +2968,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-ipc-server': '@pnpm/test-ipc-server':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-ipc-server version: link:../../__utils__/test-ipc-server
@@ -4762,7 +4762,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -5058,7 +5058,7 @@ importers:
version: link:../../pkg-manifest/read-package-json version: link:../../pkg-manifest/read-package-json
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/store-path': '@pnpm/store-path':
specifier: workspace:* specifier: workspace:*
version: link:../../store/store-path version: link:../../store/store-path
@@ -5334,7 +5334,7 @@ importers:
version: link:../read-projects-context version: link:../read-projects-context
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/store-path': '@pnpm/store-path':
specifier: workspace:* specifier: workspace:*
version: link:../../store/store-path version: link:../../store/store-path
@@ -5694,7 +5694,7 @@ importers:
version: 'link:' version: 'link:'
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -5923,7 +5923,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -6281,6 +6281,9 @@ importers:
specifier: workspace:* specifier: workspace:*
version: link:../../packages/types version: link:../../packages/types
devDependencies: devDependencies:
'@pnpm/logger':
specifier: workspace:*
version: link:../../packages/logger
'@pnpm/manifest-utils': '@pnpm/manifest-utils':
specifier: workspace:* specifier: workspace:*
version: 'link:' version: 'link:'
@@ -6318,6 +6321,9 @@ importers:
'@pnpm/logger': '@pnpm/logger':
specifier: 'catalog:' specifier: 'catalog:'
version: 1001.0.0 version: 1001.0.0
'@pnpm/manifest-utils':
specifier: workspace:*
version: link:../manifest-utils
'@pnpm/text.comments-parser': '@pnpm/text.comments-parser':
specifier: workspace:* specifier: workspace:*
version: link:../../text/comments-parser version: link:../../text/comments-parser
@@ -6542,7 +6548,7 @@ importers:
version: link:../pkg-manifest/read-project-manifest version: link:../pkg-manifest/read-project-manifest
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/run-npm': '@pnpm/run-npm':
specifier: workspace:* specifier: workspace:*
version: link:../exec/run-npm version: link:../exec/run-npm
@@ -6893,7 +6899,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -7023,7 +7029,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-ipc-server': '@pnpm/test-ipc-server':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-ipc-server version: link:../../__utils__/test-ipc-server
@@ -7760,7 +7766,7 @@ importers:
version: link:../../pkg-manifest/read-package-json version: link:../../pkg-manifest/read-package-json
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -7827,7 +7833,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/workspace.filter-packages-from-dir': '@pnpm/workspace.filter-packages-from-dir':
specifier: workspace:* specifier: workspace:*
version: link:../../workspace/filter-packages-from-dir version: link:../../workspace/filter-packages-from-dir
@@ -7912,7 +7918,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/test-fixtures': '@pnpm/test-fixtures':
specifier: workspace:* specifier: workspace:*
version: link:../../__utils__/test-fixtures version: link:../../__utils__/test-fixtures
@@ -8269,7 +8275,7 @@ importers:
version: link:../../__utils__/prepare version: link:../../__utils__/prepare
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@types/archy': '@types/archy':
specifier: 'catalog:' specifier: 'catalog:'
version: 0.0.33 version: 0.0.33
@@ -8526,7 +8532,7 @@ importers:
version: link:../../store/package-store version: link:../../store/package-store
'@pnpm/registry-mock': '@pnpm/registry-mock':
specifier: 'catalog:' specifier: 'catalog:'
version: 5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0)) version: 5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))
'@pnpm/store-controller-types': '@pnpm/store-controller-types':
specifier: workspace:* specifier: workspace:*
version: link:../../store/store-controller-types version: link:../../store/store-controller-types
@@ -10464,8 +10470,8 @@ packages:
resolution: {integrity: sha512-UY5ZFl8jTgWpPMp3qwVt1z455gDLGh4aAna7ufqsJP9qhI6lr9scFpnEamjpA51Y3MJMBtnML8KATmH6RY+NHQ==} resolution: {integrity: sha512-UY5ZFl8jTgWpPMp3qwVt1z455gDLGh4aAna7ufqsJP9qhI6lr9scFpnEamjpA51Y3MJMBtnML8KATmH6RY+NHQ==}
engines: {node: '>=18.12'} engines: {node: '>=18.12'}
'@pnpm/registry-mock@5.0.0': '@pnpm/registry-mock@5.1.0':
resolution: {integrity: sha512-ZARk5IXPQumdF6K+TYZABuFZSruYvRy2W5+iyFqP9lbQOYfGNry4W64zNnHIs7RHfvponlFS88tZnAGmU/7Kow==} resolution: {integrity: sha512-XHJmKZG296Nk86WCnmz1hBHcj8Yp8EWNsSz+iY9Ludm5L8S2l0xRYmz8TzyIxpsb5o9q8jahZ781bpW7NAuQFQ==}
engines: {node: '>=18.12'} engines: {node: '>=18.12'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@@ -11091,41 +11097,49 @@ packages:
resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-arm64-musl@1.11.1': '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-riscv64-musl@1.11.1': '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-linux-s390x-gnu@1.11.1': '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-gnu@1.11.1': '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [glibc]
'@unrs/resolver-binding-linux-x64-musl@1.11.1': '@unrs/resolver-binding-linux-x64-musl@1.11.1':
resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
libc: [musl]
'@unrs/resolver-binding-wasm32-wasi@1.11.1': '@unrs/resolver-binding-wasm32-wasi@1.11.1':
resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
@@ -14000,9 +14014,11 @@ packages:
lodash.clone@4.3.2: lodash.clone@4.3.2:
resolution: {integrity: sha512-Yc/0UmZvWkFsbx7NB4feSX5bSX03SR0ft8CTkI8RCb3w/TzT71HXew2iNDm0aml93P49tIR/NJHOIoE+XEKz9A==} resolution: {integrity: sha512-Yc/0UmZvWkFsbx7NB4feSX5bSX03SR0ft8CTkI8RCb3w/TzT71HXew2iNDm0aml93P49tIR/NJHOIoE+XEKz9A==}
deprecated: This package is deprecated. Use structuredClone instead.
lodash.clone@4.5.0: lodash.clone@4.5.0:
resolution: {integrity: sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==} resolution: {integrity: sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg==}
deprecated: This package is deprecated. Use structuredClone instead.
lodash.deburr@4.1.0: lodash.deburr@4.1.0:
resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==}
@@ -18684,7 +18700,7 @@ snapshots:
read-yaml-file: 2.1.0 read-yaml-file: 2.1.0
strip-bom: 4.0.0 strip-bom: 4.0.0
'@pnpm/registry-mock@5.0.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))': '@pnpm/registry-mock@5.1.0(verdaccio@6.1.6(encoding@0.1.13)(typanion@3.14.0))':
dependencies: dependencies:
anonymous-npm-registry-client: 0.3.2 anonymous-npm-registry-client: 0.3.2
execa: 5.1.1 execa: 5.1.1

View File

@@ -78,7 +78,7 @@ catalog:
'@pnpm/npm-package-arg': ^2.0.0 '@pnpm/npm-package-arg': ^2.0.0
'@pnpm/os.env.path-extender': ^2.0.3 '@pnpm/os.env.path-extender': ^2.0.3
'@pnpm/patch-package': 0.0.1 '@pnpm/patch-package': 0.0.1
'@pnpm/registry-mock': 5.0.0 '@pnpm/registry-mock': 5.1.0
'@pnpm/semver-diff': ^1.1.0 '@pnpm/semver-diff': ^1.1.0
'@pnpm/tabtab': ^0.5.4 '@pnpm/tabtab': ^0.5.4
'@pnpm/tgz-fixtures': 0.0.0 '@pnpm/tgz-fixtures': 0.0.0

View File

@@ -0,0 +1,11 @@
import fs from 'fs'
import { prepare } from '@pnpm/prepare'
import { execPnpm } from '../utils/index.js'
test('installing a CLI tool that requires a specific version of Node.js to be installed alongside it', async () => {
prepare()
await execPnpm(['add', '@pnpm.e2e/cli-with-node-engine@1.0.0'])
await execPnpm(['exec', 'cli-with-node-engine'])
expect(fs.readFileSync('node-version', 'utf8')).toBe('v22.19.0')
})