diff --git a/.changeset/huge-apples-hang.md b/.changeset/huge-apples-hang.md new file mode 100644 index 0000000000..6d2d06db7d --- /dev/null +++ b/.changeset/huge-apples-hang.md @@ -0,0 +1,5 @@ +--- +"@pnpm/plugin-commands-script-runners": patch +--- + +When executing the `pnpm create` command, must verify whether the node version is supported even if a cache already exists. diff --git a/exec/plugin-commands-script-runners/package.json b/exec/plugin-commands-script-runners/package.json index 8a53e8027c..3048acbcb8 100644 --- a/exec/plugin-commands-script-runners/package.json +++ b/exec/plugin-commands-script-runners/package.json @@ -73,6 +73,7 @@ "@pnpm/logger": "catalog:" }, "devDependencies": { + "@pnpm/env.system-node-version": "workspace:*", "@pnpm/filter-workspace-packages": "workspace:*", "@pnpm/logger": "workspace:*", "@pnpm/plugin-commands-script-runners": "workspace:*", diff --git a/exec/plugin-commands-script-runners/src/dlx.ts b/exec/plugin-commands-script-runners/src/dlx.ts index faa12e8b13..6b154cade1 100644 --- a/exec/plugin-commands-script-runners/src/dlx.ts +++ b/exec/plugin-commands-script-runners/src/dlx.ts @@ -1,7 +1,7 @@ import fs, { type Stats } from 'fs' import path from 'path' import util from 'util' -import { docsUrl } from '@pnpm/cli-utils' +import { docsUrl, readProjectManifestOnly } from '@pnpm/cli-utils' import { createResolver } from '@pnpm/client' import { parseWantedDependency } from '@pnpm/parse-wanted-dependency' import { OUTPUT_OPTIONS } from '@pnpm/common-cli-options-help' @@ -11,7 +11,7 @@ import { PnpmError } from '@pnpm/error' import { add } from '@pnpm/plugin-commands-installation' import { readPackageJsonFromDir } from '@pnpm/read-package-json' import { getBinsFromPackageManifest } from '@pnpm/package-bins' -import { type PnpmSettings, type SupportedArchitectures } from '@pnpm/types' +import { type PackageManifest, type PnpmSettings, type SupportedArchitectures } from '@pnpm/types' import { lexCompare } from '@pnpm/util.lex-comparator' import execa from 'execa' import pick from 'ramda/src/pick' @@ -138,15 +138,14 @@ export async function handler ( } } } - const modulesDir = path.join(cachedDir, 'node_modules') - const binsDir = path.join(modulesDir, '.bin') + const binsDir = path.join(cachedDir, 'node_modules/.bin') const env = makeEnv({ userAgent: opts.userAgent, prependPaths: [binsDir, ...opts.extraBinPaths], }) const binName = opts.package ? command - : await getBinName(modulesDir, await getPkgName(cachedDir)) + : await getBinName(cachedDir, opts) try { await execa(binName, args, { cwd: process.cwd(), @@ -174,9 +173,10 @@ async function getPkgName (pkgDir: string): Promise { return dependencyNames[0] } -async function getBinName (modulesDir: string, pkgName: string): Promise { - const pkgDir = path.join(modulesDir, pkgName) - const manifest = await readPackageJsonFromDir(pkgDir) +async function getBinName (cachedDir: string, opts: Pick): Promise { + const pkgName = await getPkgName(cachedDir) + const pkgDir = path.join(cachedDir, 'node_modules', pkgName) + const manifest = await readProjectManifestOnly(pkgDir, opts) as PackageManifest const bins = await getBinsFromPackageManifest(manifest, pkgDir) if (bins.length === 0) { throw new PnpmError('DLX_NO_BIN', `No binaries found in ${pkgName}`) diff --git a/exec/plugin-commands-script-runners/test/dlx.e2e.ts b/exec/plugin-commands-script-runners/test/dlx.e2e.ts index ad2538d624..6885bb509d 100644 --- a/exec/plugin-commands-script-runners/test/dlx.e2e.ts +++ b/exec/plugin-commands-script-runners/test/dlx.e2e.ts @@ -2,6 +2,7 @@ import fs from 'fs' import path from 'path' import { add } from '@pnpm/plugin-commands-installation' import { dlx } from '@pnpm/plugin-commands-script-runners' +import * as systemNodeVersion from '@pnpm/env.system-node-version' import { prepareEmpty } from '@pnpm/prepare' import { DLX_DEFAULT_OPTS as DEFAULT_OPTS } from './utils' @@ -235,6 +236,20 @@ test('dlx with cache', async () => { expect(spy).not.toHaveBeenCalled() spy.mockRestore() + + // Specify a node version that shx@0.3.4 does not support. Currently supported versions are >= 6. + const spySystemNodeVersion = jest.spyOn(systemNodeVersion, 'getSystemNodeVersion').mockReturnValue('v4.0.0') + + await expect(dlx.handler({ + ...DEFAULT_OPTS, + engineStrict: true, + dir: path.resolve('project'), + storeDir: path.resolve('store'), + cacheDir: path.resolve('cache'), + dlxCacheMaxAge: Infinity, + }, ['shx@0.3.4', 'touch', 'foo'])).rejects.toThrow('Unsupported engine for') + + spySystemNodeVersion.mockRestore() }) test('dlx does not reuse expired cache', async () => { diff --git a/exec/plugin-commands-script-runners/tsconfig.json b/exec/plugin-commands-script-runners/tsconfig.json index 5907592ad1..3a0fc56cac 100644 --- a/exec/plugin-commands-script-runners/tsconfig.json +++ b/exec/plugin-commands-script-runners/tsconfig.json @@ -39,6 +39,9 @@ { "path": "../../env/plugin-commands-env" }, + { + "path": "../../env/system-node-version" + }, { "path": "../../packages/core-loggers" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6d0b2800d1..817be5a8f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2747,6 +2747,9 @@ importers: specifier: 'catalog:' version: 4.3.0 devDependencies: + '@pnpm/env.system-node-version': + specifier: workspace:* + version: link:../../env/system-node-version '@pnpm/filter-workspace-packages': specifier: workspace:* version: link:../../workspace/filter-workspace-packages