UNPKG

promise-spawner

Version:

Lazy nodejs spawner with stdout/stderr streams and error handling through promises

147 lines (122 loc) 3.87 kB
var spawn = require('child_process').spawn var through = require('through2') var platform = require('os').platform() var util = require('util') var eol = require('os').EOL /** * Spawner - a wrapper to promised spawn * @param {Object} options modifiers {out: Function|string, err: Function|string} * @param {Object} spawn_options options to spawn process (nodejs.org/api/child_process.html) * @return {Object} Wrapper {Promise sp = spawn, Stream out, Stream err} */ var Spawner = function(options, spawn_options) { var opt = {} var self = this if(typeof options == 'object') { opt.out = options.out !== undefined ? options.out : opt.out opt.err = options.err !== undefined ? options.err : opt.err } //those are global streams to allow piping of the current running spawn this.out = through.obj(function (chunk, enc, callback) { this.push(chunk) self.data.out.push(chunk) return callback() }) this.err = through.obj(function (chunk, enc, callback) { this.push(chunk) self.data.err.push(chunk) return callback() }) var spawn = function() { var args = [].slice.call(arguments) var last = args.length - 1 if(!util.isArray(args[last]) && typeof args[last] == 'object') { var options = args.pop() } args = [].concat.apply([], args) //flatten var num_commands = args.length var commands = [] var i = 0 for(i; i < num_commands; i++) { commands.push({ command: this.command, args: this.args.concat([args[i]]), options: options || spawn_options || {} }) } //reset previous spawn datas this.data = {out: [], err: []} return new Promise((resolve, reject) => { var j = 0 //function to loop through promises commands //This handles the catch promise when a command fail //Then, we can reject the spawner global promise var loop = (array) => { var command = array.shift() this .promise_spawn(command, opt) .then(function(code) { if(j < num_commands - 1) { j++ return loop(array) } else { resolve(code) } }).catch(function(code) { reject(code) }) } loop(commands) }) } //main function wrapper return { sp: spawn.bind(this), spawn: spawn.bind(this), out: this.out, err: this.err } } Spawner.prototype = { command: platform == 'win32' ? 'cmd.exe' : 'sh', args: platform == 'win32' ? ['/s', '/c'] : ['-c'], /** * Promisify Spawn * @param {Object} command {command, args, spawn_options} * @param {Object} options modifiers from Spawner * @return {Promise} Promise resolve on error code = 0 or reject */ promise_spawn: function(command, options) { return new Promise((resolve, reject) => { var s = spawn(command.command, command.args, command.options) //spawn stdout to the Stream modifier //writes data back to global Stream s.stdout && s.stdout .pipe(this.pipe(options.out)) .on('data', (d) => { this.out.write(d) }) s.stderr && s.stderr .pipe(this.pipe(options.err)) .on('data', (d) => { this.err.write(d) }) s.on('close', (code) => { if(code === 0) { resolve({code: code, data: this.data}) } else { reject({code: code, data: this.data}) } }) }) }, pipe: function(modifier) { modifier = modifier === undefined ? '' : modifier return through.obj(function (chunk, enc, callback) { chunk = typeof modifier == 'function' ? modifier(chunk) : modifier + chunk.toString().replace(eol, '') this.push(chunk) callback() }) } } module.exports = Spawner