Skip to content
Permalink
Browse files
Use cross-spawn for platform-independent spawning
Closes #50.
  • Loading branch information
raphinesse authored and dpogue committed Nov 1, 2018
1 parent be6c0ea commit 1b4c64925a856b5470cd5e6f76395f2987bd8763
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 47 deletions.
@@ -25,6 +25,7 @@
"dependencies": {
"ansi": "^0.3.1",
"bplist-parser": "^0.1.0",
"cross-spawn": "^6.0.5",
"elementtree": "0.1.7",
"endent": "^1.1.1",
"fs-extra": "^6.0.1",
@@ -0,0 +1,5 @@
@echo off

for %%x in (%*) do (
echo %%~x
)
@@ -18,6 +18,7 @@
*/

var Q = require('q');
var path = require('path');
var superspawn = require('../src/superspawn');

var LS = process.platform === 'win32' ? 'dir' : 'ls';
@@ -88,4 +89,19 @@ describe('spawn method', function () {
});
});

describe('operation on windows', () => {
const TEST_SCRIPT = path.join(__dirname, 'fixtures/echo-args.cmd');
const TEST_ARGS = [ 'install', 'foo@^1.2.3', 'c o r d o v a' ];

it('should escape arguments if `cmd` is not an *.exe', () => {
if (process.platform !== 'win32') {
pending('test should only run on windows');
}

superspawn.spawn(TEST_SCRIPT, TEST_ARGS).then(output => {
expect(output.split(/\r?\n/)).toEqual(TEST_ARGS);
});
});
});

});
@@ -17,41 +17,13 @@
under the License.
*/

var child_process = require('child_process');
var crossSpawn = require('cross-spawn');
var fs = require('fs-extra');
var path = require('path');
var _ = require('underscore');
var Q = require('q');
var which = require('which');
var events = require('./events');
var iswin32 = process.platform === 'win32';

// On Windows, spawn() for batch files requires absolute path & having the extension.
function resolveWindowsExe (cmd) {
var winExtensions = ['.exe', '.bat', '.cmd', '.js', '.vbs'];
function isValidExe (c) {
return winExtensions.includes(path.extname(c)) && fs.existsSync(c);
}
if (isValidExe(cmd)) {
return cmd;
}
cmd = which.sync(cmd, { nothrow: true }) || cmd;
if (!isValidExe(cmd)) {
winExtensions.some(function (ext) {
if (fs.existsSync(cmd + ext)) {
cmd = cmd + ext;
return true;
}
});
}
return cmd;
}

function maybeQuote (a) {
if (/^[^"].*[ &].*[^"]/.test(a)) return '"' + a + '"';
return a;
}

/**
* A special implementation for child_process.spawn that handles
* Windows-specific issues with batch files and spaces in paths. Returns a
@@ -91,22 +63,6 @@ exports.spawn = function (cmd, args, opts) {
var spawnOpts = {};
var d = Q.defer();

if (iswin32) {
cmd = resolveWindowsExe(cmd);
// If we couldn't find the file, likely we'll end up failing,
// but for things like "del", cmd will do the trick.
if (path.extname(cmd) !== '.exe') {
var cmdArgs = '"' + [cmd].concat(args).map(maybeQuote).join(' ') + '"';
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
args = [['/s', '/c', cmdArgs].join(' ')];
cmd = 'cmd';
spawnOpts.windowsVerbatimArguments = true;
} else if (!fs.existsSync(cmd)) {
// We need to use /s to ensure that spaces are parsed properly with cmd spawned content
args = ['/s', '/c', cmd].concat(args).map(maybeQuote);
}
}

if (opts.stdio !== 'default') {
// Ignore 'default' value for stdio because it corresponds to child_process's default 'pipe' option
spawnOpts.stdio = opts.stdio;
@@ -129,9 +85,9 @@ exports.spawn = function (cmd, args, opts) {
}
}

events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + maybeQuote(cmd) + ' ' + args.map(maybeQuote).join(' '));
events.emit(opts.printCommand ? 'log' : 'verbose', 'Running command: ' + cmd + ' ' + args.join(' '));

var child = child_process.spawn(cmd, args, spawnOpts);
var child = crossSpawn.spawn(cmd, args, spawnOpts);
var capturedOut = '';
var capturedErr = '';

0 comments on commit 1b4c649

Please sign in to comment.