123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- "use strict";
- const pMap = require("p-map");
- const childProcess = require("@lerna/child-process");
- const { Command } = require("@lerna/command");
- const { Profiler } = require("@lerna/profiler");
- const { runTopologically } = require("@lerna/run-topologically");
- const { ValidationError } = require("@lerna/validation-error");
- const { getFilteredPackages } = require("@lerna/filter-options");
- module.exports = factory;
- function factory(argv) {
- return new ExecCommand(argv);
- }
- class ExecCommand extends Command {
- get requiresGit() {
- return false;
- }
- initialize() {
- const dashedArgs = this.options["--"] || [];
- this.command = this.options.cmd || dashedArgs.shift();
- this.args = (this.options.args || []).concat(dashedArgs);
- if (!this.command) {
- throw new ValidationError("ENOCOMMAND", "A command to execute is required");
- }
- // inverted boolean options
- this.bail = this.options.bail !== false;
- this.prefix = this.options.prefix !== false;
- // accessing properties of process.env can be expensive,
- // so cache it here to reduce churn during tighter loops
- this.env = Object.assign({}, process.env);
- let chain = Promise.resolve();
- chain = chain.then(() => getFilteredPackages(this.packageGraph, this.execOpts, this.options));
- chain = chain.then((filteredPackages) => {
- this.filteredPackages = filteredPackages;
- });
- return chain.then(() => {
- this.count = this.filteredPackages.length;
- this.packagePlural = this.count === 1 ? "package" : "packages";
- this.joinedCommand = [this.command].concat(this.args).join(" ");
- });
- }
- execute() {
- this.logger.info(
- "",
- "Executing command in %d %s: %j",
- this.count,
- this.packagePlural,
- this.joinedCommand
- );
- let chain = Promise.resolve();
- if (this.options.parallel) {
- chain = chain.then(() => this.runCommandInPackagesParallel());
- } else if (this.toposort) {
- chain = chain.then(() => this.runCommandInPackagesTopological());
- } else {
- chain = chain.then(() => this.runCommandInPackagesLexical());
- }
- if (this.bail) {
- // only the first error is caught
- chain = chain.catch((err) => {
- process.exitCode = err.exitCode;
- // rethrow to halt chain and log properly
- throw err;
- });
- } else {
- // detect error (if any) from collected results
- chain = chain.then((results) => {
- /* istanbul ignore else */
- if (results.some((result) => result.failed)) {
- // propagate "highest" error code, it's probably the most useful
- const codes = results.filter((result) => result.failed).map((result) => result.exitCode);
- const exitCode = Math.max(...codes, 1);
- this.logger.error("", "Received non-zero exit code %d during execution", exitCode);
- process.exitCode = exitCode;
- }
- });
- }
- return chain.then(() => {
- this.logger.success(
- "exec",
- "Executed command in %d %s: %j",
- this.count,
- this.packagePlural,
- this.joinedCommand
- );
- });
- }
- getOpts(pkg) {
- // these options are passed _directly_ to execa
- return {
- cwd: pkg.location,
- shell: true,
- extendEnv: false,
- env: Object.assign({}, this.env, {
- LERNA_PACKAGE_NAME: pkg.name,
- LERNA_ROOT_PATH: this.project.rootPath,
- }),
- reject: this.bail,
- pkg,
- };
- }
- getRunner() {
- return this.options.stream
- ? (pkg) => this.runCommandInPackageStreaming(pkg)
- : (pkg) => this.runCommandInPackageCapturing(pkg);
- }
- runCommandInPackagesTopological() {
- let profiler;
- let runner;
- if (this.options.profile) {
- profiler = new Profiler({
- concurrency: this.concurrency,
- log: this.logger,
- outputDirectory: this.options.profileLocation || this.project.rootPath,
- });
- const callback = this.getRunner();
- runner = (pkg) => profiler.run(() => callback(pkg), pkg.name);
- } else {
- runner = this.getRunner();
- }
- let chain = runTopologically(this.filteredPackages, runner, {
- concurrency: this.concurrency,
- rejectCycles: this.options.rejectCycles,
- });
- if (profiler) {
- chain = chain.then((results) => profiler.output().then(() => results));
- }
- return chain;
- }
- runCommandInPackagesParallel() {
- return pMap(this.filteredPackages, (pkg) => this.runCommandInPackageStreaming(pkg));
- }
- runCommandInPackagesLexical() {
- return pMap(this.filteredPackages, this.getRunner(), { concurrency: this.concurrency });
- }
- runCommandInPackageStreaming(pkg) {
- return childProcess.spawnStreaming(this.command, this.args, this.getOpts(pkg), this.prefix && pkg.name);
- }
- runCommandInPackageCapturing(pkg) {
- return childProcess.spawn(this.command, this.args, this.getOpts(pkg));
- }
- }
- module.exports.ExecCommand = ExecCommand;
|