diff --git a/.changeset/fifty-taxis-exist.md b/.changeset/fifty-taxis-exist.md new file mode 100644 index 00000000000..9b5235845f8 --- /dev/null +++ b/.changeset/fifty-taxis-exist.md @@ -0,0 +1,6 @@ +--- +"@pnpm/plugin-commands-script-runners": patch +"pnpm": patch +--- + +`pnpm dlx` should work with git-hosted packages. For example: `pnpm dlx gengjiawen/envinfo` [#4714](https://github.com/pnpm/pnpm/issues/4714). diff --git a/packages/plugin-commands-script-runners/src/dlx.ts b/packages/plugin-commands-script-runners/src/dlx.ts index b46fbd62da0..e4a800cf738 100644 --- a/packages/plugin-commands-script-runners/src/dlx.ts +++ b/packages/plugin-commands-script-runners/src/dlx.ts @@ -76,13 +76,18 @@ export async function handler ( }, pkgs) const binName = opts.package ? command - : await getBinName(modulesDir, versionless(command)) + : await getBinName(modulesDir, await getPkgName(prefix)) await execa(binName, args, { env, stdio: 'inherit', }) } +async function getPkgName (pkgDir: string) { + const manifest = await readPkgFromDir(pkgDir) + return Object.keys(manifest.dependencies ?? {})[0] +} + async function getBinName (modulesDir: string, pkgName: string): Promise { const pkgDir = path.join(modulesDir, pkgName) const manifest = await readPkgFromDir(pkgDir) @@ -111,12 +116,6 @@ function scopeless (pkgName: string) { return pkgName } -function versionless (pkgName: string) { - const index = pkgName.indexOf('@', 1) - if (index === -1) return pkgName - return pkgName.substring(0, index) -} - async function getDlxDir ( opts: { dir: string diff --git a/packages/plugin-commands-script-runners/test/dlx.e2e.ts b/packages/plugin-commands-script-runners/test/dlx.e2e.ts index 30d98724713..60e7aa097b7 100644 --- a/packages/plugin-commands-script-runners/test/dlx.e2e.ts +++ b/packages/plugin-commands-script-runners/test/dlx.e2e.ts @@ -1,4 +1,5 @@ import fs from 'fs' +import path from 'path' import { dlx } from '@pnpm/plugin-commands-script-runners' import { prepareEmpty } from '@pnpm/prepare' import { DLX_DEFAULT_OPTS as DEFAULT_OPTS } from './utils' @@ -8,18 +9,31 @@ test('dlx', async () => { await dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['shx', 'touch', 'foo']) expect(fs.existsSync('foo')).toBeTruthy() }) -test('dlx should work when the package name differs from the bin name', async () => { +test('dlx install from git', async () => { prepareEmpty() await dlx.handler({ ...DEFAULT_OPTS, dir: process.cwd(), + }, ['shelljs/shx#61aca968cd7afc712ca61a4fc4ec3201e3770dc7', 'touch', 'foo']) + + expect(fs.existsSync('foo')).toBeTruthy() +}) + +test('dlx should work when the package name differs from the bin name', async () => { + prepareEmpty() + + await dlx.handler({ + ...DEFAULT_OPTS, + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['touch-file-one-bin']) expect(fs.existsSync('touch.txt')).toBeTruthy() @@ -31,7 +45,8 @@ test('dlx should fail when the installed package has many commands and none equa await expect( dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['touch-file-many-bins']) ).rejects.toThrow('Could not determine executable to run. touch-file-many-bins has multiple binaries: t, tt') }) @@ -41,7 +56,8 @@ test('dlx should not fail when the installed package has many commands and one e await dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['touch-file-good-bin-name']) expect(fs.existsSync('touch.txt')).toBeTruthy() @@ -52,7 +68,8 @@ test('dlx --package [--package ]', async () => { await dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), package: [ 'zkochan/for-testing-pnpm-dlx', 'is-positive', @@ -68,7 +85,8 @@ test('dlx should fail when the package has no bins', async () => { await expect( dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['is-positive']) ).rejects.toThrow(/No binaries found in is-positive/) }) diff --git a/packages/plugin-commands-script-runners/test/dlx.ts b/packages/plugin-commands-script-runners/test/dlx.ts index 86e61ebbd88..fe0ceed8627 100644 --- a/packages/plugin-commands-script-runners/test/dlx.ts +++ b/packages/plugin-commands-script-runners/test/dlx.ts @@ -1,3 +1,4 @@ +import path from 'path' import execa from 'execa' import { dlx } from '@pnpm/plugin-commands-script-runners' import { prepareEmpty } from '@pnpm/prepare' @@ -13,7 +14,8 @@ test('dlx should work with scoped packages', async () => { await dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), userAgent, }, ['@foo/touch-file-one-bin']) @@ -29,7 +31,8 @@ test('dlx should work with versioned packages', async () => { await dlx.handler({ ...DEFAULT_OPTS, - dir: process.cwd(), + dir: path.resolve('project'), + storeDir: path.resolve('store'), }, ['@foo/touch-file-one-bin@latest']) expect(execa).toBeCalledWith('touch-file-one-bin', [], expect.anything())