UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

204 lines (203 loc) • 9.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StaticRunManyTerminalOutputLifeCycle = void 0; const output_1 = require("../../utils/output"); const utils_1 = require("../utils"); const formatting_utils_1 = require("./formatting-utils"); /** * The following life cycle's outputs are static, meaning no previous content * is rewritten or modified as new outputs are added. It is therefore intended * for use in CI environments. * * For the common case of a user executing a command on their local machine, * the dynamic equivalent of this life cycle is usually preferable. */ class StaticRunManyTerminalOutputLifeCycle { constructor(projectNames, tasks, args, taskOverrides) { this.projectNames = projectNames; this.tasks = tasks; this.args = args; this.taskOverrides = taskOverrides; this.failedTasks = []; this.cachedTasks = []; this.stoppedTasks = []; this.allCompletedTasks = new Map(); this.collapsedTasks = 0; /** Stopped tasks that produced output; a batch-stopped task has none. */ this.stoppedTasksWithOutput = 0; } startCommand() { if (this.tasks.length === 0) { return; } if (this.projectNames.length <= 0) { output_1.output.logSingleLine(`No projects with ${(0, formatting_utils_1.formatTargetsAndProjects)(this.projectNames, this.args.targets, this.tasks)} were run`); return; } const bodyLines = this.projectNames.map((affectedProject) => `${output_1.output.dim('-')} ${affectedProject}`); const filteredOverrides = Object.entries(this.taskOverrides).filter( // Don't print the data passed through from the version subcommand to the publish executor options, it could be quite large and it's an implementation detail. ([flag]) => flag !== 'nxReleaseVersionData'); if (filteredOverrides.length > 0) { bodyLines.push(''); bodyLines.push(`${output_1.output.dim('With additional flags:')}`); filteredOverrides .map(([flag, value]) => (0, formatting_utils_1.formatFlags)('', flag, value)) .forEach((arg) => bodyLines.push(arg)); } const title = `Running ${(0, formatting_utils_1.formatTargetsAndProjects)(this.projectNames, this.args.targets, this.tasks)}:`; output_1.output.log({ color: 'cyan', title, bodyLines, }); output_1.output.addVerticalSeparatorWithoutNewLines('cyan'); } endCommand() { output_1.output.addNewline(); if (this.tasks.length === 0) { output_1.output.logSingleLine(`No tasks were run`); return; } // A stopped task was killed mid-flight, so the run did not complete even // though nothing outright failed. `didCommandComplete` already treats it // that way; reporting "Successfully ran" here would contradict it. if (this.failedTasks.length === 0 && this.stoppedTasks.length === 0) { output_1.output.addVerticalSeparatorWithoutNewLines('green'); const bodyLines = this.cachedTasks.length > 0 ? [ output_1.output.dim(`Nx read the output from the cache instead of running the command for ${this.cachedTasks.length} out of ${this.tasks.length} tasks.`), ] : []; bodyLines.push(...this.tasksNotRunSummary()); bodyLines.push(...this.hiddenOutputHint()); output_1.output.success({ title: `Successfully ran ${(0, formatting_utils_1.formatTargetsAndProjects)(this.projectNames, this.args.targets, this.tasks)}`, bodyLines, }); } else { output_1.output.addVerticalSeparatorWithoutNewLines('red'); const bodyLines = []; const skippedTasks = this.skippedTasks(); if (skippedTasks.length > 0) { bodyLines.push(output_1.output.dim('Tasks not run because their dependencies failed or --nx-bail=true:'), '', ...skippedTasks.map((task) => `${output_1.output.dim('-')} ${task.id}`), ''); } if (this.stoppedTasks.length > 0) { bodyLines.push(output_1.output.dim('Tasks stopped before they finished:'), '', ...this.stoppedTasks.map((task) => `${output_1.output.dim('-')} ${task.id}`), ''); } if (this.failedTasks.length > 0) { bodyLines.push(output_1.output.dim('Failed tasks:'), '', ...[...this.failedTasks.values()].map((task) => `${output_1.output.dim('-')} ${task.id}`)); } bodyLines.push(...this.hiddenOutputHint()); const targets = (0, formatting_utils_1.formatTargetsAndProjects)(this.projectNames, this.args.targets, this.tasks); output_1.output.error({ title: this.failedTasks.length > 0 ? `Running ${targets} failed` : `Running ${targets} did not complete`, bodyLines, }); } } /** * Tasks with a `skipped` status are never reported through `endTasks`, so * they are derived by subtracting everything that did complete. */ skippedTasks() { return this.tasks.filter((t) => !this.allCompletedTasks.has(t.id)); } /** * Whether this run prints every task's output in full rather than collapsing * the ones that succeeded. */ get printsFullOutput() { return (0, output_1.printsFullTaskOutput)(this.args); } /** * Tells the reader that output was withheld, so a task that succeeded while * printing something worth reading is not silently swallowed. */ hiddenOutputHint() { const withheld = []; if (this.collapsedTasks > 0) { withheld.push(`${this.collapsedTasks} successful ${this.collapsedTasks === 1 ? 'task' : 'tasks'}`); } // A stopped task's partial output is what diagnoses a hang, and it is // dropped by default, so say so even when nothing collapsed. if (this.stoppedTasksWithOutput > 0) { withheld.push(`${this.stoppedTasksWithOutput} stopped ${this.stoppedTasksWithOutput === 1 ? 'task' : 'tasks'}`); } if (this.printsFullOutput || withheld.length === 0) { return []; } const total = this.collapsedTasks + this.stoppedTasksWithOutput; return [ '', `${output_1.output.dim(`Output of ${withheld.join(' and ')} ${total === 1 && withheld.length === 1 ? 'was' : 'were'} not shown. Run with`)} --verbose ${output_1.output.dim('or')} --output-style=static ${output_1.output.dim('to see it.')}`, ]; } /** * Tasks that never produced output worth printing are summarized as counts, * with their names available behind --verbose. */ tasksNotRunSummary() { const skippedTasks = this.skippedTasks(); const counts = []; if (skippedTasks.length > 0) { counts.push(`${skippedTasks.length} skipped`); } if (this.stoppedTasks.length > 0) { counts.push(`${this.stoppedTasks.length} stopped`); } if (counts.length === 0) { return []; } const lines = [output_1.output.dim(counts.join(', '))]; if (this.printsFullOutput) { lines.push('', ...[...skippedTasks, ...this.stoppedTasks].map((task) => `${output_1.output.dim('-')} ${task.id}`)); } return lines; } endTasks(taskResults) { for (let t of taskResults) { this.allCompletedTasks.set(t.task.id, t.task); if (t.status === 'failure') { this.failedTasks.push(t.task); } else if (t.status === 'stopped') { this.stoppedTasks.push(t.task); // A batch-stopped task is reported with no output at all, so counting // it would have the hint promise something --verbose cannot show. if (t.terminalOutput) { this.stoppedTasksWithOutput++; } } else if (t.status === 'local-cache') { this.cachedTasks.push(t.task); } else if (t.status === 'local-cache-kept-existing') { this.cachedTasks.push(t.task); } else if (t.status === 'remote-cache') { this.cachedTasks.push(t.task); } } } printTaskTerminalOutput(task, taskStatus, terminalOutput) { const args = (0, utils_1.getPrintableCommandArgsForTask)(task); if (this.printsFullOutput || taskStatus === 'failure') { // A stopped task was killed part way through; under --verbose or // --output-style=static its partial output is shown, which is what // diagnoses a hang. It is dropped on the default path below. output_1.output.logCommandOutput(args.join(' '), taskStatus, terminalOutput); return; } // Named in the end of run summary instead; output not shown by default. if (taskStatus === 'skipped' || taskStatus === 'stopped') { return; } this.collapsedTasks++; output_1.output.logCommandSummary(args.join(' '), taskStatus); } } exports.StaticRunManyTerminalOutputLifeCycle = StaticRunManyTerminalOutputLifeCycle;