concurrently
Advanced tools
Comparing version 3.5.1 to 3.6.0
{ | ||
"name": "concurrently", | ||
"version": "3.5.1", | ||
"version": "3.6.0", | ||
"description": "Run commands concurrently", | ||
@@ -14,3 +14,8 @@ "main": "src/main.js", | ||
"scripts": { | ||
"test": "mocha" | ||
"lint": "eslint .", | ||
"test": "mocha", | ||
"echo": "echo", | ||
"echo-test": "echo test", | ||
"echo-sound-beep": "echo beep", | ||
"echo-sound-boop": "echo boop" | ||
}, | ||
@@ -36,6 +41,7 @@ "repository": { | ||
"dependencies": { | ||
"chalk": "0.5.1", | ||
"chalk": "^2.4.1", | ||
"commander": "2.6.0", | ||
"date-fns": "^1.23.0", | ||
"lodash": "^4.5.1", | ||
"read-pkg": "^3.0.0", | ||
"rx": "2.3.24", | ||
@@ -48,2 +54,3 @@ "spawn-command": "^0.0.2-1", | ||
"chai": "^1.10.0", | ||
"eslint": "^4.19.1", | ||
"mocha": "^2.1.0", | ||
@@ -55,4 +62,5 @@ "mustache": "^1.0.0", | ||
"shelljs": "^0.3.0", | ||
"sinon": "^4.1.3", | ||
"string": "^3.0.0" | ||
} | ||
} |
101
README.md
@@ -49,2 +49,42 @@ # Concurrently | ||
NPM run commands can be shortened: | ||
```bash | ||
concurrently "npm:watch-js" "npm:watch-css" "npm:watch-node" | ||
# Equivalent to: | ||
concurrently -n watch-js,watch-css,watch-node "npm run watch-js" "npm run watch-css" "npm run watch-node" | ||
``` | ||
NPM shortened commands also support wildcards. Given the following scripts in | ||
package.json: | ||
```javascript | ||
{ | ||
//... | ||
"scripts": { | ||
// ... | ||
"watch-js": "...", | ||
"watch-css": "...", | ||
"watch-node": "...", | ||
// ... | ||
}, | ||
// ... | ||
} | ||
``` | ||
```bash | ||
concurrently "npm:watch-*" | ||
# Equivalent to: | ||
concurrently -n js,css,node "npm run watch-js" "npm run watch-css" "npm run watch-node" | ||
# Any name provided for the wildcard command will be used as a prefix to the wildcard | ||
# part of the script name: | ||
concurrently -n w: npm:watch-* | ||
# Equivalent to: | ||
concurrently -n w:js,w:css,w:node "npm run watch-js" "npm run watch-css" "npm run watch-node" | ||
``` | ||
Good frontend one-liner example [here](https://github.com/kimmobrunfeldt/dont-copy-paste-this-frontend-template/blob/5cd2bde719654941bdfc0a42c6f1b8e69ae79980/package.json#L9). | ||
@@ -96,2 +136,11 @@ | ||
--default-input-target <identifier> identifier for child process to which input on stdin should be sent if not specified at start of input. Can be either the index or the name of the process. Default: 0 | ||
Input: | ||
Input can be sent to any of the child processes using either the name or index | ||
of the command followed by a colon. If no child identifier is specified then the | ||
input will be sent to the child specified by the `--default-input-target` | ||
option, which defaults to index 0. | ||
Examples: | ||
@@ -122,3 +171,55 @@ | ||
$ concurrently --names "HTTP,WATCH" -c "bgBlue.bold,bgMagenta.bold" "http-server" "npm run watch" | ||
- Shortened NPM run commands | ||
$ concurrently npm:watch-node npm:watch-js npm:watch-css | ||
- Send input to default | ||
$ concurrently "nodemon" "npm run watch-js" | ||
rs # Sends rs command to nodemon process | ||
- Specify a default-input-target | ||
$ concurrently --default-input-target 1 "npm run watch-js" nodemon | ||
rs | ||
- Send input to specific child identified by index | ||
$ concurrently "npm run watch-js" nodemon | ||
1:rs | ||
- Send input to specific child identified by name | ||
$ concurrently -n js,srv "npm run watch-js" nodemon | ||
srv:rs | ||
- Send input to default | ||
$ concurrently "nodemon" "npm run watch-js" | ||
rs # Sends rs command to nodemon process | ||
- Specify a default-input-target | ||
$ concurrently --default-input-target 1 "npm run watch-js" nodemon | ||
rs | ||
- Send input to specific child identified by index | ||
$ concurrently "npm run watch-js" nodemon | ||
1:rs | ||
- Send input to specific child identified by name | ||
$ concurrently -n js,srv "npm run watch-js" nodemon | ||
srv:rs | ||
- Shortened NPM run commands | ||
$ concurrently npm:watch-node npm:watch-js npm:watch-css | ||
- Shortened NPM run command with wildcard | ||
$ concurrently npm:watch-* | ||
For more details, visit https://github.com/kimmobrunfeldt/concurrently | ||
@@ -125,0 +226,0 @@ ``` |
281
src/main.js
#!/usr/bin/env node | ||
'use strict'; | ||
var Rx = require('rx'); | ||
var path = require('path'); | ||
var formatDate = require('date-fns/format'); | ||
var program = require('commander'); | ||
var _ = require('lodash'); | ||
var treeKill = require('tree-kill'); | ||
var chalk = require('chalk'); | ||
var spawn = require('spawn-command'); | ||
var supportsColor = require('supports-color'); | ||
var IS_WINDOWS = /^win/.test(process.platform); | ||
const Rx = require('rx'); | ||
const path = require('path'); | ||
const formatDate = require('date-fns/format'); | ||
const program = require('commander'); | ||
const _ = require('lodash'); | ||
const treeKill = require('tree-kill'); | ||
const chalk = require('chalk'); | ||
const spawn = require('spawn-command'); | ||
const supportsColor = require('supports-color'); | ||
const IS_WINDOWS = /^win/.test(process.platform); | ||
var config = { | ||
const findChild = require('./findChild.js'); | ||
const parseCmds = require('./parseCmds'); | ||
let config = { | ||
// Kill other processes if one dies | ||
@@ -58,8 +62,11 @@ killOthers: false, | ||
// By default, restart once | ||
restartTries: 1 | ||
restartTries: 1, | ||
// Default identifier for child to which input on stdin should be sent. | ||
defaultInputTarget: '0' | ||
}; | ||
function main() { | ||
var firstBase = path.basename(process.argv[0]); | ||
var secondBase = path.basename(process.argv[1]); | ||
const firstBase = path.basename(process.argv[0]); | ||
const secondBase = path.basename(process.argv[1]); | ||
if (firstBase === 'concurrent' || secondBase === 'concurrent') { | ||
@@ -71,5 +78,5 @@ console.error('Warning: "concurrent" command is deprecated, use "concurrently" instead.\n'); | ||
config = mergeDefaultsWithArgs(config); | ||
applyDynamicDefaults(config) | ||
run(program.args); | ||
const cmds = parseCmds(program.args, config); | ||
run(cmds); | ||
} | ||
@@ -157,6 +164,21 @@ | ||
config.restartTries + '\n' | ||
) | ||
.option( | ||
'--default-input-target <identifier>', | ||
'identifier for child process to which input on ' + | ||
'stdin should be sent if not specified at start ' + | ||
'of input. Can be either the index or the name ' + | ||
'of the process. Default: ' + config.defaultInputTarget + '\n' | ||
); | ||
program.on('--help', function() { | ||
var help = [ | ||
const help = [ | ||
' Input:', | ||
'', | ||
' Input can be sent to any of the child processes using either the name or', | ||
' index of the command followed by a colon. If no child identifier is', | ||
' specified then the input will be sent to the child specified by the', | ||
' `--default-input-target` option, which defaults to index 0.', | ||
'', | ||
' Examples:', | ||
@@ -187,2 +209,30 @@ '', | ||
' $ concurrently --names "HTTP,WATCH" -c "bgBlue.bold,bgMagenta.bold" "http-server" "npm run watch"', | ||
'', | ||
' - Send input to default', | ||
'', | ||
' $ concurrently "nodemon" "npm run watch-js"', | ||
' rs # Sends rs command to nodemon process', | ||
'', | ||
' - Specify a default-input-target', | ||
'', | ||
' $ concurrently --default-input-target 1 "npm run watch-js" nodemon', | ||
' rs', | ||
'', | ||
' - Send input to specific child identified by index', | ||
'', | ||
' $ concurrently "npm run watch-js" nodemon', | ||
' 1:rs', | ||
'', | ||
' - Send input to specific child identified by name', | ||
'', | ||
' $ concurrently -n js,srv "npm run watch-js" nodemon', | ||
' srv:rs', | ||
'', | ||
' - Shortened NPM run commands', | ||
'', | ||
' $ concurrently npm:watch-node npm:watch-js npm:watch-css', | ||
'', | ||
' - Shortened NPM run command with wildcard', | ||
'', | ||
' $ concurrently npm:watch-*', | ||
'' | ||
@@ -192,3 +242,3 @@ ]; | ||
var url = 'https://github.com/kimmobrunfeldt/concurrently'; | ||
const url = 'https://github.com/kimmobrunfeldt/concurrently'; | ||
console.log(' For more details, visit ' + url); | ||
@@ -206,27 +256,8 @@ console.log(''); | ||
function applyDynamicDefaults(config) { | ||
if (!config.prefix) { | ||
config.prefix = config.names ? 'name' : 'index'; | ||
} | ||
} | ||
function stripCmdQuotes(cmd) { | ||
// Removes the quotes surrounding a command. | ||
if (cmd[0] === '"' || cmd[0] === '\'') { | ||
return cmd.substr(1, cmd.length - 2); | ||
} else { | ||
return cmd; | ||
} | ||
} | ||
function run(commands) { | ||
var childrenInfo = {}; | ||
var lastPrefixColor = _.get(chalk, chalk.gray.dim); | ||
var prefixColors = config.prefixColors.split(','); | ||
var names = config.names.split(config.nameSeparator); | ||
var children = _.map(commands, function(cmd, index) { | ||
// Remove quotes. | ||
cmd = stripCmdQuotes(cmd); | ||
const childrenInfo = {}; | ||
let lastPrefixColor = _.get(chalk, chalk.gray.dim); | ||
const children = _.map(commands, function(cmdInfo, index) { | ||
var spawnOpts = config.raw ? {stdio: 'inherit'} : {}; | ||
const spawnOpts = config.raw ? {stdio: 'inherit'} : {}; | ||
if (IS_WINDOWS) { | ||
@@ -236,17 +267,16 @@ spawnOpts.detached = false; | ||
if (supportsColor) { | ||
spawnOpts.env = Object.assign({FORCE_COLOR: supportsColor.level}, process.env) | ||
spawnOpts.env = Object.assign({FORCE_COLOR: supportsColor.level}, process.env); | ||
} | ||
var child = spawnChild(cmd, spawnOpts); | ||
const child = spawnChild(cmdInfo.cmd, spawnOpts); | ||
if (index < prefixColors.length) { | ||
var prefixColorPath = prefixColors[index]; | ||
if (cmdInfo.color) { | ||
const prefixColorPath = cmdInfo.color; | ||
lastPrefixColor = _.get(chalk, prefixColorPath, chalk.gray.dim); | ||
} | ||
var name = index < names.length ? names[index] : ''; | ||
childrenInfo[child.pid] = { | ||
command: cmd, | ||
command: cmdInfo.cmd, | ||
index: index, | ||
name: name, | ||
name: cmdInfo.name, | ||
options: spawnOpts, | ||
@@ -259,3 +289,3 @@ restartTries: config.restartTries, | ||
var streams = toStreams(children); | ||
const streams = toStreams(children); | ||
@@ -265,12 +295,30 @@ handleChildEvents(streams, children, childrenInfo); | ||
['SIGINT', 'SIGTERM'].forEach(function(signal) { | ||
process.on(signal, function() { | ||
children.forEach(function(child) { | ||
treeKill(child.pid, signal); | ||
process.on(signal, function() { | ||
children.forEach(function(child) { | ||
treeKill(child.pid, signal); | ||
}); | ||
}); | ||
}); | ||
}); | ||
process.stdin.on('data', (chunk) => { | ||
let line = chunk.toString(); | ||
let targetId = config.defaultInputTarget; | ||
if (line.indexOf(':') >= 0) { | ||
const parts = line.split(':'); | ||
targetId = parts[0]; | ||
line = parts[1]; | ||
} | ||
const target = findChild(targetId, children, childrenInfo); | ||
if (target) { | ||
target.stdin.write(line); | ||
} else { | ||
logError('', chalk.gray.dim, `Unable to find command [${targetId}]\n`); | ||
} | ||
}); | ||
} | ||
function spawnChild(cmd, options) { | ||
var child; | ||
let child; | ||
try { | ||
@@ -289,3 +337,3 @@ child = spawn(cmd, options); | ||
return _.map(children, function(child) { | ||
var childStreams = { | ||
const childStreams = { | ||
error: Rx.Node.fromEvent(child, 'error'), | ||
@@ -319,8 +367,8 @@ close: Rx.Node.fromEvent(child, 'close') | ||
function handleOutput(streams, childrenInfo, source) { | ||
var sourceStreams = _.map(streams, source); | ||
var combinedSourceStream = Rx.Observable.merge.apply(this, sourceStreams); | ||
const sourceStreams = _.map(streams, source); | ||
const combinedSourceStream = Rx.Observable.merge.apply(this, sourceStreams); | ||
combinedSourceStream.subscribe(function(event) { | ||
var prefix = getPrefix(childrenInfo, event.child); | ||
var prefixColor = childrenInfo[event.child.pid].prefixColor; | ||
const prefix = getPrefix(childrenInfo, event.child); | ||
const prefixColor = childrenInfo[event.child.pid].prefixColor; | ||
log(prefix, prefixColor, event.data.toString()); | ||
@@ -331,18 +379,18 @@ }); | ||
function handleClose(streams, children, childrenInfo) { | ||
var aliveChildren = _.clone(children); | ||
var exitCodes = []; | ||
var closeStreams = _.map(streams, 'close'); | ||
var closeStream = Rx.Observable.merge.apply(this, closeStreams); | ||
var othersKilled = false | ||
let aliveChildren = _.clone(children); | ||
const exitCodes = []; | ||
const closeStreams = _.map(streams, 'close'); | ||
const closeStream = Rx.Observable.merge.apply(this, closeStreams); | ||
let othersKilled = false; | ||
// TODO: Is it possible that amount of close events !== count of spawned? | ||
closeStream.subscribe(function(event) { | ||
var exitCode = event.data; | ||
var nonSuccess = exitCode !== 0; | ||
const exitCode = event.data; | ||
const nonSuccess = exitCode !== 0; | ||
exitCodes.push(exitCode); | ||
var prefix = getPrefix(childrenInfo, event.child); | ||
var childInfo = childrenInfo[event.child.pid]; | ||
var prefixColor = childInfo.prefixColor; | ||
var command = childInfo.command; | ||
const prefix = getPrefix(childrenInfo, event.child); | ||
const childInfo = childrenInfo[event.child.pid]; | ||
const prefixColor = childInfo.prefixColor; | ||
const command = childInfo.command; | ||
logEvent(prefix, prefixColor, command + ' exited with code ' + exitCode); | ||
@@ -363,9 +411,9 @@ | ||
if (!othersKilled) { | ||
if (config.killOthers) { | ||
killOtherProcesses(aliveChildren); | ||
othersKilled = true; | ||
} else if (config.killOthersOnFail && nonSuccess) { | ||
killOtherProcesses(aliveChildren); | ||
othersKilled = true; | ||
} | ||
if (config.killOthers) { | ||
killOtherProcesses(aliveChildren); | ||
othersKilled = true; | ||
} else if (config.killOthersOnFail && nonSuccess) { | ||
killOtherProcesses(aliveChildren); | ||
othersKilled = true; | ||
} | ||
} | ||
@@ -377,7 +425,7 @@ }); | ||
setTimeout(function() { | ||
var childInfo = childrenInfo[event.child.pid]; | ||
var prefix = getPrefix(childrenInfo, event.child); | ||
var prefixColor = childInfo.prefixColor; | ||
const childInfo = childrenInfo[event.child.pid]; | ||
const prefix = getPrefix(childrenInfo, event.child); | ||
const prefixColor = childInfo.prefixColor; | ||
logEvent(prefix, prefixColor, childInfo.command + ' restarted'); | ||
var newChild = spawnChild(childInfo.command, childInfo.options); | ||
const newChild = spawnChild(childInfo.command, childInfo.options); | ||
@@ -387,4 +435,4 @@ childrenInfo[newChild.pid] = childrenInfo[event.child.pid]; | ||
var children = [newChild]; | ||
var streams = toStreams(children); | ||
const children = [newChild]; | ||
const streams = toStreams(children); | ||
handleChildEvents(streams, children, childrenInfo); | ||
@@ -404,14 +452,14 @@ }, config.restartAfter); | ||
function exit(childExitCodes) { | ||
var success; | ||
let success; | ||
switch (config.success) { | ||
case 'first': | ||
success = _.first(childExitCodes) === 0; | ||
break; | ||
case 'last': | ||
success = _.last(childExitCodes) === 0; | ||
break; | ||
default: | ||
success = _.every(childExitCodes, function(code) { | ||
return code === 0; | ||
}); | ||
case 'first': | ||
success = _.first(childExitCodes) === 0; | ||
break; | ||
case 'last': | ||
success = _.last(childExitCodes) === 0; | ||
break; | ||
default: | ||
success = _.every(childExitCodes, function(code) { | ||
return code === 0; | ||
}); | ||
} | ||
@@ -423,7 +471,7 @@ process.exit(success ? 0 : 1); | ||
// Output emitted errors from child process | ||
var errorStreams = _.map(streams, 'error'); | ||
var processErrorStream = Rx.Observable.merge.apply(this, errorStreams); | ||
const errorStreams = _.map(streams, 'error'); | ||
const processErrorStream = Rx.Observable.merge.apply(this, errorStreams); | ||
processErrorStream.subscribe(function(event) { | ||
var command = childrenInfo[event.child.pid].command; | ||
const command = childrenInfo[event.child.pid].command; | ||
logError('', chalk.gray.dim, 'Error occured when executing command: ' + command); | ||
@@ -443,9 +491,10 @@ logError('', chalk.gray.dim, event.data.stack); | ||
function getPrefix(childrenInfo, child) { | ||
var prefixes = getPrefixes(childrenInfo, child); | ||
if (_.includes(_.keys(prefixes), config.prefix)) { | ||
return '[' + prefixes[config.prefix] + '] '; | ||
const prefixes = getPrefixes(childrenInfo, child); | ||
const prefixType = config.prefix || prefixes.name ? 'name' : 'index'; | ||
if (_.includes(_.keys(prefixes), prefixType)) { | ||
return '[' + prefixes[prefixType] + '] '; | ||
} | ||
return _.reduce(prefixes, function(memo, val, key) { | ||
var re = new RegExp('{' + key + '}', 'g'); | ||
const re = new RegExp('{' + key + '}', 'g'); | ||
return memo.replace(re, val); | ||
@@ -456,3 +505,3 @@ }, config.prefix) + ' '; | ||
function getPrefixes(childrenInfo, child) { | ||
var prefixes = {}; | ||
const prefixes = {}; | ||
@@ -465,3 +514,3 @@ prefixes.none = ''; | ||
var command = childrenInfo[child.pid].command; | ||
const command = childrenInfo[child.pid].command; | ||
prefixes.command = shortenText(command, config.prefixLength); | ||
@@ -477,7 +526,7 @@ return prefixes; | ||
var endLength = Math.floor(length / 2); | ||
var startLength = length - endLength; | ||
const endLength = Math.floor(length / 2); | ||
const startLength = length - endLength; | ||
var first = text.substring(0, startLength); | ||
var last = text.substring(text.length - endLength, text.length); | ||
const first = text.substring(0, startLength); | ||
const last = text.substring(text.length - endLength, text.length); | ||
return first + cut + last; | ||
@@ -491,3 +540,5 @@ } | ||
function logEvent(prefix, prefixColor, text) { | ||
if (config.raw) return; | ||
if (config.raw) { | ||
return; | ||
} | ||
@@ -503,7 +554,7 @@ logWithPrefix(prefix, prefixColor, text + '\n', chalk.gray.dim); | ||
var lastChar; | ||
let lastChar; | ||
function logWithPrefix(prefix, prefixColor, text, color) { | ||
if (config.raw) { | ||
if (config.raw) { | ||
process.stdout.write(text); | ||
@@ -515,7 +566,7 @@ return; | ||
var lines = text.split('\n'); | ||
const lines = text.split('\n'); | ||
// Do not bgColor trailing space | ||
var coloredPrefix = colorText(prefix.replace(/ $/, ''), prefixColor) + ' '; | ||
var paddedLines = _.map(lines, function(line, index) { | ||
var coloredLine = color ? colorText(line, color) : line; | ||
const coloredPrefix = colorText(prefix.replace(/ $/, ''), prefixColor) + ' '; | ||
const paddedLines = _.map(lines, function(line, index) { | ||
let coloredLine = color ? colorText(line, color) : line; | ||
if (index !== 0 && index !== (lines.length - 1)) { | ||
@@ -527,3 +578,3 @@ coloredLine = coloredPrefix + coloredLine; | ||
if (!lastChar || lastChar == '\n' ){ | ||
if (!lastChar || lastChar === '\n' ) { | ||
process.stdout.write(coloredPrefix); | ||
@@ -530,0 +581,0 @@ } |
['SIGINT', 'SIGTERM'].forEach(function(signal) { | ||
process.on(signal, function() { | ||
console.log(signal); | ||
process.exit(1); | ||
}); | ||
process.on(signal, function() { | ||
console.log(signal); | ||
process.exit(1); | ||
}); | ||
}); | ||
function doNothing() { | ||
setTimeout(doNothing, 1000); | ||
setTimeout(doNothing, 1000); | ||
} | ||
@@ -11,0 +11,0 @@ |
@@ -0,14 +1,15 @@ | ||
'use strict'; | ||
// Test basic usage of cli | ||
var path = require('path'); | ||
var assert = require('assert'); | ||
var run = require('./utils').run; | ||
var IS_WINDOWS = /^win/.test(process.platform); | ||
const path = require('path'); | ||
const assert = require('assert'); | ||
const run = require('./utils').run; | ||
const IS_WINDOWS = /^win/.test(process.platform); | ||
// Note: Set the DEBUG_TESTS environment variable to `true` to see output of test commands. | ||
var TEST_DIR = 'dir/'; | ||
const TEST_DIR = 'dir/'; | ||
// Abs path to test directory | ||
var testDir = path.resolve(__dirname); | ||
const testDir = path.resolve(__dirname); | ||
process.chdir(path.join(testDir, '..')); | ||
@@ -63,5 +64,5 @@ | ||
it('--kill-others-on-fail should NOT kill other commands if none of them exits with non-zero status code', (done) => { | ||
var readline = require('readline'); | ||
var exits = 0; | ||
var sigtermInOutput = false; | ||
const readline = require('readline'); | ||
let exits = 0; | ||
let sigtermInOutput = false; | ||
@@ -80,3 +81,3 @@ run('node ./src/main.js --kill-others-on-fail "echo killTest1" "echo killTest2" "echo killTest3"', { | ||
}).then(function() { | ||
if(sigtermInOutput) { | ||
if (sigtermInOutput) { | ||
done(new Error('There was a "SIGTERM" in console output')); | ||
@@ -122,3 +123,3 @@ } else if (exits !== 3) { | ||
it('--prefix should default to "index"', () => { | ||
var collectedLines = [] | ||
const collectedLines = []; | ||
@@ -128,3 +129,3 @@ return run('node ./src/main.js "echo one" "echo two"', { | ||
if (/(one|two)$/.exec(line)) { | ||
collectedLines.push(line) | ||
collectedLines.push(line); | ||
} | ||
@@ -136,7 +137,7 @@ } | ||
collectedLines.sort() | ||
collectedLines.sort(); | ||
assert.deepEqual(collectedLines, [ | ||
'[0] one', | ||
'[1] two' | ||
]) | ||
]); | ||
}); | ||
@@ -146,3 +147,3 @@ }); | ||
it('--names should set a different default prefix', () => { | ||
var collectedLines = [] | ||
const collectedLines = []; | ||
@@ -152,3 +153,3 @@ return run('node ./src/main.js -n aa,bb "echo one" "echo two"', { | ||
if (/(one|two)$/.exec(line)) { | ||
collectedLines.push(line) | ||
collectedLines.push(line); | ||
} | ||
@@ -160,7 +161,7 @@ } | ||
collectedLines.sort() | ||
collectedLines.sort(); | ||
assert.deepEqual(collectedLines, [ | ||
'[aa] one', | ||
'[bb] two' | ||
]) | ||
]); | ||
}); | ||
@@ -170,5 +171,5 @@ }); | ||
it('--allow-restart should restart a proccess with non-zero exit code', (done) => { | ||
var readline = require('readline'); | ||
var exitedWithOne = false; | ||
var restarted = false; | ||
const readline = require('readline'); | ||
let exitedWithOne = false; | ||
let restarted = false; | ||
@@ -178,5 +179,5 @@ run('node ./src/main.js --allow-restart "sleep 0.1 && exit 1" "sleep 1"', { | ||
onOutputLine: (line) => { | ||
var re = /exited with code (.+)/.exec(line); | ||
const re = /exited with code (.+)/.exec(line); | ||
if (re && re[1] === '1') { | ||
exitedWithOne = true | ||
exitedWithOne = true; | ||
} | ||
@@ -198,4 +199,4 @@ | ||
it('--restart-after=n should restart a proccess after n miliseconds', (done) => { | ||
var readline = require('readline'); | ||
var start, end; | ||
const readline = require('readline'); | ||
let start, end; | ||
@@ -223,4 +224,4 @@ run('node ./src/main.js --allow-restart --restart-after 300 "exit 1" "sleep 1"', { | ||
it('--restart-tries=n should restart a proccess at most n times', (done) => { | ||
var readline = require('readline'); | ||
var restartedTimes = 0; | ||
const readline = require('readline'); | ||
let restartedTimes = 0; | ||
@@ -235,3 +236,3 @@ run('node ./src/main.js --allow-restart --restart-tries 2 "exit 1" "sleep 1"', { | ||
}).then(function() { | ||
if (restartedTimes == 2) { | ||
if (restartedTimes === 2) { | ||
done(); | ||
@@ -245,42 +246,161 @@ } else { | ||
['SIGINT', 'SIGTERM'].forEach((signal) => { | ||
if (IS_WINDOWS) { | ||
console.log('IS_WINDOWS=true'); | ||
console.log('Skipping SIGINT/SIGTERM propagation tests ..'); | ||
return; | ||
} | ||
if (IS_WINDOWS) { | ||
console.log('IS_WINDOWS=true'); | ||
console.log('Skipping SIGINT/SIGTERM propagation tests ..'); | ||
return; | ||
} | ||
it('killing it with ' + signal + ' should propagate the signal to the children', function(done) { | ||
var readline = require('readline'); | ||
var waitingStart = 2; | ||
var waitingSignal = 2; | ||
it('killing it with ' + signal + ' should propagate the signal to the children', function(done) { | ||
const readline = require('readline'); | ||
let waitingStart = 2; | ||
let waitingSignal = 2; | ||
function waitForSignal(cb) { | ||
if (waitingSignal) { | ||
setTimeout(waitForSignal, 100); | ||
} else { | ||
cb(); | ||
} | ||
} | ||
function waitForSignal(cb) { | ||
if (waitingSignal) { | ||
setTimeout(waitForSignal, 100); | ||
} else { | ||
cb(); | ||
} | ||
} | ||
run('node ./src/main.js "node ./test/support/signal.js" "node ./test/support/signal.js"', { | ||
onOutputLine: function(line, child) { | ||
// waiting for startup | ||
if (/STARTED/.test(line)) { | ||
waitingStart--; | ||
run('node ./src/main.js "node ./test/support/signal.js" "node ./test/support/signal.js"', { | ||
onOutputLine: function(line, child) { | ||
// waiting for startup | ||
if (/STARTED/.test(line)) { | ||
waitingStart--; | ||
} | ||
if (!waitingStart) { | ||
// both processes are started | ||
child.kill(signal); | ||
} | ||
// waiting for signal | ||
if (new RegExp(signal).test(line)) { | ||
waitingSignal--; | ||
} | ||
} | ||
}).then(function() { | ||
waitForSignal(done); | ||
}); | ||
}); | ||
}); | ||
it('sends input to default stdin target process', (done) => { | ||
let echoed = false; | ||
run('node ./src/main.js "node ./test/support/read-echo.js"', { | ||
onOutputLine: (line, child) => { | ||
if (/READING/.test(line)) { | ||
child.stdin.write('stop\n'); | ||
} | ||
if (/\[\d+\] stop/.test(line)) { | ||
echoed = true; | ||
} | ||
} | ||
if (!waitingStart) { | ||
// both processes are started | ||
child.kill(signal); | ||
}) | ||
.then(() => { | ||
assert(echoed); | ||
}) | ||
.then(done, done); | ||
}); | ||
it('sends input to specified default stdin target process', (done) => { | ||
let echoed = false; | ||
run('node ./src/main.js --default-input-target 1 "echo test" "node ./test/support/read-echo.js"', { | ||
onOutputLine: (line, child) => { | ||
if (/READING/.test(line)) { | ||
child.stdin.write('stop\n'); | ||
} | ||
if (/\[1\] stop/.test(line)) { | ||
echoed = true; | ||
} | ||
} | ||
}) | ||
.then(() => { | ||
assert(echoed); | ||
}) | ||
.then(done, done); | ||
}); | ||
// waiting for signal | ||
if (new RegExp(signal).test(line)) { | ||
waitingSignal--; | ||
it('sends input to child specified by index', (done) => { | ||
let echoed = false; | ||
run('node ./src/main.js "echo test" "node ./test/support/read-echo.js"', { | ||
onOutputLine: (line, child) => { | ||
if (/READING/.test(line)) { | ||
child.stdin.write('1:stop\n'); | ||
} | ||
if (/\[1\] stop/.test(line)) { | ||
echoed = true; | ||
} | ||
} | ||
} | ||
}).then(function() { | ||
waitForSignal(done); | ||
}); | ||
}); | ||
}) | ||
.then(() => { | ||
assert(echoed); | ||
}) | ||
.then(done, done); | ||
}); | ||
it('emits error when specified read stream is not found', (done) => { | ||
let errorEmitted = false; | ||
run('node ./src/main.js "echo test" "node ./test/support/read-echo.js"', { | ||
onOutputLine: (line, child) => { | ||
if (/READING/.test(line)) { | ||
child.stdin.write('2:stop\n'); | ||
} | ||
if ('Unable to find command [2]' === line.trim()) { | ||
errorEmitted = true; | ||
// Stop read process to continue the test | ||
child.stdin.write('1:stop\n'); | ||
} | ||
} | ||
}) | ||
.then(() => { | ||
assert(errorEmitted); | ||
}) | ||
.then(done, done); | ||
}); | ||
it('should expand npm: command shortcuts', (done) => { | ||
let echo1 = false; | ||
let echo2 = false; | ||
run('node ./src/main.js "npm:echo-test" "npm:echo -- testarg"', { | ||
onOutputLine: function(line, child) { | ||
if (line === '[echo-test] test') { | ||
echo1 = true; | ||
} else if (line === '[echo] testarg') { | ||
echo2 = true; | ||
} | ||
} | ||
}) | ||
.then((exitCode) => { | ||
assert.strictEqual(exitCode, 0); | ||
assert.ok(echo1); | ||
assert.ok(echo2); | ||
}) | ||
.then(done, done); | ||
}); | ||
it('expands npm run shortcut wildcards', (done) => { | ||
let echoBeep = false; | ||
let echoBoop = false; | ||
run('node ./src/main.js "npm:echo-sound-*"', { | ||
onOutputLine: (line, child) => { | ||
if (line === '[beep] beep') { | ||
echoBeep = true; | ||
} else if (line === '[boop] boop') { | ||
echoBoop = true; | ||
} | ||
} | ||
}) | ||
.then((exitCode) => { | ||
assert.strictEqual(exitCode, 0); | ||
assert.ok(echoBeep); | ||
assert.ok(echoBoop); | ||
}) | ||
.then(done, done); | ||
}); | ||
}); | ||
@@ -287,0 +407,0 @@ |
@@ -1,8 +0,9 @@ | ||
var childProcess = require('child_process'); | ||
var _ = require('lodash'); | ||
var readline = require('readline') | ||
var shellQuote = require('shell-quote'); | ||
'use strict'; | ||
const childProcess = require('child_process'); | ||
const _ = require('lodash'); | ||
const readline = require('readline'); | ||
const shellQuote = require('shell-quote'); | ||
// If true, output of commands are shown | ||
var DEBUG_TESTS = process.env.DEBUG_TESTS === 'true'; | ||
const DEBUG_TESTS = process.env.DEBUG_TESTS === 'true'; | ||
@@ -14,6 +15,7 @@ function run(cmd, opts) { | ||
onOutputLine: undefined, | ||
onErrorLine: undefined | ||
}, opts); | ||
var child; | ||
var parts = shellQuote.parse(cmd); | ||
let child; | ||
const parts = shellQuote.parse(cmd); | ||
try { | ||
@@ -31,2 +33,8 @@ child = childProcess.spawn(_.head(parts), _.tail(parts), { | ||
if (opts.onErrorLine) { | ||
readLines(child, opts.onErrorLine, 'stderr'); | ||
} | ||
readLines(child, (l) => { console.log(l); }, 'stderr'); | ||
return new Promise(function(resolve, reject) { | ||
@@ -43,5 +51,7 @@ child.on('error', function(err) { | ||
function readLines(child, callback) { | ||
var rl = readline.createInterface({ | ||
input: child.stdout, | ||
function readLines(child, callback, src) { | ||
src = src || 'stdout'; | ||
const rl = readline.createInterface({ | ||
input: child[src], | ||
output: null | ||
@@ -55,3 +65,3 @@ }); | ||
callback(line, child) | ||
callback(line, child); | ||
}); | ||
@@ -58,0 +68,0 @@ } |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
546702
22
1210
251
9
10
+ Addedread-pkg@^3.0.0
+ Addedansi-styles@3.2.1(transitive)
+ Addedchalk@2.4.2(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedhas-flag@3.0.0(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedhosted-git-info@2.8.9(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-core-module@2.15.1(transitive)
+ Addedjson-parse-better-errors@1.0.2(transitive)
+ Addedload-json-file@4.0.0(transitive)
+ Addednormalize-package-data@2.5.0(transitive)
+ Addedparse-json@4.0.0(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpath-type@3.0.0(transitive)
+ Addedpify@3.0.0(transitive)
+ Addedread-pkg@3.0.0(transitive)
+ Addedresolve@1.22.8(transitive)
+ Addedsemver@5.7.2(transitive)
+ Addedspdx-correct@3.2.0(transitive)
+ Addedspdx-exceptions@2.5.0(transitive)
+ Addedspdx-expression-parse@3.0.1(transitive)
+ Addedspdx-license-ids@3.0.20(transitive)
+ Addedstrip-bom@3.0.0(transitive)
+ Addedsupports-color@5.5.0(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedvalidate-npm-package-license@3.0.4(transitive)
- Removedansi-regex@0.2.1(transitive)
- Removedansi-styles@1.1.0(transitive)
- Removedchalk@0.5.1(transitive)
- Removedhas-ansi@0.1.0(transitive)
- Removedstrip-ansi@0.3.0(transitive)
- Removedsupports-color@0.2.0(transitive)
Updatedchalk@^2.4.1