Repository: cordova-lib Updated Branches: refs/heads/master ffb779bef -> 540a623bb
CB-10052 Expose child process' io streams via promise progress notification. This closes #369 Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/540a623b Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/540a623b Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/540a623b Branch: refs/heads/master Commit: 540a623bb4d13bf13c8eab874d3ad2377eabe3fd Parents: ffb779b Author: Vladimir Kotikov <[email protected]> Authored: Fri Jan 22 16:39:13 2016 +0300 Committer: Vladimir Kotikov <[email protected]> Committed: Wed Feb 3 11:53:41 2016 +0300 ---------------------------------------------------------------------- cordova-common/README.md | 4 +++ cordova-common/spec/superspawn.spec.js | 56 +++++++++++++++++++++++++++++ cordova-common/src/superspawn.js | 56 ++++++++++++++++++++++------- 3 files changed, 103 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/540a623b/cordova-common/README.md ---------------------------------------------------------------------- diff --git a/cordova-common/README.md b/cordova-common/README.md index f19b59f..6454481 100644 --- a/cordova-common/README.md +++ b/cordova-common/README.md @@ -107,6 +107,10 @@ Usage: ``` var superspawn = require('cordova-common').superspawn; superspawn.spawn('adb', ['devices']) +.progress(function(data){ + if (data.stderr) + console.error('"adb devices" raised an error: ' + data.stderr); +}) .then(function(devices){ // Do something... }) http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/540a623b/cordova-common/spec/superspawn.spec.js ---------------------------------------------------------------------- diff --git a/cordova-common/spec/superspawn.spec.js b/cordova-common/spec/superspawn.spec.js new file mode 100644 index 0000000..2539199 --- /dev/null +++ b/cordova-common/spec/superspawn.spec.js @@ -0,0 +1,56 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var Q = require('q'); +var superspawn = require('../src/superspawn'); + +var LS = process.platform === 'win32' ? 'dir' : 'ls'; + +describe('spawn method', function() { + var progressSpy, failSpy; + + beforeEach(function () { + progressSpy = jasmine.createSpy('progress'); + failSpy = jasmine.createSpy('fail'); + }); + + it('should return a promise', function () { + expect(Q.isPromise(superspawn.spawn(LS))).toBe(true); + expect(Q.isPromise(superspawn.spawn('invalid_command'))).toBe(true); + }); + + it('should notify about stdout "data" events', function (done) { + superspawn.spawn(LS, [], {stdio: 'pipe'}) + .progress(progressSpy) + .fin(function () { + expect(progressSpy).toHaveBeenCalledWith({'stdout': jasmine.any(String)}); + done(); + }); + }); + + it('should notify about stderr "data" events', function (done) { + superspawn.spawn(LS, ['doesnt-exist'], {stdio: 'pipe'}) + .progress(progressSpy) + .fin(function () { + expect(progressSpy).toHaveBeenCalledWith({'stderr': jasmine.any(String)}); + done(); + }); + }); + +}); http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/540a623b/cordova-common/src/superspawn.js ---------------------------------------------------------------------- diff --git a/cordova-common/src/superspawn.js b/cordova-common/src/superspawn.js index a0372fa..a3f1431 100644 --- a/cordova-common/src/superspawn.js +++ b/cordova-common/src/superspawn.js @@ -52,15 +52,39 @@ function maybeQuote(a) { return a; } -// opts: -// printCommand: Whether to log the command (default: false) -// stdio: 'default' is to capture output and returning it as a string to success (same as exec) -// 'ignore' means don't bother capturing it -// 'inherit' means pipe the input & output. This is required for anything that prompts. -// env: Map of extra environment variables. -// cwd: Working directory for the command. -// chmod: If truthy, will attempt to set the execute bit before executing on non-Windows platforms. -// Returns a promise that succeeds only for return code = 0. +/** + * A special implementation for child_process.spawn that handles + * Windows-specific issues with batch files and spaces in paths. Returns a + * promise that succeeds only for return code 0. It is also possible to + * subscribe on spawned process' stdout and stderr streams using progress + * handler for resultant promise. + * + * @example spawn('mycommand', [], {stdio: 'pipe'}) .progress(function (stdio){ + * if (stdio.stderr) { console.error(stdio.stderr); } }) + * .then(function(result){ // do other stuff }) + * + * @param {String} cmd A command to spawn + * @param {String[]} [args=[]] An array of arguments, passed to spawned + * process + * @param {Object} [opts={}] A configuration object + * @param {String|String[]|Object} opts.stdio Property that configures how + * spawned process' stdio will behave. Has the same meaning and possible + * values as 'stdio' options for child_process.spawn method + * (https://nodejs.org/api/child_process.html#child_process_options_stdio). + * @param {Object} [env={}] A map of extra environment variables + * @param {String} [cwd=process.cwd()] Working directory for the command + * @param {Boolean} [chmod=false] If truthy, will attempt to set the execute + * bit before executing on non-Windows platforms + * + * @return {Promise} A promise that is either fulfilled if the spawned + * process is exited with zero error code or rejected otherwise. If the + * 'stdio' option set to 'default' or 'pipe', the promise also emits progress + * messages with the following contents: + * { + * 'stdout': ..., + * 'stderr': ... + * } + */ exports.spawn = function(cmd, args, opts) { args = args || []; opts = opts || {}; @@ -83,17 +107,19 @@ exports.spawn = function(cmd, args, opts) { } } - if (opts.stdio == 'ignore') { - spawnOpts.stdio = 'ignore'; - } else if (opts.stdio == 'inherit') { - spawnOpts.stdio = 'inherit'; + if (opts.stdio !== 'default') { + // Ignore 'default' value for stdio because it corresponds to child_process's default 'pipe' option + spawnOpts.stdio = opts.stdio; } + if (opts.cwd) { spawnOpts.cwd = opts.cwd; } + if (opts.env) { spawnOpts.env = _.extend(_.extend({}, process.env), opts.env); } + if (opts.chmod && !iswin32) { try { // This fails when module is installed in a system directory (e.g. via sudo npm install) @@ -113,11 +139,15 @@ exports.spawn = function(cmd, args, opts) { child.stdout.setEncoding('utf8'); child.stdout.on('data', function(data) { capturedOut += data; + d.notify({'stdout': data}); }); + } + if (child.stderr) { child.stderr.setEncoding('utf8'); child.stderr.on('data', function(data) { capturedErr += data; + d.notify({'stderr': data}); }); } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
