Files
pnpm/packages/plugin-commands-script-runners/src/exec.ts
Zoltan Kochan 6afba68819 fix(exec): exec should fail with better error
"pnpm exec" currently works only when executed recursively,
so it should fail with a meaningful error if "-r" is not
present.

close #2461
2020-04-12 12:35:22 +03:00

95 lines
2.4 KiB
TypeScript

import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
import { Config, types } from '@pnpm/config'
import PnpmError from '@pnpm/error'
import logger from '@pnpm/logger'
import sortPackages from '@pnpm/sort-packages'
import execa = require('execa')
import pLimit from 'p-limit'
import R = require('ramda')
import renderHelp = require('render-help')
export const commandNames = ['exec']
export function rcOptionsTypes () {
return R.pick([
'bail',
'sort',
'unsafe-perm',
'workspace-concurrency',
], types)
}
export const cliOptionsTypes = () => ({
...rcOptionsTypes(),
recursive: Boolean,
})
export function help () {
return renderHelp({
description: 'Run a command in each package.',
usages: ['-r exec -- <command> [args...]'],
})
}
export async function handler (
opts: Required<Pick<Config, 'selectedProjectsGraph'>> & {
bail?: boolean,
unsafePerm?: boolean,
rawConfig: object,
sort?: boolean,
workspaceConcurrency?: number,
} & Pick<Config, 'recursive'>,
params: string[],
) {
if (!opts.recursive) {
throw new PnpmError('EXEC_NOT_RECURSIVE', 'The "pnpm exec" command currently only works with the "-r" option')
}
const limitRun = pLimit(opts.workspaceConcurrency ?? 4)
const result = {
fails: [],
passes: 0,
} as RecursiveSummary
const chunks = opts.sort
? sortPackages(opts.selectedProjectsGraph)
: [Object.keys(opts.selectedProjectsGraph).sort()]
for (const chunk of chunks) {
await Promise.all(chunk.map((prefix: string) =>
limitRun(async () => {
try {
await execa(params[0], params.slice(1), {
cwd: prefix,
env: {
...process.env,
PNPM_PACKAGE_NAME: opts.selectedProjectsGraph[prefix].package.manifest.name,
},
stdio: 'inherit',
})
result.passes++
} catch (err) {
logger.info(err)
if (!opts.bail) {
result.fails.push({
error: err,
message: err.message,
prefix,
})
return
}
// tslint:disable:no-string-literal
err['code'] = 'ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL'
err['prefix'] = prefix
// tslint:enable:no-string-literal
throw err
}
},
)))
}
throwOnCommandFail('pnpm recursive exec', result)
}