Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dlx): dlx should work when pkg name is not same as cmd name #4682

Merged
merged 5 commits into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/funny-tips-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-script-runners": patch
"pnpm": patch
---

`pnpm dlx` should work when the bin name of the executed package isn't the same as the package name [#4672](https://github.com/pnpm/pnpm/issues/4672).
5 changes: 5 additions & 0 deletions .changeset/spicy-forks-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": patch
---

Export AddCommandOptions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@commitlint/prompt-cli": "^16.0.0",
"@pnpm/eslint-config": "workspace:*",
"@pnpm/meta-updater": "0.0.6",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/tsconfig": "workspace:*",
"@types/jest": "^27.4.0",
"@types/node": "^14.17.32",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/package-store": "workspace:13.0.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/store-path": "workspace:6.0.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/fs-extra": "^9.0.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/headless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@pnpm/package-store": "workspace:13.0.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/read-projects-context": "workspace:6.0.2",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/store-path": "workspace:6.0.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/fs-extra": "^9.0.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/package-requester/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@
},
"devDependencies": {
"@pnpm/client": "workspace:7.1.1",
"@pnpm/create-cafs-store": "workspace:1.0.0",
"@pnpm/logger": "^4.0.0",
"@pnpm/package-requester": "workspace:18.0.2",
"@pnpm/create-cafs-store": "workspace:1.0.0",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/normalize-path": "^3.0.0",
"@types/ramda": "0.27.39",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-commands-installation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@pnpm/modules-yaml": "workspace:10.0.1",
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/is-ci": "^3.0.0",
"@types/proxyquire": "^1.3.28",
Expand Down
18 changes: 10 additions & 8 deletions packages/plugin-commands-installation/src/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,17 @@ For options that may be used with `-r`, see "pnpm help recursive"',
})
}

export type AddCommandOptions = InstallCommandOptions & {
allowNew?: boolean
ignoreWorkspaceRootCheck?: boolean
save?: boolean
update?: boolean
useBetaCli?: boolean
workspaceRoot?: boolean
}

