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
7 changes: 6 additions & 1 deletion bin/concurrently.js
Expand Up @@ -62,6 +62,10 @@ const args = yargs
default: defaults.hide,
type: 'string'
},
'group': {
describe: 'Order the output as if the commands were run sequentially.',
type: 'boolean'
},

// Kill others
'k': {
Expand Down Expand Up @@ -142,7 +146,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'], 'General')
.group(['m', 'n', 'name-separator', 'raw', 's', 'no-color', 'hide', 'group'], '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 @@ -165,6 +169,7 @@ concurrently(args._.map((command, index) => ({
maxProcesses: args.maxProcesses,
raw: args.raw,
hide: args.hide.split(','),
group: args.group,
prefix: args.prefix,
prefixColors: args.prefixColors.split(','),
prefixLength: args.prefixLength,
Expand Down
4 changes: 3 additions & 1 deletion index.js
Expand Up @@ -12,7 +12,6 @@ const Logger = require('./src/logger');
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 @@ -24,6 +23,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
2 changes: 2 additions & 0 deletions src/command.js
Expand Up @@ -20,6 +20,7 @@ module.exports = class Command {
this.close = new Rx.Subject();
this.stdout = new Rx.Subject();
this.stderr = new Rx.Subject();
this.exited = false;
}

start() {
Expand All @@ -33,6 +34,7 @@ module.exports = class Command {
});
Rx.fromEvent(child, 'close').subscribe(([exitCode, signal]) => {
this.process = undefined;
this.exited = true;
this.close.next({
command: {
command: this.command,
Expand Down
10 changes: 10 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 @@ -73,6 +74,15 @@ module.exports = (commands, options) => {
maybeRunMore(commandsLeft);
}

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a nicer API... what do you think if Logger had a subscribe method?

Suggested change
options.logger.observable.subscribe(({command, text}) => outputWriter.write(command, text));
options.logger.subscribe(({command, text}) => outputWriter.write(command, text));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially had this actually! I ended up removing it because then it made look logger look like an Observable, which I thought might be confusing (cause then folks might expect it to have other Observable methods/props). Then I thought maybe I should make Logger extends Observable, and then I decided I'd just KISS and use composition :P

}

return new CompletionListener({ successCondition: options.successCondition })
.listen(commands)
.finally(() => {
Expand Down
2 changes: 2 additions & 0 deletions src/defaults.js
Expand Up @@ -20,6 +20,8 @@ module.exports = {
// How many bytes we'll show on the command prefix
prefixLength: 10,
raw: false,
// Whether to display output ordered as if the commands were sequential
group: false,
// Number of attempts of restarting a process, if it exits with non-0 code
restartTries: 0,
// How many milliseconds concurrently should wait before restarting a process.
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.observable = new Rx.Subject();
}

shortenText(text) {
Expand Down Expand Up @@ -85,20 +86,20 @@ 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);
}

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 @@ -114,10 +115,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.observable.next({ command, text });
}
};