diff --git a/.changeset/tiny-symbols-enjoy.md b/.changeset/tiny-symbols-enjoy.md new file mode 100644 index 0000000000..857b83b221 --- /dev/null +++ b/.changeset/tiny-symbols-enjoy.md @@ -0,0 +1,6 @@ +--- +"@pnpm/parse-cli-args": minor +"pnpm": patch +--- + +Allow `dlx` to parse CLI flags and options between the `dlx` command and the command to run or between the `dlx` command and `--` [#9719](https://github.com/pnpm/pnpm/issues/9719). diff --git a/cli/parse-cli-args/src/index.ts b/cli/parse-cli-args/src/index.ts index 65d646eb73..aab8b5d0b9 100644 --- a/cli/parse-cli-args/src/index.ts +++ b/cli/parse-cli-args/src/index.ts @@ -108,8 +108,8 @@ export async function parseCliArgs ( return 'add' } - function getEscapeArgsWithSpecialCaseForRun (): string[] | undefined { - if (cmd !== 'run') { + function getEscapeArgsWithSpecialCases (): string[] | undefined { + if (cmd !== 'run' && cmd !== 'dlx') { return opts.escapeArgs } @@ -139,7 +139,7 @@ export async function parseCliArgs ( }, inputArgv, 0, - { escapeArgs: getEscapeArgsWithSpecialCaseForRun() } + { escapeArgs: getEscapeArgsWithSpecialCases() } ) const workspaceDir = await getWorkspaceDir(options) diff --git a/cli/parse-cli-args/test/index.ts b/cli/parse-cli-args/test/index.ts index 6b9b820dc6..f21e0efbdd 100644 --- a/cli/parse-cli-args/test/index.ts +++ b/cli/parse-cli-args/test/index.ts @@ -336,3 +336,51 @@ test('should not swallows empty string in params', async () => { expect(cmd).toBe('run') expect(params).toStrictEqual(['echo', '', 'foo', '', 'bar']) }) + +test('dlx parses CLI options in between "dlx" and the command name', async () => { + const { params, options, cmd } = await parseCliArgs({ + ...DEFAULT_OPTS, + }, [ + '--reporter=append-only', + 'dlx', + '--allow-build=some-package', + '--package=some-bin-package', + 'some-command', + '--this-is-not-a-flag', + 'another-argument', + ]) + expect(cmd).toBe('dlx') + expect(options).toStrictEqual({ + reporter: 'append-only', + 'allow-build': 'some-package', + package: 'some-bin-package', + }) + expect(params).toStrictEqual([ + 'some-command', + '--this-is-not-a-flag', + 'another-argument', + ]) +}) + +test('dlx stops parsing after "--"', async () => { + const { params, options, cmd } = await parseCliArgs({ + ...DEFAULT_OPTS, + }, [ + 'dlx', + '--package=some-package', + '--allow-build=foo', + '--allow-build=bar', + '--', + '--this-is-a-command', + 'argument', + ]) + expect(cmd).toBe('dlx') + expect(options).toStrictEqual({ + package: 'some-package', + 'allow-build': ['foo', 'bar'], + }) + expect(params).toStrictEqual([ + '--this-is-a-command', + 'argument', + ]) +}) diff --git a/pnpm/src/parseCliArgs.ts b/pnpm/src/parseCliArgs.ts index 650f03c4fb..970b31188a 100644 --- a/pnpm/src/parseCliArgs.ts +++ b/pnpm/src/parseCliArgs.ts @@ -17,7 +17,7 @@ const RENAMED_OPTIONS = { export async function parseCliArgs (inputArgv: string[]): Promise { return parseCliArgsLib({ fallbackCommand: 'run', - escapeArgs: ['create', 'dlx', 'exec', 'test'], + escapeArgs: ['create', 'exec', 'test'], getCommandLongName: getCommandFullName, getTypesByCommandName: getCliOptionsTypes, renamedOptions: RENAMED_OPTIONS, diff --git a/pnpm/test/dlx.ts b/pnpm/test/dlx.ts index 52e29ab0ff..b5b7c32f49 100644 --- a/pnpm/test/dlx.ts +++ b/pnpm/test/dlx.ts @@ -18,6 +18,23 @@ beforeAll(async () => { const createCacheKey = (...pkgs: string[]): string => dlx.createCacheKey(pkgs, registries) +test('dlx parses options between "dlx" and the command name', async () => { + prepareEmpty() + const global = path.resolve('..', 'global') + const pnpmHome = path.join(global, 'pnpm') + fs.mkdirSync(global) + + const env = { + [PATH_NAME]: `${pnpmHome}${path.delimiter}${process.env[PATH_NAME]}`, + PNPM_HOME: pnpmHome, + XDG_DATA_HOME: global, + } + + const result = execPnpmSync(['dlx', '--package', 'shx@0.3.4', '--silent', 'shx', 'echo', 'hi'], { env, expectSuccess: true }) + + expect(result.stdout.toString().trim()).toBe('hi') +}) + test('silent dlx prints the output of the child process only', async () => { prepare({}) const global = path.resolve('..', 'global')