export async function handler (
opts: InstallCommandOptions & {
allowNew?: boolean
ignoreWorkspaceRootCheck?: boolean
save?: boolean
update?: boolean
useBetaCli?: boolean
workspaceRoot?: boolean
},
opts: AddCommandOptions,
params: string[]
) {
if (opts.cliOptions['save'] === false) {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-commands-listing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/plugin-commands-listing": "workspace:5.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/ramda": "0.27.39",
"execa": "npm:safe-execa@^0.1.1",
"strip-ansi": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-commands-outdated/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/plugin-commands-outdated": "workspace:6.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/lru-cache": "^5.1.0",
"@types/ramda": "0.27.39",
"@types/wrap-ansi": "^3.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-commands-publishing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-publishing": "workspace:5.0.4",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/cross-spawn": "^6.0.2",
"@types/is-ci": "^3.0.0",
"@types/is-windows": "^1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-commands-rebuild/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-rebuild": "workspace:6.1.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.27.39",
"@types/semver": "^7.3.4",
Expand Down
5 changes: 4 additions & 1 deletion packages/plugin-commands-script-runners/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-script-runners": "workspace:5.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/is-windows": "^1.0.0",
"@types/ramda": "0.27.39",
"is-windows": "^1.0.2",
Expand All @@ -51,6 +51,9 @@
"@pnpm/config": "workspace:15.1.1",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/lifecycle": "workspace:13.0.2",
"@pnpm/package-bins": "workspace:6.0.1",
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/read-package-json": "workspace:6.0.2",
"@pnpm/read-project-manifest": "workspace:3.0.2",
"@pnpm/sort-packages": "workspace:3.0.2",
"@pnpm/store-path": "workspace:6.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-commands-script-runners/src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as dlx from './dlx'

export const commandNames = ['create']

export async function handler (_opts: Record<string, never>, params: string[]) {
export async function handler (_opts: dlx.DlxCommandOptions, params: string[]) {
const [packageName, ...packageArgs] = params
if (packageName === undefined) {
throw new PnpmError(
Expand All @@ -17,7 +17,7 @@ export async function handler (_opts: Record<string, never>, params: string[]) {
}

const createPackageName = convertToCreateName(packageName)
return dlx.handler({}, [createPackageName, ...packageArgs])
return dlx.handler(_opts, [createPackageName, ...packageArgs])
}

export function rcOptionsTypes () {
Expand Down
96 changes: 65 additions & 31 deletions packages/plugin-commands-script-runners/src/dlx.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import fs from 'fs'
import os from 'os'
import path from 'path'
import { docsUrl } from '@pnpm/cli-utils'
import { OUTPUT_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config } from '@pnpm/config'
import rimraf from '@zkochan/rimraf'
import PnpmError from '@pnpm/error'
import { add } from '@pnpm/plugin-commands-installation'
import { fromDir as readPkgFromDir } from '@pnpm/read-package-json'
import packageBins from '@pnpm/package-bins'
import storePath from '@pnpm/store-path'
import execa from 'execa'
import renderHelp from 'render-help'
import { makeEnv } from './makeEnv'
Expand Down Expand Up @@ -40,16 +43,22 @@ export function help () {
})
}

export type DlxCommandOptions = {
package?: string[]
} & Pick<Config, 'reporter' | 'userAgent'> & add.AddCommandOptions

export async function handler (
opts: {
package?: string[]
} & Pick<Config, 'reporter' | 'userAgent'>,
params: string[]
opts: DlxCommandOptions,
[command, ...args]: string[]
) {
const prefix = path.join(fs.realpathSync(os.tmpdir()), `dlx-${process.pid.toString()}`)
const bins = process.platform === 'win32'
? prefix
: path.join(prefix, 'bin')
const dlxDir = await getDlxDir({
dir: opts.dir,
pnpmHomeDir: opts.pnpmHomeDir,
storeDir: opts.storeDir,
})
const prefix = path.join(dlxDir, `dlx-${process.pid.toString()}`)
const modulesDir = path.join(prefix, 'node_modules')
const binsDir = path.join(modulesDir, '.bin')
fs.mkdirSync(prefix, { recursive: true })
process.on('exit', () => {
try {
Expand All @@ -59,37 +68,62 @@ export async function handler (
})
} catch (err) {}
})
await rimraf(bins)
const pkgs = opts.package ?? params.slice(0, 1)
const pnpmArgs = [
'add',
...pkgs,
'--global',
`--global-dir=${prefix}`,
`--dir=${prefix}`,
`--config.global-bin-dir=${bins}`,
]
if (opts.reporter) {
pnpmArgs.push(`--reporter=${opts.reporter}`)
}
const env = makeEnv({ userAgent: opts.userAgent, prependPaths: [bins] })
await execa('pnpm', pnpmArgs, {
env,
stdio: 'inherit',
})
await execa(versionless(scopeless(params[0])), params.slice(1), {
const pkgs = opts.package ?? [command]
const env = makeEnv({ userAgent: opts.userAgent, prependPaths: [binsDir] })
await add.handler({
...opts,
dir: prefix,
bin: binsDir,
}, pkgs)
const binName = opts.package
? command
: await getBinName(modulesDir, versionless(command))
await execa(binName, args, {
env,
stdio: 'inherit',
})
}

async function getBinName (modulesDir: string, pkgName: string): Promise<string> {
const pkgDir = path.join(modulesDir, pkgName)
const manifest = await readPkgFromDir(pkgDir)
const bins = await packageBins(manifest, pkgDir)
if (bins.length === 0) {
throw new PnpmError('DLX_NO_BIN', `No binaries found in ${pkgName}`)
}
if (bins.length === 1) {
return bins[0].name
}
const scopelessPkgName = scopeless(manifest.name)
const defaultBin = bins.find(({ name }) => name === scopelessPkgName)
if (defaultBin) return defaultBin.name
throw new PnpmError('DLX_MULTIPLE_BINS', `Multiple binaries found in ${pkgName}`)
}

function scopeless (pkgName: string) {
if (pkgName.startsWith('@')) {
return pkgName.split('/')[1]
}
return pkgName
}

function versionless (scopelessPkgName: string) {
return scopelessPkgName.split('@')[0]
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
storeDir?: string
pnpmHomeDir: string
}
): Promise<string> {
const storeDir = await storePath({
pkgRoot: opts.dir,
storePath: opts.storeDir,
pnpmHomeDir: opts.pnpmHomeDir,
})
return path.join(storeDir, 'tmp')
}