Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --group option #272

Merged
merged 12 commits into from Dec 28, 2021
15 changes: 15 additions & 0 deletions bin/concurrently.spec.ts
Expand Up @@ -184,6 +184,21 @@ describe('--hide', () => {
});
});

describe('--group', () => {
it('groups output per process', done => {
const child = run('--group "echo foo && sleep 1 && echo bar" "echo baz"');
child.log.pipe(buffer(child.close)).subscribe(lines => {
expect(lines.slice(0, 4)).toEqual([
expect.stringContaining('foo'),
expect.stringContaining('bar'),
expect.any(String),
expect.stringContaining('baz'),
]);
done();
}, done);
});
});

describe('--names', () => {
it('is aliased to -n', done => {
const child = run('-n foo,bar "echo foo" "echo bar"');
Expand Down
8 changes: 7 additions & 1 deletion bin/concurrently.ts
Expand Up @@ -65,6 +65,11 @@ const args = yargs
default: defaults.hide,
type: 'string'
},
'group': {
alias: 'g',
describe: 'Order the output as if the commands were run sequentially.',
type: 'boolean'
},
'timings': {
describe: 'Show timing information for all processes',
type: 'boolean',
Expand Down Expand Up @@ -150,7 +155,7 @@ const args = yargs
'Can be either the index or the name of the process.'
}
})
.group(['m', 'n', 'name-separator', 'raw', 's', 'no-color', 'hide', 'timings'], 'General')
.group(['m', 'n', 'name-separator', 'raw', 's', 'no-color', 'hide', 'group', 'timings'], 'General')
.group(['p', 'c', 'l', 't'], 'Prefix styling')
.group(['i', 'default-input-target'], 'Input handling')
.group(['k', 'kill-others-on-fail'], 'Killing other processes')
Expand All @@ -172,6 +177,7 @@ concurrently(args._.map((command, index) => ({
maxProcesses: args.maxProcesses,
raw: args.raw,
hide: args.hide.split(','),
group: args.group,
prefix: args.prefix,
prefixColors: args['prefix-colors'].split(','),
prefixLength: args.prefixLength,
Expand Down
4 changes: 3 additions & 1 deletion index.js
Expand Up @@ -13,7 +13,6 @@ const LogTimings = require( './src/flow-control/log-timings' );
module.exports = exports = (commands, options = {}) => {
const logger = new Logger({
hide: options.hide,
outputStream: options.outputStream || process.stdout,
prefixFormat: options.prefix,
prefixLength: options.prefixLength,
raw: options.raw,
Expand All @@ -25,6 +24,9 @@ module.exports = exports = (commands, options = {}) => {
raw: options.raw,
successCondition: options.successCondition,
cwd: options.cwd,
logger,
outputStream: options.outputStream || process.stdout,
group: options.group,
controllers: [
new LogError({ logger }),
new LogOutput({ logger }),
Expand Down
3 changes: 3 additions & 0 deletions src/command.ts
Expand Up @@ -59,6 +59,7 @@ export class Command implements CommandInfo {
stdin?: Writable;
pid?: number;
killed = false;
exited = false;

get killable() {
return !!this.process;
Expand Down Expand Up @@ -96,6 +97,8 @@ export class Command implements CommandInfo {
});
Rx.fromEvent<[number | null, NodeJS.Signals | null]>(child, 'close').subscribe(([exitCode, signal]) => {
this.process = undefined;
this.exited = true;

const endDate = new Date(Date.now());
this.timer.next({ startDate, endDate });
const [durationSeconds, durationNanoSeconds] = process.hrtime(highResStartTime);
Expand Down
11 changes: 11 additions & 0 deletions src/concurrently.js
Expand Up @@ -11,6 +11,7 @@ const CompletionListener = require('./completion-listener');

const { getSpawnOpts } = require('./get-spawn-opts');
const { Command } = require('./command');
const { OutputWriter } = require('./output-writer');

const defaults = {
spawn,
Expand Down Expand Up @@ -63,6 +64,16 @@ module.exports = (commands, options) => {
);
commands = handleResult.commands;


if (options.logger) {
const outputWriter = new OutputWriter({
outputStream: options.outputStream,
group: options.group,
commands,
});
options.logger.output.subscribe(({command, text}) => outputWriter.write(command, text));
}

const commandsLeft = commands.slice();
const maxProcesses = Math.max(1, Number(options.maxProcesses) || commandsLeft.length);
for (let i = 0; i < maxProcesses; i++) {
Expand Down
21 changes: 13 additions & 8 deletions src/logger.js
@@ -1,20 +1,21 @@
const chalk = require('chalk');
const _ = require('lodash');
const formatDate = require('date-fns/format');
const Rx = require('rxjs');

const defaults = require('./defaults');

module.exports = class Logger {
constructor({ hide, outputStream, prefixFormat, prefixLength, raw, timestampFormat }) {
constructor({ hide, prefixFormat, prefixLength, raw, timestampFormat }) {
// To avoid empty strings from hiding the output of commands that don't have a name,
// keep in the list of commands to hide only strings with some length.
// This might happen through the CLI when no `--hide` argument is specified, for example.
this.hide = _.castArray(hide).filter(name => name || name === 0).map(String);
this.raw = raw;
this.outputStream = outputStream;
this.prefixFormat = prefixFormat;
this.prefixLength = prefixLength || defaults.prefixLength;
this.timestampFormat = timestampFormat || defaults.timestampFormat;
this.output = new Rx.Subject();
}

shortenText(text) {
Expand Down Expand Up @@ -85,15 +86,15 @@ module.exports = class Logger {
}

const prefix = this.colorText(command, this.getPrefix(command));
return this.log(prefix + (prefix ? ' ' : ''), text);
return this.log(prefix + (prefix ? ' ' : ''), text, command);
}

logGlobalEvent(text) {
if (this.raw) {
return;
}

this.log(chalk.reset('-->') + ' ', chalk.reset(text) + '\n');
this.log(chalk.reset('-->') + ' ', chalk.reset(text) + '\n', null);
}

logTable(tableContents) {
Expand Down Expand Up @@ -153,9 +154,9 @@ module.exports = class Logger {
this.logGlobalEvent(`└─${borderRowFormatted.join('─┴─')}─┘`);
}

log(prefix, text) {
log(prefix, text, command) {
if (this.raw) {
return this.outputStream.write(text);
return this.emit(command, text);
}

// #70 - replace some ANSI code that would impact clearing lines
Expand All @@ -171,10 +172,14 @@ module.exports = class Logger {
});

if (!this.lastChar || this.lastChar === '\n') {
this.outputStream.write(prefix);
this.emit(command, prefix);
}

this.lastChar = text[text.length - 1];
this.outputStream.write(lines.join('\n'));
this.emit(command, lines.join('\n'));
}

emit(command, text) {
this.output.next({ command, text });
}
};