mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-11 00:18:32 -05:00
fix: the dlx and create commands should set npm_config_user_agent (#4317)
close #3985
This commit is contained in:
6
.changeset/thirty-owls-fix.md
Normal file
6
.changeset/thirty-owls-fix.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
The `pnpx`, `pnpm dlx`, `pnpm create`, and `pnpm exec` commands should set the `npm_config_user_agent` env variable [#3985](https://github.com/pnpm/pnpm/issues/3985).
|
||||
@@ -6,8 +6,8 @@ import { OUTPUT_OPTIONS } from '@pnpm/common-cli-options-help'
|
||||
import { Config } from '@pnpm/config'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import execa from 'execa'
|
||||
import PATH from 'path-name'
|
||||
import renderHelp from 'render-help'
|
||||
import { makeEnv } from './makeEnv'
|
||||
|
||||
export const commandNames = ['dlx']
|
||||
|
||||
@@ -43,7 +43,7 @@ export function help () {
|
||||
export async function handler (
|
||||
opts: {
|
||||
package?: string[]
|
||||
} & Pick<Config, 'reporter'>,
|
||||
} & Pick<Config, 'reporter' | 'userAgent'>,
|
||||
params: string[]
|
||||
) {
|
||||
const prefix = path.join(fs.realpathSync(os.tmpdir()), `dlx-${process.pid.toString()}`)
|
||||
@@ -69,13 +69,7 @@ export async function handler (
|
||||
stdio: 'inherit',
|
||||
})
|
||||
await execa(versionless(scopeless(params[0])), params.slice(1), {
|
||||
env: {
|
||||
...process.env,
|
||||
[PATH]: [
|
||||
bins,
|
||||
process.env[PATH],
|
||||
].join(path.delimiter),
|
||||
},
|
||||
env: makeEnv({ userAgent: opts.userAgent, prependPaths: [bins] }),
|
||||
stdio: 'inherit',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import sortPackages from '@pnpm/sort-packages'
|
||||
import { Project } from '@pnpm/types'
|
||||
import execa from 'execa'
|
||||
import pLimit from 'p-limit'
|
||||
import PATH from 'path-name'
|
||||
import pick from 'ramda/src/pick'
|
||||
import renderHelp from 'render-help'
|
||||
import existsInDir from './existsInDir'
|
||||
import { makeEnv } from './makeEnv'
|
||||
import {
|
||||
PARALLEL_OPTION_HELP,
|
||||
shorthands as runShorthands,
|
||||
@@ -70,7 +70,7 @@ export async function handler (
|
||||
reverse?: boolean
|
||||
sort?: boolean
|
||||
workspaceConcurrency?: number
|
||||
} & Pick<Config, 'extraBinPaths' | 'lockfileDir' | 'dir' | 'recursive' | 'workspaceDir'>,
|
||||
} & Pick<Config, 'extraBinPaths' | 'lockfileDir' | 'dir' | 'userAgent' | 'recursive' | 'workspaceDir'>,
|
||||
params: string[]
|
||||
) {
|
||||
// For backward compatibility
|
||||
@@ -119,18 +119,20 @@ export async function handler (
|
||||
const extraEnv = pnpPath
|
||||
? makeNodeRequireOption(pnpPath)
|
||||
: {}
|
||||
await execa(params[0], params.slice(1), {
|
||||
cwd: prefix,
|
||||
env: {
|
||||
...process.env,
|
||||
const env = makeEnv({
|
||||
extraEnv: {
|
||||
...extraEnv,
|
||||
[PATH]: [
|
||||
path.join(opts.dir, 'node_modules/.bin'),
|
||||
...opts.extraBinPaths,
|
||||
process.env[PATH],
|
||||
].join(path.delimiter),
|
||||
PNPM_PACKAGE_NAME: opts.selectedProjectsGraph?.[prefix]?.package.manifest.name,
|
||||
},
|
||||
prependPaths: [
|
||||
path.join(opts.dir, 'node_modules/.bin'),
|
||||
...opts.extraBinPaths,
|
||||
],
|
||||
userAgent: opts.userAgent,
|
||||
})
|
||||
await execa(params[0], params.slice(1), {
|
||||
cwd: prefix,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
})
|
||||
result.passes++
|
||||
|
||||
20
packages/plugin-commands-script-runners/src/makeEnv.ts
Normal file
20
packages/plugin-commands-script-runners/src/makeEnv.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import path from 'path'
|
||||
import PATH from 'path-name'
|
||||
|
||||
export function makeEnv (
|
||||
opts: {
|
||||
extraEnv?: NodeJS.ProcessEnv
|
||||
userAgent?: string
|
||||
prependPaths: string[]
|
||||
}
|
||||
) {
|
||||
return {
|
||||
...process.env,
|
||||
...opts.extraEnv,
|
||||
npm_config_user_agent: opts.userAgent ?? 'pnpm',
|
||||
[PATH]: [
|
||||
...opts.prependPaths,
|
||||
process.env[PATH],
|
||||
].join(path.delimiter),
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ For options that may be used with `-r`, see "pnpm help recursive"',
|
||||
export type RunOpts =
|
||||
& Omit<RecursiveRunOpts, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>
|
||||
& { recursive?: boolean }
|
||||
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts'>
|
||||
& Pick<Config, 'dir' | 'engineStrict' | 'extraBinPaths' | 'reporter' | 'scriptsPrependNodePath' | 'scriptShell' | 'shellEmulator' | 'enablePrePostScripts' | 'userAgent'>
|
||||
& (
|
||||
& { recursive?: false }
|
||||
& Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
|
||||
@@ -8,10 +8,15 @@ beforeEach((execa as jest.Mock).mockClear)
|
||||
|
||||
test('dlx should work with scoped packages', async () => {
|
||||
prepareEmpty()
|
||||
const userAgent = 'pnpm/0.0.0'
|
||||
|
||||
await dlx.handler({}, ['@foo/bar'])
|
||||
await dlx.handler({ userAgent }, ['@foo/bar'])
|
||||
|
||||
expect(execa).toBeCalledWith('bar', [], expect.anything())
|
||||
expect(execa).toBeCalledWith('bar', [], expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
npm_config_user_agent: userAgent,
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
test('dlx should work with versioned packages', async () => {
|
||||
|
||||
484
packages/plugin-commands-script-runners/test/exec.e2e.ts
Normal file
484
packages/plugin-commands-script-runners/test/exec.e2e.ts
Normal file
@@ -0,0 +1,484 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import { exec, run } from '@pnpm/plugin-commands-script-runners'
|
||||
import prepare, { prepareEmpty, preparePackages } from '@pnpm/prepare'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import execa from 'execa'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
|
||||
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
|
||||
|
||||
test('pnpm recursive exec', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1', 'project-2-prebuild', 'project-2', 'project-2-postbuild'])
|
||||
expect(outputs2).toStrictEqual(['project-1', 'project-3'])
|
||||
})
|
||||
|
||||
test('exec inside a workspace package', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: path.resolve('project-1'),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1'])
|
||||
expect(outputs2).toStrictEqual(['project-1'])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec sets PNPM_PACKAGE_NAME env var', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['node', '-e', 'require(\'fs\').writeFileSync(\'pkgname\', process.env.PNPM_PACKAGE_NAME, \'utf8\')'])
|
||||
|
||||
expect(await fs.readFile('foo/pkgname', 'utf8')).toBe('foo')
|
||||
})
|
||||
|
||||
test('testing the bail config with "pnpm recursive exec"', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'exit 1 && node -e "process.stdout.write(\'project-2\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
let failed = false
|
||||
let err1!: PnpmError
|
||||
try {
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build', '--no-bail'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err1 = _err
|
||||
failed = true
|
||||
}
|
||||
expect(err1.code).toBe('ERR_PNPM_RECURSIVE_FAIL')
|
||||
expect(failed).toBeTruthy()
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual(['project-1', 'project-3'])
|
||||
|
||||
await rimraf('./output.json')
|
||||
|
||||
failed = false
|
||||
let err2!: PnpmError
|
||||
try {
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err2 = _err
|
||||
failed = true
|
||||
}
|
||||
|
||||
expect(err2.code).toBe('ERR_PNPM_RECURSIVE_FAIL')
|
||||
expect(failed).toBeTruthy()
|
||||
})
|
||||
|
||||
test('pnpm recursive exec --no-sort', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'a-dependent',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'b-dependency': '1.0.0',
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'a-dependent\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'b-dependency',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'b-dependency\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
sort: false,
|
||||
workspaceConcurrency: 1,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
|
||||
expect(outputs).toStrictEqual(['a-dependent', 'b-dependency'])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec --reverse', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
selectedProjectsGraph,
|
||||
recursive: true,
|
||||
sort: true,
|
||||
reverse: true,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
|
||||
expect(outputs1[outputs1.length - 1]).toBe('project-1')
|
||||
})
|
||||
|
||||
test('pnpm exec on single project', async () => {
|
||||
prepare({})
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'require("fs").writeFileSync("output.json", "[]", "utf8")'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual([])
|
||||
})
|
||||
|
||||
test('pnpm exec on single project should return non-zero exit code when the process fails', async () => {
|
||||
prepare({})
|
||||
|
||||
{
|
||||
const { exitCode } = await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'process.exitCode=1'])
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
}
|
||||
|
||||
{
|
||||
const runResult = await run.handler({
|
||||
...DEFAULT_OPTS,
|
||||
argv: {
|
||||
original: ['pnpm', 'node', '-e', 'process.exitCode=1'],
|
||||
},
|
||||
dir: process.cwd(),
|
||||
fallbackCommandUsed: true,
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node'])
|
||||
|
||||
expect(runResult['exitCode']).toBe(1)
|
||||
}
|
||||
})
|
||||
|
||||
test('pnpm exec outside of projects', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'require("fs").writeFileSync("output.json", "[]", "utf8")'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual([])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec works with PnP', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
], {
|
||||
env: {
|
||||
NPM_CONFIG_NODE_LINKER: 'pnp',
|
||||
NPM_CONFIG_SYMLINK: 'false',
|
||||
},
|
||||
})
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1', 'project-2-prebuild', 'project-2', 'project-2-postbuild'])
|
||||
expect(outputs2).toStrictEqual(['project-1', 'project-3'])
|
||||
})
|
||||
@@ -1,484 +1,26 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import path from 'path'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import { exec, run } from '@pnpm/plugin-commands-script-runners'
|
||||
import prepare, { prepareEmpty, preparePackages } from '@pnpm/prepare'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import execa from 'execa'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
import { exec } from '@pnpm/plugin-commands-script-runners'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
|
||||
jest.mock('execa')
|
||||
|
||||
test('pnpm recursive exec', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
beforeEach((execa as jest.Mock).mockClear)
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1', 'project-2-prebuild', 'project-2', 'project-2-postbuild'])
|
||||
expect(outputs2).toStrictEqual(['project-1', 'project-3'])
|
||||
})
|
||||
|
||||
test('exec inside a workspace package', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: path.resolve('project-1'),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1'])
|
||||
expect(outputs2).toStrictEqual(['project-1'])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec sets PNPM_PACKAGE_NAME env var', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['node', '-e', 'require(\'fs\').writeFileSync(\'pkgname\', process.env.PNPM_PACKAGE_NAME, \'utf8\')'])
|
||||
|
||||
expect(await fs.readFile('foo/pkgname', 'utf8')).toBe('foo')
|
||||
})
|
||||
|
||||
test('testing the bail config with "pnpm recursive exec"', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'exit 1 && node -e "process.stdout.write(\'project-2\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
let failed = false
|
||||
let err1!: PnpmError
|
||||
try {
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build', '--no-bail'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err1 = _err
|
||||
failed = true
|
||||
}
|
||||
expect(err1.code).toBe('ERR_PNPM_RECURSIVE_FAIL')
|
||||
expect(failed).toBeTruthy()
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual(['project-1', 'project-3'])
|
||||
|
||||
await rimraf('./output.json')
|
||||
|
||||
failed = false
|
||||
let err2!: PnpmError
|
||||
try {
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err2 = _err
|
||||
failed = true
|
||||
}
|
||||
|
||||
expect(err2.code).toBe('ERR_PNPM_RECURSIVE_FAIL')
|
||||
expect(failed).toBeTruthy()
|
||||
})
|
||||
|
||||
test('pnpm recursive exec --no-sort', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'a-dependent',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'b-dependency': '1.0.0',
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'a-dependent\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'b-dependency',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'b-dependency\')" | json-append ../output.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
sort: false,
|
||||
workspaceConcurrency: 1,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
|
||||
expect(outputs).toStrictEqual(['a-dependent', 'b-dependency'])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec --reverse', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
selectedProjectsGraph,
|
||||
recursive: true,
|
||||
sort: true,
|
||||
reverse: true,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
|
||||
expect(outputs1[outputs1.length - 1]).toBe('project-1')
|
||||
})
|
||||
|
||||
test('pnpm exec on single project', async () => {
|
||||
prepare({})
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'require("fs").writeFileSync("output.json", "[]", "utf8")'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual([])
|
||||
})
|
||||
|
||||
test('pnpm exec on single project should return non-zero exit code when the process fails', async () => {
|
||||
prepare({})
|
||||
|
||||
{
|
||||
const { exitCode } = await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'process.exitCode=1'])
|
||||
|
||||
expect(exitCode).toBe(1)
|
||||
}
|
||||
|
||||
{
|
||||
const runResult = await run.handler({
|
||||
...DEFAULT_OPTS,
|
||||
argv: {
|
||||
original: ['pnpm', 'node', '-e', 'process.exitCode=1'],
|
||||
},
|
||||
dir: process.cwd(),
|
||||
fallbackCommandUsed: true,
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node'])
|
||||
|
||||
expect(runResult['exitCode']).toBe(1)
|
||||
}
|
||||
})
|
||||
|
||||
test('pnpm exec outside of projects', async () => {
|
||||
test('exec should set npm_config_user_agent', async () => {
|
||||
prepareEmpty()
|
||||
const userAgent = 'pnpm/0.0.0'
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: false,
|
||||
selectedProjectsGraph: {},
|
||||
}, ['node', '-e', 'require("fs").writeFileSync("output.json", "[]", "utf8")'])
|
||||
userAgent,
|
||||
}, ['eslint'])
|
||||
|
||||
const { default: outputs } = await import(path.resolve('output.json'))
|
||||
expect(outputs).toStrictEqual([])
|
||||
})
|
||||
|
||||
test('pnpm recursive exec works with PnP', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
name: 'project-1',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-1\')" | json-append ../output1.json && node -e "process.stdout.write(\'project-1\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-2',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-2\')" | json-append ../output1.json',
|
||||
postbuild: 'node -e "process.stdout.write(\'project-2-postbuild\')" | json-append ../output1.json',
|
||||
prebuild: 'node -e "process.stdout.write(\'project-2-prebuild\')" | json-append ../output1.json',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'project-3',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'json-append': '1',
|
||||
'project-1': '1',
|
||||
},
|
||||
scripts: {
|
||||
build: 'node -e "process.stdout.write(\'project-3\')" | json-append ../output2.json',
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa(pnpmBin, [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
], {
|
||||
env: {
|
||||
NPM_CONFIG_NODE_LINKER: 'pnp',
|
||||
NPM_CONFIG_SYMLINK: 'false',
|
||||
},
|
||||
})
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
}, ['npm', 'run', 'build'])
|
||||
|
||||
const { default: outputs1 } = await import(path.resolve('output1.json'))
|
||||
const { default: outputs2 } = await import(path.resolve('output2.json'))
|
||||
|
||||
expect(outputs1).toStrictEqual(['project-1', 'project-2-prebuild', 'project-2', 'project-2-postbuild'])
|
||||
expect(outputs2).toStrictEqual(['project-1', 'project-3'])
|
||||
expect(execa).toBeCalledWith('eslint', [], expect.objectContaining({
|
||||
env: expect.objectContaining({
|
||||
npm_config_user_agent: userAgent,
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from 'path'
|
||||
import packageManager from '@pnpm/cli-meta'
|
||||
import findWorkspaceDir from '@pnpm/find-workspace-dir'
|
||||
import storePath from '@pnpm/store-path'
|
||||
import npx from '@zkochan/libnpx/index'
|
||||
@@ -9,6 +10,7 @@ const PNPM_PATH = path.join(__dirname, 'pnpm.cjs')
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
; (async () => {
|
||||
const workspaceRoot = await findWorkspaceDir(process.cwd())
|
||||
process.env['npm_config_user_agent'] = `${packageManager.name}/${packageManager.version} npm/? node/${process.version} ${process.platform} ${process.arch}`
|
||||
if (workspaceRoot) {
|
||||
process.env[PATH] = `${path.join(workspaceRoot, 'node_modules/.bin')}${path.delimiter}${process.env[PATH] ?? ''}`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user