concurrently 3.6.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.travis.yml +3 -4
- package/.vscode/settings.json +5 -0
- package/CONTRIBUTING.md +1 -4
- package/README.md +131 -94
- package/appveyor.yml +1 -1
- package/bin/concurrently.js +164 -0
- package/bin/concurrently.spec.js +340 -0
- package/bin/epilogue.txt +46 -0
- package/{test/support → bin/fixtures}/read-echo.js +0 -2
- package/index.js +56 -0
- package/package.json +28 -31
- package/src/command-parser/expand-npm-shortcut.js +13 -0
- package/src/command-parser/expand-npm-shortcut.spec.js +36 -0
- package/src/command-parser/expand-npm-wildcard.js +34 -0
- package/src/command-parser/expand-npm-wildcard.spec.js +58 -0
- package/src/command-parser/strip-quotes.js +12 -0
- package/src/command-parser/strip-quotes.spec.js +14 -0
- package/src/command.js +50 -0
- package/src/command.spec.js +142 -0
- package/src/completion-listener.js +35 -0
- package/src/completion-listener.spec.js +88 -0
- package/src/concurrently.js +69 -0
- package/src/concurrently.spec.js +77 -0
- package/src/defaults.js +27 -0
- package/src/flow-control/fixtures/fake-command.js +16 -0
- package/src/flow-control/input-handler.js +39 -0
- package/src/flow-control/input-handler.spec.js +79 -0
- package/src/flow-control/kill-on-signal.js +29 -0
- package/src/flow-control/kill-on-signal.spec.js +70 -0
- package/src/flow-control/kill-others.js +35 -0
- package/src/flow-control/kill-others.spec.js +66 -0
- package/src/flow-control/log-error.js +20 -0
- package/src/flow-control/log-error.spec.js +40 -0
- package/src/flow-control/log-exit.js +13 -0
- package/src/flow-control/log-exit.spec.js +36 -0
- package/src/flow-control/log-output.js +14 -0
- package/src/flow-control/log-output.spec.js +41 -0
- package/src/flow-control/restart-process.js +51 -0
- package/src/flow-control/restart-process.spec.js +129 -0
- package/src/get-spawn-opts.js +12 -0
- package/src/get-spawn-opts.spec.js +18 -0
- package/src/logger.js +109 -0
- package/src/logger.spec.js +178 -0
- package/src/findChild.js +0 -11
- package/src/main.js +0 -563
- package/src/parseCmds.js +0 -65
- package/src/pkgInfo.js +0 -17
- package/test/support/signal.js +0 -13
- package/test/test-findChild.js +0 -39
- package/test/test-functional.js +0 -396
- package/test/test-parseCmds.js +0 -204
- package/test/utils.js +0 -68
package/src/main.js
DELETED
|
@@ -1,563 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const Rx = require('rx');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const formatDate = require('date-fns/format');
|
|
7
|
-
const program = require('commander');
|
|
8
|
-
const _ = require('lodash');
|
|
9
|
-
const treeKill = require('tree-kill');
|
|
10
|
-
const chalk = require('chalk');
|
|
11
|
-
const spawn = require('spawn-command');
|
|
12
|
-
const supportsColor = require('supports-color');
|
|
13
|
-
const IS_WINDOWS = /^win/.test(process.platform);
|
|
14
|
-
|
|
15
|
-
const findChild = require('./findChild.js');
|
|
16
|
-
const parseCmds = require('./parseCmds');
|
|
17
|
-
|
|
18
|
-
let config = {
|
|
19
|
-
// Kill other processes if one dies
|
|
20
|
-
killOthers: false,
|
|
21
|
-
|
|
22
|
-
// Kill other processes if one exits with non zero status code
|
|
23
|
-
killOthersOnFail: false,
|
|
24
|
-
|
|
25
|
-
// Return success or failure of the 'first' child to terminate, the 'last' child,
|
|
26
|
-
// or succeed only if 'all' children succeed
|
|
27
|
-
success: 'all',
|
|
28
|
-
|
|
29
|
-
// Prefix logging with pid
|
|
30
|
-
// Possible values: 'pid', 'none', 'time', 'command', 'index', 'name'
|
|
31
|
-
prefix: '',
|
|
32
|
-
|
|
33
|
-
// List of custom names to be used in prefix template
|
|
34
|
-
names: '',
|
|
35
|
-
|
|
36
|
-
// What to split the list of custom names on
|
|
37
|
-
nameSeparator: ',',
|
|
38
|
-
|
|
39
|
-
// Comma-separated list of chalk color paths to use on prefixes.
|
|
40
|
-
prefixColors: 'gray.dim',
|
|
41
|
-
|
|
42
|
-
// moment/date-fns format
|
|
43
|
-
timestampFormat: 'YYYY-MM-DD HH:mm:ss.SSS',
|
|
44
|
-
|
|
45
|
-
// How many characters to display from start of command in prefix if
|
|
46
|
-
// command is defined. Note that also '..' will be added in the middle
|
|
47
|
-
prefixLength: 10,
|
|
48
|
-
|
|
49
|
-
// By default, color output
|
|
50
|
-
color: true,
|
|
51
|
-
|
|
52
|
-
// If true, the output will only be raw output of processes, nothing more
|
|
53
|
-
raw: false,
|
|
54
|
-
|
|
55
|
-
// If true, the process restart when it exited with status code non-zero
|
|
56
|
-
allowRestart: false,
|
|
57
|
-
|
|
58
|
-
// By default, restart instantly
|
|
59
|
-
restartAfter: 0,
|
|
60
|
-
|
|
61
|
-
// By default, restart once
|
|
62
|
-
restartTries: 1,
|
|
63
|
-
|
|
64
|
-
// Default identifier for child to which input on stdin should be sent.
|
|
65
|
-
defaultInputTarget: '0'
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
function main() {
|
|
69
|
-
const firstBase = path.basename(process.argv[0]);
|
|
70
|
-
const secondBase = path.basename(process.argv[1]);
|
|
71
|
-
if (firstBase === 'concurrent' || secondBase === 'concurrent') {
|
|
72
|
-
console.error('Warning: "concurrent" command is deprecated, use "concurrently" instead.\n');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
parseArgs();
|
|
76
|
-
config = mergeDefaultsWithArgs(config);
|
|
77
|
-
|
|
78
|
-
const cmds = parseCmds(program.args, config);
|
|
79
|
-
run(cmds);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function parseArgs() {
|
|
83
|
-
program
|
|
84
|
-
.version(require('../package.json').version)
|
|
85
|
-
.usage('[options] <command ...>')
|
|
86
|
-
.option(
|
|
87
|
-
'-k, --kill-others',
|
|
88
|
-
'kill other processes if one exits or dies'
|
|
89
|
-
)
|
|
90
|
-
.option(
|
|
91
|
-
'--kill-others-on-fail',
|
|
92
|
-
'kill other processes if one exits with non zero status code'
|
|
93
|
-
)
|
|
94
|
-
.option(
|
|
95
|
-
'--no-color',
|
|
96
|
-
'disable colors from logging'
|
|
97
|
-
)
|
|
98
|
-
.option(
|
|
99
|
-
'-p, --prefix <prefix>',
|
|
100
|
-
'prefix used in logging for each process.\n' +
|
|
101
|
-
'Possible values: index, pid, time, command, name, none, or a template. Default: ' +
|
|
102
|
-
'index or name (when --names is set). Example template: "{time}-{pid}"\n'
|
|
103
|
-
)
|
|
104
|
-
.option(
|
|
105
|
-
'-n, --names <names>',
|
|
106
|
-
'List of custom names to be used in prefix template.\n' +
|
|
107
|
-
'Example names: "main,browser,server"\n'
|
|
108
|
-
)
|
|
109
|
-
.option(
|
|
110
|
-
'--name-separator <char>',
|
|
111
|
-
'The character to split <names> on.\n' +
|
|
112
|
-
'Default: "' + config.nameSeparator + '". Example usage: ' +
|
|
113
|
-
'concurrently -n "styles,scripts|server" --name-separator "|" <command ...>\n'
|
|
114
|
-
)
|
|
115
|
-
.option(
|
|
116
|
-
'-c, --prefix-colors <colors>',
|
|
117
|
-
'Comma-separated list of chalk colors to use on prefixes. If there are more commands than colors, the last color will be repeated.\n' +
|
|
118
|
-
'Available modifiers: reset, bold, dim, italic, underline, inverse, hidden, strikethrough\n' +
|
|
119
|
-
'Available colors: black, red, green, yellow, blue, magenta, cyan, white, gray\n' +
|
|
120
|
-
'Available background colors: bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite\n' +
|
|
121
|
-
'See https://www.npmjs.com/package/chalk for more information.\n' +
|
|
122
|
-
'Default: "' + config.prefixColors + '". Example: "black.bgWhite,cyan,gray.dim"\n'
|
|
123
|
-
)
|
|
124
|
-
.option(
|
|
125
|
-
'-t, --timestamp-format <format>',
|
|
126
|
-
'specify the timestamp in moment/date-fns format. Default: ' +
|
|
127
|
-
config.timestampFormat + '\n'
|
|
128
|
-
)
|
|
129
|
-
.option(
|
|
130
|
-
'-r, --raw',
|
|
131
|
-
'output only raw output of processes,' +
|
|
132
|
-
' disables prettifying and concurrently coloring'
|
|
133
|
-
)
|
|
134
|
-
.option(
|
|
135
|
-
'-s, --success <first|last|all>',
|
|
136
|
-
'Return exit code of zero or one based on the success or failure ' +
|
|
137
|
-
'of the "first" child to terminate, the "last" child, or succeed ' +
|
|
138
|
-
' only if "all" child processes succeed. Default: ' +
|
|
139
|
-
config.success + '\n'
|
|
140
|
-
)
|
|
141
|
-
.option(
|
|
142
|
-
'-l, --prefix-length <length>',
|
|
143
|
-
'limit how many characters of the command is displayed in prefix.\n' +
|
|
144
|
-
'The option can be used to shorten long commands.\n' +
|
|
145
|
-
'Works only if prefix is set to "command". Default: ' +
|
|
146
|
-
config.prefixLength + '\n'
|
|
147
|
-
)
|
|
148
|
-
.option(
|
|
149
|
-
'--allow-restart',
|
|
150
|
-
'Restart a process which died. Default: ' +
|
|
151
|
-
config.allowRestart + '\n'
|
|
152
|
-
)
|
|
153
|
-
.option(
|
|
154
|
-
'--restart-after <miliseconds>',
|
|
155
|
-
'delay time to respawn the process. Default: ' +
|
|
156
|
-
config.restartAfter + '\n'
|
|
157
|
-
)
|
|
158
|
-
.option(
|
|
159
|
-
'--restart-tries <times>',
|
|
160
|
-
'limit the number of respawn tries. Default: ' +
|
|
161
|
-
config.restartTries + '\n'
|
|
162
|
-
|
|
163
|
-
)
|
|
164
|
-
.option(
|
|
165
|
-
'--default-input-target <identifier>',
|
|
166
|
-
'identifier for child process to which input on ' +
|
|
167
|
-
'stdin should be sent if not specified at start ' +
|
|
168
|
-
'of input. Can be either the index or the name ' +
|
|
169
|
-
'of the process. Default: ' + config.defaultInputTarget + '\n'
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
program.on('--help', function() {
|
|
173
|
-
const help = [
|
|
174
|
-
' Input:',
|
|
175
|
-
'',
|
|
176
|
-
' Input can be sent to any of the child processes using either the name or',
|
|
177
|
-
' index of the command followed by a colon. If no child identifier is',
|
|
178
|
-
' specified then the input will be sent to the child specified by the',
|
|
179
|
-
' `--default-input-target` option, which defaults to index 0.',
|
|
180
|
-
'',
|
|
181
|
-
' Examples:',
|
|
182
|
-
'',
|
|
183
|
-
' - Kill other processes if one exits or dies',
|
|
184
|
-
'',
|
|
185
|
-
' $ concurrently --kill-others "grunt watch" "http-server"',
|
|
186
|
-
'',
|
|
187
|
-
' - Kill other processes if one exits with non zero status code',
|
|
188
|
-
'',
|
|
189
|
-
' $ concurrently --kill-others-on-fail "npm run build:client" "npm run build:server"',
|
|
190
|
-
'',
|
|
191
|
-
' - Output nothing more than stdout+stderr of child processes',
|
|
192
|
-
'',
|
|
193
|
-
' $ concurrently --raw "npm run watch-less" "npm run watch-js"',
|
|
194
|
-
'',
|
|
195
|
-
' - Normal output but without colors e.g. when logging to file',
|
|
196
|
-
'',
|
|
197
|
-
' $ concurrently --no-color "grunt watch" "http-server" > log',
|
|
198
|
-
'',
|
|
199
|
-
' - Custom prefix',
|
|
200
|
-
'',
|
|
201
|
-
' $ concurrently --prefix "{time}-{pid}" "npm run watch" "http-server"',
|
|
202
|
-
'',
|
|
203
|
-
' - Custom names and colored prefixes',
|
|
204
|
-
'',
|
|
205
|
-
' $ concurrently --names "HTTP,WATCH" -c "bgBlue.bold,bgMagenta.bold" "http-server" "npm run watch"',
|
|
206
|
-
'',
|
|
207
|
-
' - Send input to default',
|
|
208
|
-
'',
|
|
209
|
-
' $ concurrently "nodemon" "npm run watch-js"',
|
|
210
|
-
' rs # Sends rs command to nodemon process',
|
|
211
|
-
'',
|
|
212
|
-
' - Specify a default-input-target',
|
|
213
|
-
'',
|
|
214
|
-
' $ concurrently --default-input-target 1 "npm run watch-js" nodemon',
|
|
215
|
-
' rs',
|
|
216
|
-
'',
|
|
217
|
-
' - Send input to specific child identified by index',
|
|
218
|
-
'',
|
|
219
|
-
' $ concurrently "npm run watch-js" nodemon',
|
|
220
|
-
' 1:rs',
|
|
221
|
-
'',
|
|
222
|
-
' - Send input to specific child identified by name',
|
|
223
|
-
'',
|
|
224
|
-
' $ concurrently -n js,srv "npm run watch-js" nodemon',
|
|
225
|
-
' srv:rs',
|
|
226
|
-
'',
|
|
227
|
-
' - Shortened NPM run commands',
|
|
228
|
-
'',
|
|
229
|
-
' $ concurrently npm:watch-node npm:watch-js npm:watch-css',
|
|
230
|
-
'',
|
|
231
|
-
' - Shortened NPM run command with wildcard',
|
|
232
|
-
'',
|
|
233
|
-
' $ concurrently npm:watch-*',
|
|
234
|
-
''
|
|
235
|
-
];
|
|
236
|
-
console.log(help.join('\n'));
|
|
237
|
-
|
|
238
|
-
const url = 'https://github.com/kimmobrunfeldt/concurrently';
|
|
239
|
-
console.log(' For more details, visit ' + url);
|
|
240
|
-
console.log('');
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
program.parse(process.argv);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function mergeDefaultsWithArgs(config) {
|
|
247
|
-
// This will pollute config object with other attributes from program too
|
|
248
|
-
return _.merge(config, program);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function run(commands) {
|
|
252
|
-
const childrenInfo = {};
|
|
253
|
-
let lastPrefixColor = _.get(chalk, chalk.gray.dim);
|
|
254
|
-
const children = _.map(commands, function(cmdInfo, index) {
|
|
255
|
-
|
|
256
|
-
const spawnOpts = config.raw ? {stdio: 'inherit'} : {};
|
|
257
|
-
if (IS_WINDOWS) {
|
|
258
|
-
spawnOpts.detached = false;
|
|
259
|
-
}
|
|
260
|
-
if (supportsColor) {
|
|
261
|
-
spawnOpts.env = Object.assign({FORCE_COLOR: supportsColor.level}, process.env);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const child = spawnChild(cmdInfo.cmd, spawnOpts);
|
|
265
|
-
|
|
266
|
-
if (cmdInfo.color) {
|
|
267
|
-
const prefixColorPath = cmdInfo.color;
|
|
268
|
-
lastPrefixColor = _.get(chalk, prefixColorPath, chalk.gray.dim);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
childrenInfo[child.pid] = {
|
|
272
|
-
command: cmdInfo.cmd,
|
|
273
|
-
index: index,
|
|
274
|
-
name: cmdInfo.name,
|
|
275
|
-
options: spawnOpts,
|
|
276
|
-
restartTries: config.restartTries,
|
|
277
|
-
prefixColor: lastPrefixColor
|
|
278
|
-
};
|
|
279
|
-
return child;
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
const streams = toStreams(children);
|
|
283
|
-
|
|
284
|
-
handleChildEvents(streams, children, childrenInfo);
|
|
285
|
-
|
|
286
|
-
['SIGINT', 'SIGTERM'].forEach(function(signal) {
|
|
287
|
-
process.on(signal, function() {
|
|
288
|
-
children.forEach(function(child) {
|
|
289
|
-
treeKill(child.pid, signal);
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
process.stdin.on('data', (chunk) => {
|
|
295
|
-
let line = chunk.toString();
|
|
296
|
-
|
|
297
|
-
let targetId = config.defaultInputTarget;
|
|
298
|
-
if (line.indexOf(':') >= 0) {
|
|
299
|
-
const parts = line.split(':');
|
|
300
|
-
targetId = parts[0];
|
|
301
|
-
line = parts[1];
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const target = findChild(targetId, children, childrenInfo);
|
|
305
|
-
if (target) {
|
|
306
|
-
target.stdin.write(line);
|
|
307
|
-
} else {
|
|
308
|
-
logError('', chalk.gray.dim, `Unable to find command [${targetId}]\n`);
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function spawnChild(cmd, options) {
|
|
314
|
-
let child;
|
|
315
|
-
try {
|
|
316
|
-
child = spawn(cmd, options);
|
|
317
|
-
} catch (e) {
|
|
318
|
-
logError('', chalk.gray.dim, 'Error occured when executing command: ' + cmd);
|
|
319
|
-
logError('', chalk.gray.dim, e.stack);
|
|
320
|
-
process.exit(1);
|
|
321
|
-
}
|
|
322
|
-
return child;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function toStreams(children) {
|
|
326
|
-
// Transform all process events to rx streams
|
|
327
|
-
return _.map(children, function(child) {
|
|
328
|
-
const childStreams = {
|
|
329
|
-
error: Rx.Node.fromEvent(child, 'error'),
|
|
330
|
-
close: Rx.Node.fromEvent(child, 'close')
|
|
331
|
-
};
|
|
332
|
-
if (!config.raw) {
|
|
333
|
-
childStreams.stdout = Rx.Node.fromReadableStream(child.stdout);
|
|
334
|
-
childStreams.stderr = Rx.Node.fromReadableStream(child.stderr);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return _.reduce(childStreams, function(memo, stream, key) {
|
|
338
|
-
memo[key] = stream.map(function(data) {
|
|
339
|
-
return {child: child, data: data};
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
return memo;
|
|
343
|
-
}, {});
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function handleChildEvents(streams, children, childrenInfo) {
|
|
348
|
-
handleClose(streams, children, childrenInfo);
|
|
349
|
-
handleError(streams, childrenInfo);
|
|
350
|
-
if (!config.raw) {
|
|
351
|
-
handleOutput(streams, childrenInfo, 'stdout');
|
|
352
|
-
handleOutput(streams, childrenInfo, 'stderr');
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function handleOutput(streams, childrenInfo, source) {
|
|
357
|
-
const sourceStreams = _.map(streams, source);
|
|
358
|
-
const combinedSourceStream = Rx.Observable.merge.apply(this, sourceStreams);
|
|
359
|
-
|
|
360
|
-
combinedSourceStream.subscribe(function(event) {
|
|
361
|
-
const prefix = getPrefix(childrenInfo, event.child);
|
|
362
|
-
const prefixColor = childrenInfo[event.child.pid].prefixColor;
|
|
363
|
-
log(prefix, prefixColor, event.data.toString());
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
function handleClose(streams, children, childrenInfo) {
|
|
368
|
-
let aliveChildren = _.clone(children);
|
|
369
|
-
const exitCodes = [];
|
|
370
|
-
const closeStreams = _.map(streams, 'close');
|
|
371
|
-
const closeStream = Rx.Observable.merge.apply(this, closeStreams);
|
|
372
|
-
let othersKilled = false;
|
|
373
|
-
|
|
374
|
-
// TODO: Is it possible that amount of close events !== count of spawned?
|
|
375
|
-
closeStream.subscribe(function(event) {
|
|
376
|
-
const exitCode = event.data;
|
|
377
|
-
const nonSuccess = exitCode !== 0;
|
|
378
|
-
exitCodes.push(exitCode);
|
|
379
|
-
|
|
380
|
-
const prefix = getPrefix(childrenInfo, event.child);
|
|
381
|
-
const childInfo = childrenInfo[event.child.pid];
|
|
382
|
-
const prefixColor = childInfo.prefixColor;
|
|
383
|
-
const command = childInfo.command;
|
|
384
|
-
logEvent(prefix, prefixColor, command + ' exited with code ' + exitCode);
|
|
385
|
-
|
|
386
|
-
aliveChildren = _.filter(aliveChildren, function(child) {
|
|
387
|
-
return child.pid !== event.child.pid;
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
if (nonSuccess && config.allowRestart && childInfo.restartTries--) {
|
|
391
|
-
respawnChild(event, childrenInfo);
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (aliveChildren.length === 0) {
|
|
396
|
-
exit(exitCodes);
|
|
397
|
-
}
|
|
398
|
-
if (!othersKilled) {
|
|
399
|
-
if (config.killOthers) {
|
|
400
|
-
killOtherProcesses(aliveChildren);
|
|
401
|
-
othersKilled = true;
|
|
402
|
-
} else if (config.killOthersOnFail && nonSuccess) {
|
|
403
|
-
killOtherProcesses(aliveChildren);
|
|
404
|
-
othersKilled = true;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
function respawnChild(event, childrenInfo) {
|
|
411
|
-
setTimeout(function() {
|
|
412
|
-
const childInfo = childrenInfo[event.child.pid];
|
|
413
|
-
const prefix = getPrefix(childrenInfo, event.child);
|
|
414
|
-
const prefixColor = childInfo.prefixColor;
|
|
415
|
-
logEvent(prefix, prefixColor, childInfo.command + ' restarted');
|
|
416
|
-
const newChild = spawnChild(childInfo.command, childInfo.options);
|
|
417
|
-
|
|
418
|
-
childrenInfo[newChild.pid] = childrenInfo[event.child.pid];
|
|
419
|
-
delete childrenInfo[event.child.pid];
|
|
420
|
-
|
|
421
|
-
const children = [newChild];
|
|
422
|
-
const streams = toStreams(children);
|
|
423
|
-
handleChildEvents(streams, children, childrenInfo);
|
|
424
|
-
}, config.restartAfter);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function killOtherProcesses(processes) {
|
|
428
|
-
logEvent('--> ', chalk.gray.dim, 'Sending SIGTERM to other processes..');
|
|
429
|
-
|
|
430
|
-
// Send SIGTERM to alive children
|
|
431
|
-
_.each(processes, function(child) {
|
|
432
|
-
treeKill(child.pid, 'SIGTERM');
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function exit(childExitCodes) {
|
|
437
|
-
let success;
|
|
438
|
-
switch (config.success) {
|
|
439
|
-
case 'first':
|
|
440
|
-
success = _.first(childExitCodes) === 0;
|
|
441
|
-
break;
|
|
442
|
-
case 'last':
|
|
443
|
-
success = _.last(childExitCodes) === 0;
|
|
444
|
-
break;
|
|
445
|
-
default:
|
|
446
|
-
success = _.every(childExitCodes, function(code) {
|
|
447
|
-
return code === 0;
|
|
448
|
-
});
|
|
449
|
-
}
|
|
450
|
-
process.exit(success ? 0 : 1);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
function handleError(streams, childrenInfo) {
|
|
454
|
-
// Output emitted errors from child process
|
|
455
|
-
const errorStreams = _.map(streams, 'error');
|
|
456
|
-
const processErrorStream = Rx.Observable.merge.apply(this, errorStreams);
|
|
457
|
-
|
|
458
|
-
processErrorStream.subscribe(function(event) {
|
|
459
|
-
const command = childrenInfo[event.child.pid].command;
|
|
460
|
-
logError('', chalk.gray.dim, 'Error occured when executing command: ' + command);
|
|
461
|
-
logError('', chalk.gray.dim, event.data.stack);
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function colorText(text, color) {
|
|
466
|
-
if (!config.color) {
|
|
467
|
-
return text;
|
|
468
|
-
} else {
|
|
469
|
-
return color(text);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function getPrefix(childrenInfo, child) {
|
|
474
|
-
const prefixes = getPrefixes(childrenInfo, child);
|
|
475
|
-
const prefixType = config.prefix || prefixes.name ? 'name' : 'index';
|
|
476
|
-
if (_.includes(_.keys(prefixes), prefixType)) {
|
|
477
|
-
return '[' + prefixes[prefixType] + '] ';
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
return _.reduce(prefixes, function(memo, val, key) {
|
|
481
|
-
const re = new RegExp('{' + key + '}', 'g');
|
|
482
|
-
return memo.replace(re, val);
|
|
483
|
-
}, config.prefix) + ' ';
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function getPrefixes(childrenInfo, child) {
|
|
487
|
-
const prefixes = {};
|
|
488
|
-
|
|
489
|
-
prefixes.none = '';
|
|
490
|
-
prefixes.pid = child.pid;
|
|
491
|
-
prefixes.index = childrenInfo[child.pid].index;
|
|
492
|
-
prefixes.name = childrenInfo[child.pid].name;
|
|
493
|
-
prefixes.time = formatDate(Date.now(), config.timestampFormat);
|
|
494
|
-
|
|
495
|
-
const command = childrenInfo[child.pid].command;
|
|
496
|
-
prefixes.command = shortenText(command, config.prefixLength);
|
|
497
|
-
return prefixes;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function shortenText(text, length, cut) {
|
|
501
|
-
if (text.length <= length) {
|
|
502
|
-
return text;
|
|
503
|
-
}
|
|
504
|
-
cut = _.isString(cut) ? cut : '..';
|
|
505
|
-
|
|
506
|
-
const endLength = Math.floor(length / 2);
|
|
507
|
-
const startLength = length - endLength;
|
|
508
|
-
|
|
509
|
-
const first = text.substring(0, startLength);
|
|
510
|
-
const last = text.substring(text.length - endLength, text.length);
|
|
511
|
-
return first + cut + last;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
function log(prefix, prefixColor, text) {
|
|
515
|
-
logWithPrefix(prefix, prefixColor, text);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function logEvent(prefix, prefixColor, text) {
|
|
519
|
-
if (config.raw) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
logWithPrefix(prefix, prefixColor, text + '\n', chalk.gray.dim);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function logError(prefix, prefixColor, text) {
|
|
527
|
-
// This is for now same as log, there might be separate colors for stderr
|
|
528
|
-
// and stdout
|
|
529
|
-
logWithPrefix(prefix, prefixColor, text, chalk.red.bold);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
let lastChar;
|
|
533
|
-
|
|
534
|
-
function logWithPrefix(prefix, prefixColor, text, color) {
|
|
535
|
-
|
|
536
|
-
if (config.raw) {
|
|
537
|
-
process.stdout.write(text);
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
text = text.replace(/\u2026/g,'...'); // Ellipsis
|
|
542
|
-
|
|
543
|
-
const lines = text.split('\n');
|
|
544
|
-
// Do not bgColor trailing space
|
|
545
|
-
const coloredPrefix = colorText(prefix.replace(/ $/, ''), prefixColor) + ' ';
|
|
546
|
-
const paddedLines = _.map(lines, function(line, index) {
|
|
547
|
-
let coloredLine = color ? colorText(line, color) : line;
|
|
548
|
-
if (index !== 0 && index !== (lines.length - 1)) {
|
|
549
|
-
coloredLine = coloredPrefix + coloredLine;
|
|
550
|
-
}
|
|
551
|
-
return coloredLine;
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
if (!lastChar || lastChar === '\n' ) {
|
|
555
|
-
process.stdout.write(coloredPrefix);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
lastChar = text[text.length - 1];
|
|
559
|
-
|
|
560
|
-
process.stdout.write(paddedLines.join('\n'));
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
main();
|
package/src/parseCmds.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
const _ = require('lodash');
|
|
3
|
-
|
|
4
|
-
const pkgInfo = require('./pkgInfo');
|
|
5
|
-
|
|
6
|
-
module.exports = function(cmds, config) {
|
|
7
|
-
config = config || {};
|
|
8
|
-
|
|
9
|
-
const names = (config.names || '').split(config.nameSeparator || ',');
|
|
10
|
-
const prefixColors = config.prefixColors ? config.prefixColors.split(',') : [];
|
|
11
|
-
|
|
12
|
-
cmds = cmds.map(stripCmdQuotes);
|
|
13
|
-
|
|
14
|
-
cmds = cmds.map((cmd, idx) => ({
|
|
15
|
-
cmd: cmd,
|
|
16
|
-
name: names[idx] || ''
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
cmds = _.flatMap(cmds, expandCmdShortcuts);
|
|
20
|
-
|
|
21
|
-
return cmds.map((cmd, idx) => Object.assign(cmd, {
|
|
22
|
-
color: prefixColors[idx]
|
|
23
|
-
}));
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function stripCmdQuotes(cmd) {
|
|
27
|
-
// Removes the quotes surrounding a command.
|
|
28
|
-
if (cmd[0] === '"' || cmd[0] === '\'') {
|
|
29
|
-
return cmd.substr(1, cmd.length - 2);
|
|
30
|
-
} else {
|
|
31
|
-
return cmd;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function expandCmdShortcuts(cmd) {
|
|
36
|
-
const shortcut = cmd.cmd.match(/^npm:(\S+)(.*)/);
|
|
37
|
-
if (shortcut) {
|
|
38
|
-
const cmdName = shortcut[1];
|
|
39
|
-
const args = shortcut[2];
|
|
40
|
-
|
|
41
|
-
const wildcard = cmdName.indexOf('*');
|
|
42
|
-
if (wildcard >= 0) {
|
|
43
|
-
return expandNpmWildcard(cmd, cmdName, wildcard, args);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!cmd.name) {
|
|
47
|
-
cmd.name = cmdName;
|
|
48
|
-
}
|
|
49
|
-
cmd.cmd = `npm run ${cmdName}${args}`;
|
|
50
|
-
}
|
|
51
|
-
return [ cmd ];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function expandNpmWildcard(cmd, cmdName, wildcardPos, args) {
|
|
55
|
-
const rePre = _.escapeRegExp(cmdName.substr(0, wildcardPos));
|
|
56
|
-
const reSuf = _.escapeRegExp(cmdName.substr(wildcardPos + 1));
|
|
57
|
-
const wildcardRe = new RegExp(`^${rePre}(.*?)${reSuf}$`);
|
|
58
|
-
|
|
59
|
-
return pkgInfo.getScripts()
|
|
60
|
-
.filter(script => script.match(wildcardRe))
|
|
61
|
-
.map(script => Object.assign({}, cmd, {
|
|
62
|
-
cmd: `npm run ${script}${args}`,
|
|
63
|
-
name: cmd.name + script.match(wildcardRe)[1]
|
|
64
|
-
}));
|
|
65
|
-
}
|
package/src/pkgInfo.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const readPkg = require('read-pkg');
|
|
4
|
-
|
|
5
|
-
let pkg;
|
|
6
|
-
|
|
7
|
-
function getPkg() {
|
|
8
|
-
if (!pkg) {
|
|
9
|
-
pkg = readPkg.sync();
|
|
10
|
-
}
|
|
11
|
-
return pkg;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
exports.getScripts = function() {
|
|
15
|
-
const pkg = getPkg();
|
|
16
|
-
return Object.keys(pkg.scripts || {});
|
|
17
|
-
};
|
package/test/support/signal.js
DELETED
package/test/test-findChild.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const assert = require('assert');
|
|
4
|
-
|
|
5
|
-
const findChild = require('../src/findChild');
|
|
6
|
-
|
|
7
|
-
describe('findChild', () => {
|
|
8
|
-
|
|
9
|
-
const aChild = { pid: '111' };
|
|
10
|
-
const bChild = { pid: '222' };
|
|
11
|
-
|
|
12
|
-
const children = [ aChild, bChild ];
|
|
13
|
-
|
|
14
|
-
const childrenInfo = {
|
|
15
|
-
111: {
|
|
16
|
-
index: 0,
|
|
17
|
-
name: 'a child'
|
|
18
|
-
},
|
|
19
|
-
222: {
|
|
20
|
-
index: 1,
|
|
21
|
-
name: 'b child'
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
it('finds child by index', () => {
|
|
26
|
-
const child = findChild('1', children, childrenInfo);
|
|
27
|
-
assert.strictEqual(child, bChild);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('finds child by name', () => {
|
|
31
|
-
const child = findChild('a child', children, childrenInfo);
|
|
32
|
-
assert.strictEqual(child, aChild);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('returns undefined when no matching child found', () => {
|
|
36
|
-
const child = findChild('no child', children, childrenInfo);
|
|
37
|
-
assert.strictEqual(child, undefined);
|
|
38
|
-
});
|
|
39
|
-
});
|