pax_global_header00006660000000000000000000000064130546202440014512gustar00rootroot0000000000000052 comment=1da4c09ccf658079849a3d191b16e59bc600e8b4 node-cross-spawn-5.1.0/000077500000000000000000000000001305462024400147175ustar00rootroot00000000000000node-cross-spawn-5.1.0/.editorconfig000066400000000000000000000003341305462024400173740ustar00rootroot00000000000000root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false [package.json] indent_size = 2 node-cross-spawn-5.1.0/.eslintrc000066400000000000000000000001771305462024400165500ustar00rootroot00000000000000{ "root": true, "extends": [ "@satazor/eslint-config/es5", "@satazor/eslint-config/addons/node" ] }node-cross-spawn-5.1.0/.gitignore000066400000000000000000000001201305462024400167000ustar00rootroot00000000000000node_modules/ npm-debug.* test/fixtures/(* test/fixtures/shebang_noenv test/tmp node-cross-spawn-5.1.0/.travis.yml000066400000000000000000000001111305462024400170210ustar00rootroot00000000000000language: node_js node_js: - '0.10' - '0.12' - '4' - '6' - '7' node-cross-spawn-5.1.0/CHANGELOG.md000066400000000000000000000003501305462024400165260ustar00rootroot00000000000000## 5.0.0 - 2016-10-30 - Add support for `options.shell` - Improve parsing of shebangs by using [`shebang-command`](https://github.com/kevva/shebang-command) module - Refactor some code to make it more clear - Update README caveats node-cross-spawn-5.1.0/LICENSE000066400000000000000000000020401305462024400157200ustar00rootroot00000000000000Copyright (c) 2014 IndigoUnited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-cross-spawn-5.1.0/README.md000066400000000000000000000064531305462024400162060ustar00rootroot00000000000000# cross-spawn [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] [npm-url]:https://npmjs.org/package/cross-spawn [downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg [npm-image]:http://img.shields.io/npm/v/cross-spawn.svg [travis-url]:https://travis-ci.org/IndigoUnited/node-cross-spawn [travis-image]:http://img.shields.io/travis/IndigoUnited/node-cross-spawn/master.svg [appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn [appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg [david-dm-url]:https://david-dm.org/IndigoUnited/node-cross-spawn [david-dm-image]:https://img.shields.io/david/IndigoUnited/node-cross-spawn.svg [david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-cross-spawn#info=devDependencies [david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-cross-spawn.svg A cross platform solution to node's spawn and spawnSync. ## Installation `$ npm install cross-spawn` If you are using `spawnSync` on node 0.10 or older, you will also need to install `spawn-sync`: `$ npm install spawn-sync` ## Why Node has issues when using spawn on Windows: - It ignores [PATHEXT](https://github.com/joyent/node/issues/2318) - It does not support [shebangs](http://pt.wikipedia.org/wiki/Shebang) - No `options.shell` support on node < v6 - It does not allow you to run `del` or `dir` All these issues are handled correctly by `cross-spawn`. There are some known modules, such as [win-spawn](https://github.com/ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments. ## Usage Exactly the same way as node's [`spawn`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) or [`spawnSync`](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options), so it's a drop in replacement. ```js var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' }); // Spawn NPM synchronously var results = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' }); ``` ## Caveats #### `options.shell` as an alternative to `cross-spawn` Starting from node v6, `spawn` has a `shell` option that allows you run commands from within a shell. This new option solves most of the problems that `cross-spawn` attempts to solve, but: - It's not supported in node < v6 - It has no support for shebangs on Windows - You must manually escape the command and arguments which is very error prone, specially when passing user input If you are using the `shell` option to spawn a command in a cross platform way, consider using `cross-spawn` instead. You have been warned. #### Shebangs While `cross-spawn` handles shebangs on Windows, its support is limited: e.g.: it doesn't handle arguments after the path, e.g.: `#!/bin/bash -e`. Remember to always test your code on Windows! ## Tests `$ npm test` ## License Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php). node-cross-spawn-5.1.0/appveyor.yml000066400000000000000000000011101305462024400173000ustar00rootroot00000000000000# appveyor file # http://www.appveyor.com/docs/appveyor-yml # build version format version: "{build}" # fix lineendings in Windows init: - git config --global core.autocrlf input # what combinations to test environment: matrix: - nodejs_version: 0.10 - nodejs_version: 0.12 - nodejs_version: 4 - nodejs_version: 6 - nodejs_version: 7 # get the latest stable version of Node 0.STABLE.latest install: - ps: Install-Product node $env:nodejs_version - npm install build: off test_script: - node --version - npm --version - cmd: npm test --no-color node-cross-spawn-5.1.0/index.js000066400000000000000000000031551305462024400163700ustar00rootroot00000000000000'use strict'; var cp = require('child_process'); var parse = require('./lib/parse'); var enoent = require('./lib/enoent'); var cpSpawnSync = cp.spawnSync; function spawn(command, args, options) { var parsed; var spawned; // Parse the arguments parsed = parse(command, args, options); // Spawn the child process spawned = cp.spawn(parsed.command, parsed.args, parsed.options); // Hook into child process "exit" event to emit an error if the command // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 enoent.hookChildProcess(spawned, parsed); return spawned; } function spawnSync(command, args, options) { var parsed; var result; if (!cpSpawnSync) { try { cpSpawnSync = require('spawn-sync'); // eslint-disable-line global-require } catch (ex) { throw new Error( 'In order to use spawnSync on node 0.10 or older, you must ' + 'install spawn-sync:\n\n' + ' npm install spawn-sync --save' ); } } // Parse the arguments parsed = parse(command, args, options); // Spawn the child process result = cpSpawnSync(parsed.command, parsed.args, parsed.options); // Analyze if the command does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16 result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); return result; } module.exports = spawn; module.exports.spawn = spawn; module.exports.sync = spawnSync; module.exports._parse = parse; module.exports._enoent = enoent; node-cross-spawn-5.1.0/lib/000077500000000000000000000000001305462024400154655ustar00rootroot00000000000000node-cross-spawn-5.1.0/lib/enoent.js000066400000000000000000000036301305462024400173150ustar00rootroot00000000000000'use strict'; var isWin = process.platform === 'win32'; var resolveCommand = require('./util/resolveCommand'); var isNode10 = process.version.indexOf('v0.10.') === 0; function notFoundError(command, syscall) { var err; err = new Error(syscall + ' ' + command + ' ENOENT'); err.code = err.errno = 'ENOENT'; err.syscall = syscall + ' ' + command; return err; } function hookChildProcess(cp, parsed) { var originalEmit; if (!isWin) { return; } originalEmit = cp.emit; cp.emit = function (name, arg1) { var err; // If emitting "exit" event and exit code is 1, we need to check if // the command exists and emit an "error" instead // See: https://github.com/IndigoUnited/node-cross-spawn/issues/16 if (name === 'exit') { err = verifyENOENT(arg1, parsed, 'spawn'); if (err) { return originalEmit.call(cp, 'error', err); } } return originalEmit.apply(cp, arguments); }; } function verifyENOENT(status, parsed) { if (isWin && status === 1 && !parsed.file) { return notFoundError(parsed.original, 'spawn'); } return null; } function verifyENOENTSync(status, parsed) { if (isWin && status === 1 && !parsed.file) { return notFoundError(parsed.original, 'spawnSync'); } // If we are in node 10, then we are using spawn-sync; if it exited // with -1 it probably means that the command does not exist if (isNode10 && status === -1) { parsed.file = isWin ? parsed.file : resolveCommand(parsed.original); if (!parsed.file) { return notFoundError(parsed.original, 'spawnSync'); } } return null; } module.exports.hookChildProcess = hookChildProcess; module.exports.verifyENOENT = verifyENOENT; module.exports.verifyENOENTSync = verifyENOENTSync; module.exports.notFoundError = notFoundError; node-cross-spawn-5.1.0/lib/parse.js000066400000000000000000000074611305462024400171450ustar00rootroot00000000000000'use strict'; var resolveCommand = require('./util/resolveCommand'); var hasEmptyArgumentBug = require('./util/hasEmptyArgumentBug'); var escapeArgument = require('./util/escapeArgument'); var escapeCommand = require('./util/escapeCommand'); var readShebang = require('./util/readShebang'); var isWin = process.platform === 'win32'; var skipShellRegExp = /\.(?:com|exe)$/i; // Supported in Node >= 6 and >= 4.8 var supportsShellOption = parseInt(process.version.substr(1).split('.')[0], 10) >= 6 || parseInt(process.version.substr(1).split('.')[0], 10) === 4 && parseInt(process.version.substr(1).split('.')[1], 10) >= 8; function parseNonShell(parsed) { var shebang; var needsShell; var applyQuotes; if (!isWin) { return parsed; } // Detect & add support for shebangs parsed.file = resolveCommand(parsed.command); parsed.file = parsed.file || resolveCommand(parsed.command, true); shebang = parsed.file && readShebang(parsed.file); if (shebang) { parsed.args.unshift(parsed.file); parsed.command = shebang; needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(resolveCommand(shebang) || resolveCommand(shebang, true)); } else { needsShell = hasEmptyArgumentBug || !skipShellRegExp.test(parsed.file); } // If a shell is required, use cmd.exe and take care of escaping everything correctly if (needsShell) { // Escape command & arguments applyQuotes = (parsed.command !== 'echo'); // Do not quote arguments for the special "echo" command parsed.command = escapeCommand(parsed.command); parsed.args = parsed.args.map(function (arg) { return escapeArgument(arg, applyQuotes); }); // Make use of cmd.exe parsed.args = ['/d', '/s', '/c', '"' + parsed.command + (parsed.args.length ? ' ' + parsed.args.join(' ') : '') + '"']; parsed.command = process.env.comspec || 'cmd.exe'; parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped } return parsed; } function parseShell(parsed) { var shellCommand; // If node supports the shell option, there's no need to mimic its behavior if (supportsShellOption) { return parsed; } // Mimic node shell option, see: https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335 shellCommand = [parsed.command].concat(parsed.args).join(' '); if (isWin) { parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe'; parsed.args = ['/d', '/s', '/c', '"' + shellCommand + '"']; parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped } else { if (typeof parsed.options.shell === 'string') { parsed.command = parsed.options.shell; } else if (process.platform === 'android') { parsed.command = '/system/bin/sh'; } else { parsed.command = '/bin/sh'; } parsed.args = ['-c', shellCommand]; } return parsed; } // ------------------------------------------------ function parse(command, args, options) { var parsed; // Normalize arguments, similar to nodejs if (args && !Array.isArray(args)) { options = args; args = null; } args = args ? args.slice(0) : []; // Clone array to avoid changing the original options = options || {}; // Build our parsed object parsed = { command: command, args: args, options: options, file: undefined, original: command, }; // Delegate further parsing to shell or non-shell return options.shell ? parseShell(parsed) : parseNonShell(parsed); } module.exports = parse; node-cross-spawn-5.1.0/lib/util/000077500000000000000000000000001305462024400164425ustar00rootroot00000000000000node-cross-spawn-5.1.0/lib/util/escapeArgument.js000066400000000000000000000015531305462024400217470ustar00rootroot00000000000000'use strict'; function escapeArgument(arg, quote) { // Convert to string arg = '' + arg; // If we are not going to quote the argument, // escape shell metacharacters, including double and single quotes: if (!quote) { arg = arg.replace(/([()%!^<>&|;,"'\s])/g, '^$1'); } else { // Sequence of backslashes followed by a double quote: // double up all the backslashes and escape the double quote arg = arg.replace(/(\\*)"/g, '$1$1\\"'); // Sequence of backslashes followed by the end of the string // (which will become a double quote later): // double up all the backslashes arg = arg.replace(/(\\*)$/, '$1$1'); // All other backslashes occur literally // Quote the whole thing: arg = '"' + arg + '"'; } return arg; } module.exports = escapeArgument; node-cross-spawn-5.1.0/lib/util/escapeCommand.js000066400000000000000000000006071305462024400215420ustar00rootroot00000000000000'use strict'; var escapeArgument = require('./escapeArgument'); function escapeCommand(command) { // Do not escape if this command is not dangerous.. // We do this so that commands like "echo" or "ifconfig" work // Quoting them, will make them unaccessible return /^[a-z0-9_-]+$/i.test(command) ? command : escapeArgument(command, true); } module.exports = escapeCommand; node-cross-spawn-5.1.0/lib/util/hasEmptyArgumentBug.js000066400000000000000000000006571305462024400227430ustar00rootroot00000000000000'use strict'; // See: https://github.com/IndigoUnited/node-cross-spawn/pull/34#issuecomment-221623455 function hasEmptyArgumentBug() { var nodeVer; if (process.platform !== 'win32') { return false; } nodeVer = process.version.substr(1).split('.').map(function (num) { return parseInt(num, 10); }); return (nodeVer[0] === 0 && nodeVer[1] < 12); } module.exports = hasEmptyArgumentBug(); node-cross-spawn-5.1.0/lib/util/readShebang.js000066400000000000000000000015761305462024400212140ustar00rootroot00000000000000'use strict'; var fs = require('fs'); var LRU = require('lru-cache'); var shebangCommand = require('shebang-command'); var shebangCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec function readShebang(command) { var buffer; var fd; var shebang; // Check if it is in the cache first if (shebangCache.has(command)) { return shebangCache.get(command); } // Read the first 150 bytes from the file buffer = new Buffer(150); try { fd = fs.openSync(command, 'r'); fs.readSync(fd, buffer, 0, 150, 0); fs.closeSync(fd); } catch (e) { /* empty */ } // Attempt to extract shebang (null is returned if not a shebang) shebang = shebangCommand(buffer.toString()); // Store the shebang in the cache shebangCache.set(command, shebang); return shebang; } module.exports = readShebang; node-cross-spawn-5.1.0/lib/util/resolveCommand.js000066400000000000000000000014451305462024400217620ustar00rootroot00000000000000'use strict'; var path = require('path'); var which = require('which'); var LRU = require('lru-cache'); var commandCache = new LRU({ max: 50, maxAge: 30 * 1000 }); // Cache just for 30sec function resolveCommand(command, noExtension) { var resolved; noExtension = !!noExtension; resolved = commandCache.get(command + '!' + noExtension); // Check if its resolved in the cache if (commandCache.has(command)) { return commandCache.get(command); } try { resolved = !noExtension ? which.sync(command) : which.sync(command, { pathExt: path.delimiter + (process.env.PATHEXT || '') }); } catch (e) { /* empty */ } commandCache.set(command + '!' + noExtension, resolved); return resolved; } module.exports = resolveCommand; node-cross-spawn-5.1.0/package.json000066400000000000000000000022231305462024400172040ustar00rootroot00000000000000{ "name": "cross-spawn", "version": "5.1.0", "description": "Cross platform child_process#spawn and child_process#spawnSync", "main": "index.js", "scripts": { "test": "node test/prepare && mocha --bail test/test", "lint": "eslint '{*.js,lib/**/*.js,test/**/*.js}'" }, "bugs": { "url": "https://github.com/IndigoUnited/node-cross-spawn/issues/" }, "repository": { "type": "git", "url": "git://github.com/IndigoUnited/node-cross-spawn.git" }, "files": [ "index.js", "lib" ], "keywords": [ "spawn", "spawnSync", "windows", "cross", "platform", "path", "ext", "path-ext", "path_ext", "shebang", "hashbang", "cmd", "execute" ], "author": "IndigoUnited (http://indigounited.com)", "license": "MIT", "dependencies": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", "which": "^1.2.9" }, "devDependencies": { "@satazor/eslint-config": "^3.0.0", "eslint": "^3.0.0", "expect.js": "^0.3.0", "glob": "^7.0.0", "mkdirp": "^0.5.1", "mocha": "^3.0.2", "once": "^1.4.0", "rimraf": "^2.5.0" } } node-cross-spawn-5.1.0/test/000077500000000000000000000000001305462024400156765ustar00rootroot00000000000000node-cross-spawn-5.1.0/test/.eslintrc000066400000000000000000000002401305462024400175160ustar00rootroot00000000000000{ "env": { "mocha": true }, "rules": { "no-invalid-this": 0, "max-nested-callbacks": 0, "callback-return": 0 } }node-cross-spawn-5.1.0/test/fixtures/000077500000000000000000000000001305462024400175475ustar00rootroot00000000000000node-cross-spawn-5.1.0/test/fixtures/()%!^&;, .bat000077500000000000000000000000271305462024400211620ustar00rootroot00000000000000@echo off echo special node-cross-spawn-5.1.0/test/fixtures/bar space000077500000000000000000000000261305462024400213130ustar00rootroot00000000000000#!/bin/bash echo bar node-cross-spawn-5.1.0/test/fixtures/bar space.bat000077500000000000000000000000231305462024400220550ustar00rootroot00000000000000@echo off echo bar node-cross-spawn-5.1.0/test/fixtures/echo.js000077500000000000000000000002421305462024400210240ustar00rootroot00000000000000'use strict'; var args = process.argv.slice(2); args.forEach(function (arg, index) { process.stdout.write(arg + (index < args.length - 1 ? '\n' : '')); }); node-cross-spawn-5.1.0/test/fixtures/exit.js000077500000000000000000000000411305462024400210540ustar00rootroot00000000000000'use strict'; process.exit(25); node-cross-spawn-5.1.0/test/fixtures/exit1000077500000000000000000000000241305462024400205230ustar00rootroot00000000000000#!/bin/bash exit 1 node-cross-spawn-5.1.0/test/fixtures/exit1.bat000077500000000000000000000000071305462024400212710ustar00rootroot00000000000000exit 1 node-cross-spawn-5.1.0/test/fixtures/foo000077500000000000000000000000261305462024400202560ustar00rootroot00000000000000#!/bin/bash echo foo node-cross-spawn-5.1.0/test/fixtures/foo.bat000077500000000000000000000000231305462024400210200ustar00rootroot00000000000000@echo off echo foo node-cross-spawn-5.1.0/test/fixtures/prepare_()%!^&;, .sh000077500000000000000000000000321305462024400225400ustar00rootroot00000000000000#!/bin/bash echo special node-cross-spawn-5.1.0/test/fixtures/shebang000077500000000000000000000001141305462024400211000ustar00rootroot00000000000000#!/usr/bin/env node 'use strict'; process.stdout.write('shebang works!'); node-cross-spawn-5.1.0/test/fixtures/shebang_enoent000077500000000000000000000000671305462024400224570ustar00rootroot00000000000000#!/usr/bin/env somecommandthatwillneverexist echo foo node-cross-spawn-5.1.0/test/fixtures/win-ppid.js000066400000000000000000000005611305462024400216360ustar00rootroot00000000000000#!/usr/bin/env node var spawnSync = require('child_process').spawnSync || require('spawn-sync'); function ppidSync() { var res = spawnSync('wmic', ['process', 'where', '(processid=' + process.pid + ')', 'get', 'parentprocessid']); var lines = res.stdout.toString().split(/\r?\n/); return parseInt(lines[1].trim(), 10); } console.log(ppidSync()); node-cross-spawn-5.1.0/test/prepare.js000066400000000000000000000023711305462024400176750ustar00rootroot00000000000000'use strict'; var glob = require('glob'); var fs = require('fs'); var path = require('path'); var buffered = require('./util/buffered'); // Preare fixtures var fixturesDir = __dirname + '/fixtures'; glob.sync('prepare_*', { cwd: __dirname + '/fixtures' }).forEach(function (file) { var contents = fs.readFileSync(fixturesDir + '/' + file); var finalFile = file.replace(/^prepare_/, '').replace(/\.sh$/, ''); fs.writeFileSync(fixturesDir + '/' + finalFile, contents); fs.chmodSync(fixturesDir + '/' + finalFile, parseInt('0777', 8)); process.stdout.write('Copied "' + file + '" to "' + finalFile + '"\n'); }); // Install spawn-sync for older node versions if (/^v0\.10\./.test(process.version)) { process.stdout.write('Installing spawn-sync..\n'); buffered('spawn', 'npm', ['install', 'spawn-sync'], { stdio: 'inherit' }, function (err) { if (err) { throw err; } process.exit(); }); } // Fix AppVeyor tests because Git bin folder is in PATH and it has a "echo" program there if (process.env.APPVEYOR) { process.env.PATH = process.env.PATH .split(path.delimiter) .filter(function (entry) { return !/\\git\\bin$/i.test(path.normalize(entry)); }) .join(path.delimiter); } node-cross-spawn-5.1.0/test/test.js000066400000000000000000000507631305462024400172260ustar00rootroot00000000000000'use strict'; var fs = require('fs'); var path = require('path'); var expect = require('expect.js'); var rimraf = require('rimraf'); var mkdirp = require('mkdirp'); var hasEmptyArgumentBug = require('../lib/util/hasEmptyArgumentBug'); var spawn = require('../'); var buffered = require('./util/buffered'); var isWin = process.platform === 'win32'; describe('cross-spawn', function () { var methods = ['spawn', 'sync']; methods.forEach(function (method) { describe(method, function () { var originalPath = process.env.PATH; before(function () { mkdirp.sync(__dirname + '/tmp'); }); after(function (next) { // Give it some time, RIMRAF was giving problems on windows this.timeout(10000); rimraf(__dirname + '/tmp', function () { // Ignore errors, RIMRAF was giving problems on windows next(null); }); }); afterEach(function () { process.env.PATH = originalPath; }); it('should support shebang in executables with /usr/bin/env', function (next) { buffered(method, __dirname + '/fixtures/shebang', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang works!'); // Test if the actual shebang file is resolved against the PATH process.env.PATH = path.normalize(__dirname + '/fixtures/') + path.delimiter + process.env.PATH; buffered(method, 'shebang', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang works!'); next(); }); }); }); it('should support shebang in executables with relative path', function (next) { var executable = './' + path.relative(process.cwd(), __dirname + '/fixtures/shebang'); fs.writeFileSync(__dirname + '/tmp/shebang', '#!/usr/bin/env node\n\nprocess.stdout.write(\'yeah\');', { mode: parseInt('0777', 8) }); process.env.PATH = path.normalize(__dirname + '/tmp/') + path.delimiter + process.env.PATH; buffered(method, executable, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang works!'); next(); }); }); it('should support shebang in executables with relative path that starts with `..`', function (next) { var executable = '../' + path.basename(process.cwd()) + '/' + path.relative(process.cwd(), __dirname + '/fixtures/shebang'); fs.writeFileSync(__dirname + '/tmp/shebang', '#!/usr/bin/env node\n\nprocess.stdout.write(\'yeah\');', { mode: parseInt('0777', 8) }); process.env.PATH = path.normalize(__dirname + '/tmp/') + path.delimiter + process.env.PATH; buffered(method, executable, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang works!'); next(); }); }); it('should support shebang in executables with extensions', function (next) { fs.writeFileSync(__dirname + '/tmp/shebang_' + method + '.js', '#!/usr/bin/env node\n\nprocess.stdout.write(\'shebang with \ extension\');', { mode: parseInt('0777', 8) }); process.env.PATH = path.normalize(__dirname + '/tmp/') + path.delimiter + process.env.PATH; buffered(method, __dirname + '/tmp/shebang_' + method + '.js', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang with extension'); // Test if the actual shebang file is resolved against the PATH process.env.PATH = path.normalize(__dirname + '/fixtures/') + path.delimiter + process.env.PATH; buffered(method, 'shebang_' + method + '.js', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('shebang with extension'); next(); }); }); }); it('should expand using PATHEXT properly', function (next) { buffered(method, __dirname + '/fixtures/foo', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo'); next(); }); }); it('should handle commands with spaces', function (next) { buffered(method, __dirname + '/fixtures/bar space', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('bar'); next(); }); }); it('should handle commands with special shell chars', function (next) { buffered(method, __dirname + '/fixtures/()%!^&;, ', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('special'); next(); }); }); it('should handle arguments with quotes', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', '"foo"', 'foo"bar"foo', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('"foo"\nfoo"bar"foo'); next(); }); }); it('should handle empty arguments', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 'foo', '', 'bar', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('foo\n\nbar'); buffered(method, 'echo', [ 'foo', '', 'bar', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo bar'); next(); }); }); }); it('should handle non-string arguments', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 1234, ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('1234'); next(); }); }); it('should handle arguments with spaces', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 'I am', 'André Cruz', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('I am\nAndré Cruz'); next(); }); }); it('should handle arguments with \\"', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 'foo', '\\"', 'bar', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('foo\n\\"\nbar'); next(); }); }); it('should handle arguments that end with \\', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 'foo', 'bar\\', 'baz', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('foo\nbar\\\nbaz'); next(); }); }); it('should handle arguments that contain shell special chars', function (next) { buffered(method, 'node', [ __dirname + '/fixtures/echo', 'foo', '()', 'foo', '%!', 'foo', '^<', 'foo', '>&', 'foo', '|;', 'foo', ', ', 'foo', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data).to.equal('foo\n()\nfoo\n%!\nfoo\n^<\nfoo\n>&\nfoo\n|;\nfoo\n, \nfoo'); next(); }); }); it('should handle special arguments when using echo', function (next) { buffered(method, 'echo', ['foo\\"foo\\foo&bar"foo\'bar'], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo\\"foo\\foo&bar"foo\'bar'); buffered(method, 'echo', [ 'foo', '()', 'foo', '%!', 'foo', '^<', 'foo', '>&', 'foo', '|;', 'foo', ', ', 'foo', ], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo () foo %! foo ^< foo >& foo |; foo , foo'); next(); }); }); }); it('should handle optional args correctly', function (next) { buffered(method, __dirname + '/fixtures/foo', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); buffered(method, __dirname + '/fixtures/foo', { stdio: ['pipe', 'pipe', 'pipe'], }, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); buffered(method, __dirname + '/fixtures/foo', null, { stdio: ['pipe', 'pipe', 'pipe'], }, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); next(); }); }); }); }); it('should not mutate args nor options', function (next) { var args = []; var options = {}; buffered(method, __dirname + '/fixtures/foo', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(args).to.have.length(0); expect(Object.keys(options)).to.have.length(0); next(); }); }); it('should give correct exit code', function (next) { buffered(method, 'node', [__dirname + '/fixtures/exit'], function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(25); next(); }); }); it('should work with a relative command', function (next) { buffered(method, path.relative(process.cwd(), __dirname + '/fixtures/foo'), function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo'); if (!isWin) { return next(); } buffered(method, path.relative(process.cwd(), __dirname + '/fixtures/foo.bat'), function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('foo'); next(); }); }); }); it('should emit "error" and "close" if command does not exist', function (next) { var errors; var spawned = spawn[method]('somecommandthatwillneverexist'); this.timeout(5000); function assertError(err) { var syscall = method === 'sync' ? 'spawnSync' : 'spawn'; expect(err).to.be.an(Error); expect(err.message).to.contain(syscall); expect(err.message).to.contain('ENOENT'); expect(err.message).to.not.contain('undefined'); expect(err.code).to.be('ENOENT'); expect(err.errno).to.be('ENOENT'); expect(err.syscall).to.contain(syscall); expect(err.syscall).to.not.contain('undefined'); } if (method === 'spawn') { errors = []; spawned .on('error', function (err) { errors.push(err); }) .on('exit', function () { spawned.removeAllListeners(); next(new Error('Should not emit exit')); }) .on('close', function (code, signal) { expect(code).to.not.be(0); expect(signal).to.be(null); setTimeout(function () { expect(errors).to.have.length(1); assertError(errors[0]); next(); }, 1000); }); } else { assertError(spawned.error); next(); } }); it('should NOT emit "error" if shebang command does not exist', function (next) { var spawned = spawn[method](__dirname + '/fixtures/shebang_enoent'); var exited; var timeout; this.timeout(5000); if (method === 'spawn') { spawned .on('error', function () { spawned.removeAllListeners(); clearTimeout(timeout); next(new Error('Should not emit error')); }) .on('exit', function () { exited = true; }) .on('close', function (code, signal) { expect(code).to.not.be(0); expect(signal).to.be(null); expect(exited).to.be(true); timeout = setTimeout(next, 1000); }); } else { expect(spawned.error).to.not.be.ok(); next(); } }); it('should NOT emit "error" if the command actual exists but exited with 1', function (next) { var spawned = spawn[method](__dirname + '/fixtures/exit1'); var exited; var timeout; this.timeout(5000); if (method === 'spawn') { spawned .on('error', function () { spawned.removeAllListeners(); clearTimeout(timeout); next(new Error('Should not emit error')); }) .on('exit', function () { exited = true; }) .on('close', function (code, signal) { expect(code).to.not.be(0); expect(signal).to.be(null); expect(exited).to.be(true); timeout = setTimeout(next, 1000); }); } else { expect(spawned.error).to.not.be.ok(); next(); } }); if (isWin) { it('should use nodejs\' spawn when option.shell is specified', function (next) { buffered(method, 'echo', ['%RANDOM%'], { shell: true }, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.match(/\d+/); buffered(method, 'echo', ['%RANDOM%'], { shell: false }, function (err, data) { // In some windows versions, the echo exists outside the shell as echo.exe so we must account for that here if (err) { expect(err).to.be.an(Error); expect(err.message).to.contain('ENOENT'); } else { expect(data.trim()).to.equal('%RANDOM%'); } next(); }); }); }); } else { it('should use nodejs\' spawn when option.shell is specified', function (next) { buffered(method, 'echo', ['hello &&', 'echo there'], { shell: true }, function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('hello\nthere'); buffered(method, 'echo', ['hello &&', 'echo there'], { shell: false }, function (err, data) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('hello && echo there'); next(); }); }); }); } if (isWin) { if (hasEmptyArgumentBug) { it('should spawn a shell for a .exe on old Node', function (next) { buffered(method, __dirname + '/fixtures/win-ppid.js', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.not.equal('' + process.pid); next(); }); }); } else { it('should NOT spawn a shell for a .exe', function (next) { buffered(method, __dirname + '/fixtures/win-ppid.js', function (err, data, code) { expect(err).to.not.be.ok(); expect(code).to.be(0); expect(data.trim()).to.equal('' + process.pid); next(); }); }); } } }); }); }); node-cross-spawn-5.1.0/test/util/000077500000000000000000000000001305462024400166535ustar00rootroot00000000000000node-cross-spawn-5.1.0/test/util/buffered.js000066400000000000000000000023271305462024400207770ustar00rootroot00000000000000'use strict'; var spawn = require('../../index'); var once = require('once'); function buffered(method, command, args, options, callback) { var cp; var stdout; var stderr; var results; if (typeof options === 'function') { callback = options; options = null; } if (typeof args === 'function') { callback = args; args = options = null; } if (method === 'sync') { results = spawn.sync(command, args, options); callback(results.error, results.stdout ? results.stdout.toString() : null, results.status); } else { cp = spawn(command, args, options); stdout = stderr = null; callback = once(callback); cp.stdout && cp.stdout.on('data', function (buffer) { stdout = stdout || ''; stdout += buffer.toString(); }); cp.stderr && cp.stderr.on('data', function (buffer) { stderr = stderr || ''; stderr += buffer.toString(); }); cp.on('error', callback); cp.on('close', function (code) { code !== 0 && stderr && console.warn(stderr); callback(null, stdout, code); }); } } module.exports = buffered;