concurrently 3.4.0 → 3.6.1

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/src/main.js CHANGED
@@ -1,17 +1,21 @@
1
1
  #!/usr/bin/env node
2
-
3
- var Rx = require('rx');
4
- var path = require('path');
5
- var formatDate = require('date-fns/format');
6
- var program = require('commander');
7
- var _ = require('lodash');
8
- var treeKill = require('tree-kill');
9
- var chalk = require('chalk');
10
- var spawn = require('spawn-command');
11
- var supportsColor = require('supports-color');
12
- var IS_WINDOWS = /^win/.test(process.platform);
13
-
14
- var config = {
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 = {
15
19
  // Kill other processes if one dies
16
20
  killOthers: false,
17
21
 
@@ -24,7 +28,7 @@ var config = {
24
28
 
25
29
  // Prefix logging with pid
26
30
  // Possible values: 'pid', 'none', 'time', 'command', 'index', 'name'
27
- prefix: 'index',
31
+ prefix: '',
28
32
 
29
33
  // List of custom names to be used in prefix template
30
34
  names: '',
@@ -46,19 +50,33 @@ var config = {
46
50
  color: true,
47
51
 
48
52
  // If true, the output will only be raw output of processes, nothing more
49
- raw: false
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'
50
66
  };
51
67
 
52
68
  function main() {
53
- var firstBase = path.basename(process.argv[0]);
54
- var secondBase = path.basename(process.argv[1]);
69
+ const firstBase = path.basename(process.argv[0]);
70
+ const secondBase = path.basename(process.argv[1]);
55
71
  if (firstBase === 'concurrent' || secondBase === 'concurrent') {
56
72
  console.error('Warning: "concurrent" command is deprecated, use "concurrently" instead.\n');
57
73
  }
58
74
 
59
75
  parseArgs();
60
76
  config = mergeDefaultsWithArgs(config);
61
- run(program.args);
77
+
78
+ const cmds = parseCmds(program.args, config);
79
+ run(cmds);
62
80
  }
63
81
 
64
82
  function parseArgs() {
@@ -81,7 +99,7 @@ function parseArgs() {
81
99
  '-p, --prefix <prefix>',
82
100
  'prefix used in logging for each process.\n' +
83
101
  'Possible values: index, pid, time, command, name, none, or a template. Default: ' +
84
- config.prefix + '. Example template: "{time}-{pid}"\n'
102
+ 'index or name (when --names is set). Example template: "{time}-{pid}"\n'
85
103
  )
86
104
  .option(
87
105
  '-n, --names <names>',
@@ -126,10 +144,40 @@ function parseArgs() {
126
144
  'The option can be used to shorten long commands.\n' +
127
145
  'Works only if prefix is set to "command". Default: ' +
128
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'
129
170
  );
130
171
 
131
172
  program.on('--help', function() {
132
- var help = [
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
+ '',
133
181
  ' Examples:',
134
182
  '',
135
183
  ' - Kill other processes if one exits or dies',
@@ -154,12 +202,40 @@ function parseArgs() {
154
202
  '',
155
203
  ' - Custom names and colored prefixes',
156
204
  '',
157
- ' $ concurrently --prefix "[{name}]" --names "HTTP,WATCH" -c "bgBlue.bold,bgMagenta.bold" "npm run watch" "http-server"',
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-*',
158
234
  ''
159
235
  ];
160
236
  console.log(help.join('\n'));
161
237
 
162
- var url = 'https://github.com/kimmobrunfeldt/concurrently';
238
+ const url = 'https://github.com/kimmobrunfeldt/concurrently';
163
239
  console.log(' For more details, visit ' + url);
164
240
  console.log('');
165
241
  });
@@ -172,59 +248,84 @@ function mergeDefaultsWithArgs(config) {
172
248
  return _.merge(config, program);
173
249
  }
174
250
 
175
- function stripCmdQuotes(cmd) {
176
- // Removes the quotes surrounding a command.
177
- if (cmd[0] === '"' || cmd[0] === '\'') {
178
- return cmd.substr(1, cmd.length - 2);
179
- } else {
180
- return cmd;
181
- }
182
- }
183
-
184
251
  function run(commands) {
185
- var childrenInfo = {};
186
- var lastPrefixColor = _.get(chalk, chalk.gray.dim);
187
- var prefixColors = config.prefixColors.split(',');
188
- var names = config.names.split(config.nameSeparator);
189
- var children = _.map(commands, function(cmd, index) {
190
- // Remove quotes.
191
- cmd = stripCmdQuotes(cmd);
192
-
193
- var spawnOpts = config.raw ? {stdio: 'inherit'} : {};
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'} : {};
194
257
  if (IS_WINDOWS) {
195
258
  spawnOpts.detached = false;
196
259
  }
197
260
  if (supportsColor) {
198
- spawnOpts.env = Object.assign({FORCE_COLOR: supportsColor.level}, process.env)
261
+ spawnOpts.env = Object.assign({FORCE_COLOR: supportsColor.level}, process.env);
199
262
  }
200
263
 
201
- var child;
202
- try {
203
- child = spawn(cmd, spawnOpts);
204
- } catch (e) {
205
- logError('', chalk.gray.dim, 'Error occured when executing command: ' + cmd);
206
- logError('', chalk.gray.dim, e.stack);
207
- process.exit(1);
208
- }
264
+ const child = spawnChild(cmdInfo.cmd, spawnOpts);
209
265
 
210
- if (index < prefixColors.length) {
211
- var prefixColorPath = prefixColors[index];
266
+ if (cmdInfo.color) {
267
+ const prefixColorPath = cmdInfo.color;
212
268
  lastPrefixColor = _.get(chalk, prefixColorPath, chalk.gray.dim);
213
269
  }
214
270
 
215
- var name = index < names.length ? names[index] : '';
216
271
  childrenInfo[child.pid] = {
217
- command: cmd,
272
+ command: cmdInfo.cmd,
218
273
  index: index,
219
- name: name,
274
+ name: cmdInfo.name,
275
+ options: spawnOpts,
276
+ restartTries: config.restartTries,
220
277
  prefixColor: lastPrefixColor
221
278
  };
222
279
  return child;
223
280
  });
224
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) {
225
326
  // Transform all process events to rx streams
226
- var streams = _.map(children, function(child) {
227
- var childStreams = {
327
+ return _.map(children, function(child) {
328
+ const childStreams = {
228
329
  error: Rx.Node.fromEvent(child, 'error'),
229
330
  close: Rx.Node.fromEvent(child, 'close')
230
331
  };
@@ -241,71 +342,88 @@ function run(commands) {
241
342
  return memo;
242
343
  }, {});
243
344
  });
345
+ }
244
346
 
347
+ function handleChildEvents(streams, children, childrenInfo) {
245
348
  handleClose(streams, children, childrenInfo);
246
349
  handleError(streams, childrenInfo);
247
350
  if (!config.raw) {
248
351
  handleOutput(streams, childrenInfo, 'stdout');
249
352
  handleOutput(streams, childrenInfo, 'stderr');
250
353
  }
251
-
252
- ['SIGINT', 'SIGTERM'].forEach(function(signal) {
253
- process.on(signal, function() {
254
- children.forEach(function(child) {
255
- treeKill(child.pid, signal);
256
- });
257
- });
258
- });
259
354
  }
260
355
 
261
356
  function handleOutput(streams, childrenInfo, source) {
262
- var sourceStreams = _.map(streams, source);
263
- var combinedSourceStream = Rx.Observable.merge.apply(this, sourceStreams);
357
+ const sourceStreams = _.map(streams, source);
358
+ const combinedSourceStream = Rx.Observable.merge.apply(this, sourceStreams);
264
359
 
265
360
  combinedSourceStream.subscribe(function(event) {
266
- var prefix = getPrefix(childrenInfo, event.child);
267
- var prefixColor = childrenInfo[event.child.pid].prefixColor;
361
+ const prefix = getPrefix(childrenInfo, event.child);
362
+ const prefixColor = childrenInfo[event.child.pid].prefixColor;
268
363
  log(prefix, prefixColor, event.data.toString());
269
364
  });
270
365
  }
271
366
 
272
367
  function handleClose(streams, children, childrenInfo) {
273
- var aliveChildren = _.clone(children);
274
- var exitCodes = [];
275
- var closeStreams = _.map(streams, 'close');
276
- var closeStream = Rx.Observable.merge.apply(this, closeStreams);
277
- var othersKilled = false
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;
278
373
 
279
374
  // TODO: Is it possible that amount of close events !== count of spawned?
280
375
  closeStream.subscribe(function(event) {
281
- var exitCode = event.data;
282
- var nonSuccess = exitCode !== 0;
376
+ const exitCode = event.data;
377
+ const nonSuccess = exitCode !== 0;
283
378
  exitCodes.push(exitCode);
284
379
 
285
- var prefix = getPrefix(childrenInfo, event.child);
286
- var prefixColor = childrenInfo[event.child.pid].prefixColor;
287
- var command = childrenInfo[event.child.pid].command;
380
+ const prefix = getPrefix(childrenInfo, event.child);
381
+ const childInfo = childrenInfo[event.child.pid];
382
+ const prefixColor = childInfo.prefixColor;
383
+ const command = childInfo.command;
288
384
  logEvent(prefix, prefixColor, command + ' exited with code ' + exitCode);
289
385
 
290
386
  aliveChildren = _.filter(aliveChildren, function(child) {
291
387
  return child.pid !== event.child.pid;
292
388
  });
293
389
 
390
+ if (nonSuccess && config.allowRestart && childInfo.restartTries--) {
391
+ respawnChild(event, childrenInfo);
392
+ return;
393
+ }
394
+
294
395
  if (aliveChildren.length === 0) {
295
396
  exit(exitCodes);
296
397
  }
297
398
  if (!othersKilled) {
298
- if (config.killOthers) {
299
- killOtherProcesses(aliveChildren);
300
- othersKilled = true;
301
- } else if (config.killOthersOnFail && nonSuccess) {
302
- killOtherProcesses(aliveChildren);
303
- othersKilled = true;
304
- }
399
+ if (config.killOthers) {
400
+ killOtherProcesses(aliveChildren);
401
+ othersKilled = true;
402
+ } else if (config.killOthersOnFail && nonSuccess) {
403
+ killOtherProcesses(aliveChildren);
404
+ othersKilled = true;
405
+ }
305
406
  }
306
407
  });
307
408
  }
308
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
+
309
427
  function killOtherProcesses(processes) {
310
428
  logEvent('--> ', chalk.gray.dim, 'Sending SIGTERM to other processes..');
311
429
 
@@ -316,29 +434,29 @@ function killOtherProcesses(processes) {
316
434
  }
317
435
 
318
436
  function exit(childExitCodes) {
319
- var success;
437
+ let success;
320
438
  switch (config.success) {
321
- case 'first':
322
- success = _.first(childExitCodes) === 0;
323
- break;
324
- case 'last':
325
- success = _.last(childExitCodes) === 0;
326
- break;
327
- default:
328
- success = _.every(childExitCodes, function(code) {
329
- return code === 0;
330
- });
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
+ });
331
449
  }
332
450
  process.exit(success ? 0 : 1);
333
451
  }
334
452
 
335
453
  function handleError(streams, childrenInfo) {
336
454
  // Output emitted errors from child process
337
- var errorStreams = _.map(streams, 'error');
338
- var processErrorStream = Rx.Observable.merge.apply(this, errorStreams);
455
+ const errorStreams = _.map(streams, 'error');
456
+ const processErrorStream = Rx.Observable.merge.apply(this, errorStreams);
339
457
 
340
458
  processErrorStream.subscribe(function(event) {
341
- var command = childrenInfo[event.child.pid].command;
459
+ const command = childrenInfo[event.child.pid].command;
342
460
  logError('', chalk.gray.dim, 'Error occured when executing command: ' + command);
343
461
  logError('', chalk.gray.dim, event.data.stack);
344
462
  });
@@ -353,19 +471,20 @@ function colorText(text, color) {
353
471
  }
354
472
 
355
473
  function getPrefix(childrenInfo, child) {
356
- var prefixes = getPrefixes(childrenInfo, child);
357
- if (_.includes(_.keys(prefixes), config.prefix)) {
358
- return '[' + prefixes[config.prefix] + '] ';
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] + '] ';
359
478
  }
360
479
 
361
480
  return _.reduce(prefixes, function(memo, val, key) {
362
- var re = new RegExp('{' + key + '}', 'g');
481
+ const re = new RegExp('{' + key + '}', 'g');
363
482
  return memo.replace(re, val);
364
483
  }, config.prefix) + ' ';
365
484
  }
366
485
 
367
486
  function getPrefixes(childrenInfo, child) {
368
- var prefixes = {};
487
+ const prefixes = {};
369
488
 
370
489
  prefixes.none = '';
371
490
  prefixes.pid = child.pid;
@@ -373,7 +492,7 @@ function getPrefixes(childrenInfo, child) {
373
492
  prefixes.name = childrenInfo[child.pid].name;
374
493
  prefixes.time = formatDate(Date.now(), config.timestampFormat);
375
494
 
376
- var command = childrenInfo[child.pid].command;
495
+ const command = childrenInfo[child.pid].command;
377
496
  prefixes.command = shortenText(command, config.prefixLength);
378
497
  return prefixes;
379
498
  }
@@ -384,11 +503,11 @@ function shortenText(text, length, cut) {
384
503
  }
385
504
  cut = _.isString(cut) ? cut : '..';
386
505
 
387
- var endLength = Math.floor(length / 2);
388
- var startLength = length - endLength;
506
+ const endLength = Math.floor(length / 2);
507
+ const startLength = length - endLength;
389
508
 
390
- var first = text.substring(0, startLength);
391
- var last = text.substring(text.length - endLength, text.length);
509
+ const first = text.substring(0, startLength);
510
+ const last = text.substring(text.length - endLength, text.length);
392
511
  return first + cut + last;
393
512
  }
394
513
 
@@ -397,9 +516,11 @@ function log(prefix, prefixColor, text) {
397
516
  }
398
517
 
399
518
  function logEvent(prefix, prefixColor, text) {
400
- if (config.raw) return;
519
+ if (config.raw) {
520
+ return;
521
+ }
401
522
 
402
- logWithPrefix(prefix, prefixColor, text, chalk.gray.dim);
523
+ logWithPrefix(prefix, prefixColor, text + '\n', chalk.gray.dim);
403
524
  }
404
525
 
405
526
  function logError(prefix, prefixColor, text) {
@@ -408,31 +529,35 @@ function logError(prefix, prefixColor, text) {
408
529
  logWithPrefix(prefix, prefixColor, text, chalk.red.bold);
409
530
  }
410
531
 
532
+ let lastChar;
533
+
411
534
  function logWithPrefix(prefix, prefixColor, text, color) {
412
- var lastChar = text[text.length - 1];
413
- if (config.raw) {
414
- if (lastChar !== '\n') {
415
- text += '\n';
416
- }
417
535
 
536
+ if (config.raw) {
418
537
  process.stdout.write(text);
419
538
  return;
420
539
  }
421
540
 
422
- if (lastChar === '\n') {
423
- // Remove extra newline from the end to prevent extra newlines in input
424
- text = text.slice(0, text.length - 1);
425
- }
541
+ text = text.replace(/\u2026/g,'...'); // Ellipsis
426
542
 
427
- var lines = text.split('\n');
543
+ const lines = text.split('\n');
428
544
  // Do not bgColor trailing space
429
- var coloredPrefix = colorText(prefix.replace(/ $/, ''), prefixColor) + ' ';
430
- var paddedLines = _.map(lines, function(line, i) {
431
- var coloredLine = color ? colorText(line, color) : line;
432
- return coloredPrefix + coloredLine;
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;
433
552
  });
434
553
 
435
- console.log(paddedLines.join('\n'));
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'));
436
561
  }
437
562
 
438
563
  main();
@@ -0,0 +1,65 @@
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 ADDED
@@ -0,0 +1,17 @@
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
+ };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ process.stdin.on('data', (chunk) => {
4
+ const line = chunk.toString().trim();
5
+ console.log(line);
6
+
7
+ if (line === 'stop') {
8
+ process.exit(0);
9
+ }
10
+ });
11
+
12
+ console.log('READING');
@@ -1,12 +1,12 @@
1
1
  ['SIGINT', 'SIGTERM'].forEach(function(signal) {
2
- process.on(signal, function() {
3
- console.log(signal);
4
- process.exit(1);
5
- });
2
+ process.on(signal, function() {
3
+ console.log(signal);
4
+ process.exit(1);
5
+ });
6
6
  });
7
7
 
8
8
  function doNothing() {
9
- setTimeout(doNothing, 1000);
9
+ setTimeout(doNothing, 1000);
10
10
  }
11
11
 
12
12
  doNothing();