diff options
Diffstat (limited to 'node_modules/spawn-wrap')
51 files changed, 3145 insertions, 0 deletions
diff --git a/node_modules/spawn-wrap/.npmignore b/node_modules/spawn-wrap/.npmignore new file mode 100644 index 000000000..07e6e472c --- /dev/null +++ b/node_modules/spawn-wrap/.npmignore @@ -0,0 +1 @@ +/node_modules diff --git a/node_modules/spawn-wrap/.travis.yml b/node_modules/spawn-wrap/.travis.yml new file mode 100644 index 000000000..280a6462f --- /dev/null +++ b/node_modules/spawn-wrap/.travis.yml @@ -0,0 +1,7 @@ +sudo: false +language: node_js +node_js: + - '0.12' + - '4' + - '5' + - '6' diff --git a/node_modules/spawn-wrap/LICENSE b/node_modules/spawn-wrap/LICENSE new file mode 100644 index 000000000..19129e315 --- /dev/null +++ b/node_modules/spawn-wrap/LICENSE @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/spawn-wrap/README.md b/node_modules/spawn-wrap/README.md new file mode 100644 index 000000000..0cac17fa5 --- /dev/null +++ b/node_modules/spawn-wrap/README.md @@ -0,0 +1,97 @@ +# spawn-wrap + +Wrap all spawned Node.js child processes by adding environs and +arguments ahead of the main JavaScript file argument. + +Any child processes launched by that child process will also be +wrapped in a similar fashion. + +This is a bit of a brutal hack, designed primarily to support code +coverage reporting in cases where tests or the system under test are +loaded via child processes rather than via `require()`. + +It can also be handy if you want to run your own mock executable +instead of some other thing when child procs call into it. + +[![Build Status](https://travis-ci.org/tapjs/spawn-wrap.svg)](https://travis-ci.org/tapjs/spawn-wrap) [![Build status](https://ci.appveyor.com/api/projects/status/oea7gdvqa0qeijrm?svg=true)](https://ci.appveyor.com/project/isaacs/spawn-wrap) + +## USAGE + +```javascript +var wrap = require('spawn-wrap') + +// wrap(wrapperArgs, environs) +var unwrap = wrap(['/path/to/my/main.js', 'foo=bar'], { FOO: 1 }) + +// later to undo the wrapping, you can call the returned function +unwrap() +``` + +In this example, the `/path/to/my/main.js` file will be used as the +"main" module, whenever any Node or io.js child process is started, +whether via a call to `spawn` or `exec`, whether node is invoked +directly as the command or as the result of a shebang `#!` lookup. + +In `/path/to/my/main.js`, you can do whatever instrumentation or +environment manipulation you like. When you're done, and ready to run +the "real" main.js file (ie, the one that was spawned in the first +place), you can do this: + +```javascript +// /path/to/my/main.js +// process.argv[1] === 'foo=bar' +// and process.env.FOO === '1' + +// my wrapping manipulations +setupInstrumentationOrCoverageOrWhatever() +process.on('exit', function (code) { + storeCoverageInfoSynchronously() +}) + +// now run the instrumented and covered or whatever codes +require('spawn-wrap').runMain() +``` + +## CONTRACTS and CAVEATS + +The initial wrap call uses synchronous I/O. Probably you should not +be using this script in any production environments anyway. + +Also, this will slow down child process execution by a lot, since +we're adding a few layers of indirection. + +The contract which this library aims to uphold is: + +* Wrapped processes behave identical to their unwrapped counterparts + for all intents and purposes. That means that the wrapper script + propagates all signals and exit codes. +* If you send a signal to the wrapper, the child gets the signal. +* If the child exits with a numeric status code, then the wrapper + exits with that code. +* If the child dies with a signal, then the wrapper dies with the + same signal. +* If you execute any Node child process, in any of the various ways + that such a thing can be done, it will be wrapped. +* Children of wrapped processes are also wrapped. + +(Much of this made possible by +[foreground-child](http://npm.im/foreground-child).) + +There are a few ways situations in which this contract cannot be +adhered to, despite best efforts: + +1. In order to handle cases where `node` is invoked in a shell script, + the `PATH` environment variable is modified such that the the shim + will be run before the "real" node. However, since Windows does + not allow executing shebang scripts like regular programs, a + `node.cmd` file is required. +2. Signal propagation through `dash` doesn't always work. So, if you + use `child_process.exec()` on systems where `/bin/sh` is actually + `dash`, then the process may exit with a status code > 128 rather + than indicating that it received a signal. +3. `cmd.exe` is even stranger with how it propagates and interprets + unix signals. If you want your programs to be portable, then + probably you wanna not rely on signals too much. +4. It *is* possible to escape the wrapping, if you spawn a bash + script, and that script modifies the `PATH`, and then calls a + specific `node` binary explicitly. diff --git a/node_modules/spawn-wrap/appveyor.yml b/node_modules/spawn-wrap/appveyor.yml new file mode 100644 index 000000000..9c2a3bc82 --- /dev/null +++ b/node_modules/spawn-wrap/appveyor.yml @@ -0,0 +1,20 @@ +environment: + matrix: + - nodejs_version: '6' + - nodejs_version: '5' + - nodejs_version: '4' + - nodejs_version: '0.12' +install: + - ps: Install-Product node $env:nodejs_version + - set CI=true + - npm -g install npm@latest + - set PATH=%APPDATA%\npm;%PATH% + - npm install +matrix: + fast_finish: true +build: off +version: '{build}' +shallow_clone: true +clone_depth: 1 +test_script: + - npm test diff --git a/node_modules/spawn-wrap/index.js b/node_modules/spawn-wrap/index.js new file mode 100644 index 000000000..a25cedb10 --- /dev/null +++ b/node_modules/spawn-wrap/index.js @@ -0,0 +1,326 @@ +module.exports = wrap +wrap.runMain = runMain + +var Module = require('module') +var fs = require('fs') +var cp = require('child_process') +var ChildProcess = cp.ChildProcess +var assert = require('assert') +var crypto = require('crypto') +var mkdirp = require('mkdirp') +var rimraf = require('rimraf') +var path = require('path') +var signalExit = require('signal-exit') +var homedir = require('os-homedir')() + '/.node-spawn-wrap-' +var winRebase = require('./lib/win-rebase') +var which = require('which') + +var cmdname = path.basename(process.execPath, '.exe') + +var shim = '#!' + process.execPath + '\n' + + fs.readFileSync(__dirname + '/shim.js') + +var isWindows = require('./lib/is-windows')() + +var pathRe = /^PATH=/ +if (isWindows) pathRe = /^PATH=/i + +var colon = isWindows ? ';' : ':' + +function wrap (argv, env, workingDir) { + if (!ChildProcess) { + var child = cp.spawn(process.execPath, []) + ChildProcess = child.constructor + child.kill('SIGKILL') + } + + // spawn_sync available since Node v0.11 + var spawnSyncBinding, spawnSync + try { + spawnSyncBinding = process.binding('spawn_sync') + } catch (e) {} + + // if we're passed in the working dir, then it means that setup + // was already done, so no need. + var doSetup = !workingDir + if (doSetup) { + workingDir = setup(argv, env) + } + var spawn = ChildProcess.prototype.spawn + if (spawnSyncBinding) { + spawnSync = spawnSyncBinding.spawn + } + + function unwrap () { + if (doSetup) { + rimraf.sync(workingDir) + } + ChildProcess.prototype.spawn = spawn + if (spawnSyncBinding) { + spawnSync = spawnSyncBinding.spawn + } + } + + if (spawnSyncBinding) { + spawnSyncBinding.spawn = wrappedSpawnFunction(spawnSync, workingDir) + } + ChildProcess.prototype.spawn = wrappedSpawnFunction(spawn, workingDir) + + return unwrap +} + +function wrappedSpawnFunction (fn, workingDir) { + return wrappedSpawn + + function wrappedSpawn (options) { + var pathEnv + var cmdi, c, re, match, exe + + // handle case where node/iojs is exec'd + // this doesn't handle EVERYTHING, but just the most common + // case of doing `exec(process.execPath + ' file.js') + var file = path.basename(options.file, '.exe') + if (file === 'dash' || + file === 'sh' || + file === 'bash' || + file === 'zsh') { + cmdi = options.args.indexOf('-c') + if (cmdi !== -1) { + c = options.args[cmdi + 1] + re = /^\s*((?:[^\= ]*\=[^\=\s]*\s*)*)([^\s]+|"[^"]+"|'[^']+')/ + match = c.match(re) + if (match) { + exe = path.basename(match[2]) + // Strip a trailing quote from the basename if it matches a + // leading quote. + var quote = match[2].charAt(0) + if ((quote === '"' || quote === '\'') && quote === exe.slice(-1)) { + exe = exe.slice(0, -1) + } + + if (exe === 'iojs' || + exe === 'node' || + exe === cmdname) { + c = c.replace(re, '$1 ' + workingDir + '/node') + options.args[cmdi + 1] = c + } else if (exe === 'npm' && !isWindows) { + var npmPath = whichOrUndefined('npm') + + if (npmPath) { + c = c.replace(re, '$1 ' + workingDir + '/node ' + npmPath) + options.args[cmdi + 1] = c + } + } + } + } + } else if (isWindows && ( + file === path.basename(process.env.comspec || '') || + file === 'cmd' + )) { + cmdi = options.args.indexOf('/c') + if (cmdi !== -1) { + options.args[cmdi + 1] = winRebase(options.args[cmdi + 1], + workingDir + '/node.cmd', + whichOrUndefined) + } + } else if (file === 'node' || file === 'iojs' || cmdname === file) { + // make sure it has a main script. + // otherwise, just let it through. + var a = 0 + var hasMain = false + for (var a = 1; !hasMain && a < options.args.length; a++) { + switch (options.args[a]) { + case '-i': + case '--interactive': + case '--eval': + case '-e': + case '-pe': + hasMain = false + a = options.args.length + continue + + case '-r': + case '--require': + a += 1 + continue + + default: + if (options.args[a].match(/^-/)) { + continue + } else { + hasMain = true + a = options.args.length + break + } + } + } + + if (hasMain) { + options.file = workingDir + '/' + file + options.args[0] = workingDir + '/' + file + } + + } else { + try { + var resolved = which.sync(options.file) + } catch (er) {} + if (resolved) { + var shebang = fs.readFileSync(resolved, 'utf8') + var match = shebang.match(/^#!([^\r\n]+)/) + if (match) { + var shebangbin = match[1].split(' ')[0] + var maybeNode = path.basename(shebangbin) + if (maybeNode === 'node' || maybeNode === 'iojs' || cmdname === maybeNode) { + file = options.file = shebangbin + options.args = [shebangbin, workingDir + '/' + maybeNode] + .concat(resolved) + .concat(match[1].split(' ').slice(1)) + .concat(options.args.slice(1)) + } + } + } + } + + for (var i = 0; i < options.envPairs.length; i++) { + var ep = options.envPairs[i] + if (ep.match(pathRe)) { + pathEnv = ep.substr(5) + var k = ep.substr(0, 5) + options.envPairs[i] = k + workingDir + colon + pathEnv + } + } + if (!pathEnv) { + options.envPairs.push((isWindows ? 'Path=' : 'PATH=') + workingDir) + } + + if (file === 'npm' && !isWindows) { + var npmPath = whichOrUndefined('npm') + + if (npmPath) { + options.args[0] = npmPath + + options.file = workingDir + '/node' + options.args.unshift(workingDir + '/node') + } + } + + if (isWindows) fixWindowsBins(workingDir, options) + + return fn.call(this, options) + } +} + +function whichOrUndefined (executable) { + var path + try { + path = which.sync(executable) + } catch (er) {} + return path +} + +// by default Windows will reference the full +// path to the node.exe or iojs.exe as the bin, +// we should instead point spawn() at our .cmd shim. +function fixWindowsBins (workingDir, options) { + var re = /.*\b(node|iojs)(\.exe)?$/ + if (options.file.match(re)) { + options.file = process.execPath + var shim = workingDir + '/node' + options.args.splice(0, 1, options.file, workingDir + '/node') + } +} + +function setup (argv, env) { + if (argv && typeof argv === 'object' && !env && !Array.isArray(argv)) { + env = argv + argv = [] + } + + if (!argv && !env) { + throw new Error('at least one of "argv" and "env" required') + } + + if (argv) { + assert(Array.isArray(argv), 'argv must be array') + } else { + argv = [] + } + + if (env) { + assert(typeof env === 'object', 'env must be an object') + } else { + env = {} + } + + // For stuff like --use_strict or --harmony, we need to inject + // the argument *before* the wrap-main. + var execArgv = [] + for (var i = 0; i < argv.length; i++) { + if (argv[i].match(/^-/)) { + execArgv.push(argv[i]) + if (argv[i] === '-r' || argv[i] === '--require') { + execArgv.push(argv[++i]) + } + } else { + break + } + } + if (execArgv.length) { + if (execArgv.length === argv.length) { + argv.length = 0 + } else { + argv = argv.slice(execArgv.length) + } + } + + var settings = JSON.stringify({ + module: __filename, + deps: { + foregroundChild: require.resolve('foreground-child'), + signalExit: require.resolve('signal-exit'), + }, + argv: argv, + execArgv: execArgv, + env: env, + root: process.pid + }, null, 2) + '\n' + + var workingDir = homedir + process.pid + '-' + + crypto.randomBytes(6).toString('hex') + + signalExit(function () { + rimraf.sync(workingDir) + }) + + mkdirp.sync(workingDir) + workingDir = fs.realpathSync(workingDir) + if (isWindows) { + var cmdShim = + '@echo off\r\n' + + 'SETLOCAL\r\n' + + 'SET PATHEXT=%PATHEXT:;.JS;=;%\r\n' + + '"' + process.execPath + '"' + ' "%~dp0\\.\\node" %*\r\n' + + fs.writeFileSync(workingDir + '/node.cmd', cmdShim) + fs.chmodSync(workingDir + '/node.cmd', '0755') + fs.writeFileSync(workingDir + '/iojs.cmd', cmdShim) + fs.chmodSync(workingDir + '/iojs.cmd', '0755') + } + fs.writeFileSync(workingDir + '/node', shim) + fs.chmodSync(workingDir + '/node', '0755') + fs.writeFileSync(workingDir + '/iojs', shim) + fs.chmodSync(workingDir + '/iojs', '0755') + if (cmdname !== 'iojs' && cmdname !== 'node') { + fs.writeFileSync(workingDir + '/' + cmdname, shim) + fs.chmodSync(workingDir + '/' + cmdname, '0755') + } + fs.writeFileSync(workingDir + '/settings.json', settings) + + return workingDir +} + +function runMain () { + process.argv.splice(1, 1) + process.argv[1] = path.resolve(process.argv[1]) + Module.runMain() +} diff --git a/node_modules/spawn-wrap/lib/is-windows.js b/node_modules/spawn-wrap/lib/is-windows.js new file mode 100644 index 000000000..278abb3a0 --- /dev/null +++ b/node_modules/spawn-wrap/lib/is-windows.js @@ -0,0 +1,5 @@ +module.exports = function () { + return process.platform === 'win32' || + process.env.OSTYPE === 'cygwin' || + process.env.OSTYPE === 'msys' +} diff --git a/node_modules/spawn-wrap/lib/win-rebase.js b/node_modules/spawn-wrap/lib/win-rebase.js new file mode 100644 index 000000000..d4af5fed4 --- /dev/null +++ b/node_modules/spawn-wrap/lib/win-rebase.js @@ -0,0 +1,18 @@ +var re = /^\s*("*)([^"]*?\b(?:node|iojs)(?:\.exe)?)("*)( |$)/ +var npmre = /^\s*("*)([^"]*?\b(?:npm))("*)( |$)/ +var path_ = require('path') +if (path_.win32) path_ = path_.win32 + +module.exports = function (path, rebase, whichOrUndefined) { + var m = path.match(re) + if (!m) { + m = path.match(npmre) + if (!m) return path + var npmPath = whichOrUndefined('npm') || 'npm' + npmPath = path_.dirname(npmPath) + '\\node_modules\\npm\\bin\\npm-cli.js' + return path.replace(npmre, m[1] + rebase + ' "' + npmPath + '"' + m[3] + m[4]) + } + // preserve the quotes + var replace = m[1] + rebase + m[3] + m[4] + return path.replace(re, replace) +} diff --git a/node_modules/spawn-wrap/node_modules/.bin/mkdirp b/node_modules/spawn-wrap/node_modules/.bin/mkdirp new file mode 120000 index 000000000..91a5f623f --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/.bin/mkdirp @@ -0,0 +1 @@ +../../../mkdirp/bin/cmd.js
\ No newline at end of file diff --git a/node_modules/spawn-wrap/node_modules/.bin/rimraf b/node_modules/spawn-wrap/node_modules/.bin/rimraf new file mode 120000 index 000000000..632d6da23 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/.bin/rimraf @@ -0,0 +1 @@ +../../../rimraf/bin.js
\ No newline at end of file diff --git a/node_modules/spawn-wrap/node_modules/.bin/which b/node_modules/spawn-wrap/node_modules/.bin/which new file mode 120000 index 000000000..091d52ad6 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/.bin/which @@ -0,0 +1 @@ +../../../which/bin/which
\ No newline at end of file diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/.npmignore b/node_modules/spawn-wrap/node_modules/signal-exit/.npmignore new file mode 100644 index 000000000..28ffc9de2 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/.npmignore @@ -0,0 +1,4 @@ +node_modules +.DS_Store +nyc_output +coverage diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/.travis.yml b/node_modules/spawn-wrap/node_modules/signal-exit/.travis.yml new file mode 100644 index 000000000..9c9695baf --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/.travis.yml @@ -0,0 +1,7 @@ +sudo: false +language: node_js +node_js: +- '0.12' +- '0.10' +- iojs +after_success: npm run coverage diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/LICENSE.txt b/node_modules/spawn-wrap/node_modules/signal-exit/LICENSE.txt new file mode 100644 index 000000000..c7e27478a --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/LICENSE.txt @@ -0,0 +1,14 @@ +Copyright (c) 2015, Contributors + +Permission to use, copy, modify, and/or distribute this software +for any purpose with or without fee is hereby granted, provided +that the above copyright notice and this permission notice +appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE +LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/README.md b/node_modules/spawn-wrap/node_modules/signal-exit/README.md new file mode 100644 index 000000000..5c6dc3fb9 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/README.md @@ -0,0 +1,38 @@ +# signal-exit + +[![Build Status](https://travis-ci.org/bcoe/signal-exit.png)](https://travis-ci.org/bcoe/signal-exit) +[![Coverage Status](https://coveralls.io/repos/bcoe/signal-exit/badge.svg?branch=)](https://coveralls.io/r/bcoe/signal-exit?branch=) +[![NPM version](https://img.shields.io/npm/v/signal-exit.svg)](https://www.npmjs.com/package/signal-exit) + +When you want to fire an event no matter how a process exits: + +* reaching the end of execution. +* explicitly having `process.exit(code)` called. +* having `process.kill(pid, sig)` called. +* receiving a fatal signal from outside the process + +Use `signal-exit`. + +```js +var onExit = require('signal-exit') + +onExit(function (code, signal) { + console.log('process exited!') +}) +``` + +## API + +`var remove = onExit(function (code, signal) {}, options)` + +The return value of the function is a function that will remove the +handler. + +Note that the function *only* fires for signals if the signal would +cause the proces to exit. That is, there are no other listeners, and +it is a fatal signal. + +## Options + +* `alwaysLast`: Run this handler after any other signal or exit + handlers. This causes `process.emit` to be monkeypatched. diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/index.js b/node_modules/spawn-wrap/node_modules/signal-exit/index.js new file mode 100644 index 000000000..7dd8d917d --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/index.js @@ -0,0 +1,148 @@ +// Note: since nyc uses this module to output coverage, any lines +// that are in the direct sync flow of nyc's outputCoverage are +// ignored, since we can never get coverage for them. +var assert = require('assert') +var signals = require('./signals.js') + +var EE = require('events') +/* istanbul ignore if */ +if (typeof EE !== 'function') { + EE = EE.EventEmitter +} + +var emitter +if (process.__signal_exit_emitter__) { + emitter = process.__signal_exit_emitter__ +} else { + emitter = process.__signal_exit_emitter__ = new EE() + emitter.count = 0 + emitter.emitted = {} +} + +module.exports = function (cb, opts) { + assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler') + + if (loaded === false) { + load() + } + + var ev = 'exit' + if (opts && opts.alwaysLast) { + ev = 'afterexit' + } + + var remove = function () { + emitter.removeListener(ev, cb) + if (emitter.listeners('exit').length === 0 && + emitter.listeners('afterexit').length === 0) { + unload() + } + } + emitter.on(ev, cb) + + return remove +} + +module.exports.unload = unload +function unload () { + if (!loaded) { + return + } + loaded = false + + signals.forEach(function (sig) { + try { + process.removeListener(sig, sigListeners[sig]) + } catch (er) {} + }) + process.emit = originalProcessEmit + process.reallyExit = originalProcessReallyExit + emitter.count -= 1 +} + +function emit (event, code, signal) { + if (emitter.emitted[event]) { + return + } + emitter.emitted[event] = true + emitter.emit(event, code, signal) +} + +// { <signal>: <listener fn>, ... } +var sigListeners = {} +signals.forEach(function (sig) { + sigListeners[sig] = function listener () { + // If there are no other listeners, an exit is coming! + // Simplest way: remove us and then re-send the signal. + // We know that this will kill the process, so we can + // safely emit now. + var listeners = process.listeners(sig) + if (listeners.length === emitter.count) { + unload() + emit('exit', null, sig) + /* istanbul ignore next */ + emit('afterexit', null, sig) + /* istanbul ignore next */ + process.kill(process.pid, sig) + } + } +}) + +module.exports.signals = function () { + return signals +} + +module.exports.load = load + +var loaded = false + +function load () { + if (loaded) { + return + } + loaded = true + + // This is the number of onSignalExit's that are in play. + // It's important so that we can count the correct number of + // listeners on signals, and don't wait for the other one to + // handle it instead of us. + emitter.count += 1 + + signals = signals.filter(function (sig) { + try { + process.on(sig, sigListeners[sig]) + return true + } catch (er) { + return false + } + }) + + process.emit = processEmit + process.reallyExit = processReallyExit +} + +var originalProcessReallyExit = process.reallyExit +function processReallyExit (code) { + process.exitCode = code || 0 + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + /* istanbul ignore next */ + originalProcessReallyExit.call(process, process.exitCode) +} + +var originalProcessEmit = process.emit +function processEmit (ev, arg) { + if (ev === 'exit') { + if (arg !== undefined) { + process.exitCode = arg + } + var ret = originalProcessEmit.apply(this, arguments) + emit('exit', process.exitCode, null) + /* istanbul ignore next */ + emit('afterexit', process.exitCode, null) + return ret + } else { + return originalProcessEmit.apply(this, arguments) + } +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/package.json b/node_modules/spawn-wrap/node_modules/signal-exit/package.json new file mode 100644 index 000000000..35db9057a --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/package.json @@ -0,0 +1,31 @@ +{ + "name": "signal-exit", + "version": "2.1.2", + "description": "when you want to fire an event no matter how a process exits.", + "main": "index.js", + "scripts": { + "test": "standard && nyc tap --timeout=240 ./test/*.js", + "coverage": "nyc report --reporter=text-lcov | coveralls" + }, + "repository": { + "type": "git", + "url": "https://github.com/bcoe/signal-exit.git" + }, + "keywords": [ + "signal", + "exit" + ], + "author": "Ben Coe <ben@npmjs.com>", + "license": "ISC", + "bugs": { + "url": "https://github.com/bcoe/signal-exit/issues" + }, + "homepage": "https://github.com/bcoe/signal-exit", + "devDependencies": { + "chai": "^2.3.0", + "coveralls": "^2.11.2", + "nyc": "^2.1.2", + "standard": "^3.9.0", + "tap": "1.0.4" + } +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/signals.js b/node_modules/spawn-wrap/node_modules/signal-exit/signals.js new file mode 100644 index 000000000..453fb0e86 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/signals.js @@ -0,0 +1,47 @@ +// This is not the set of all possible signals. +// +// It IS, however, the set of all signals that trigger +// an exit on either Linux or BSD systems. Linux is a +// superset of the signal names supported on BSD, and +// the unknown signals just fail to register, so we can +// catch that easily enough. +// +// Don't bother with SIGKILL. It's uncatchable, which +// means that we can't fire any callbacks anyway. +// +// If a user does happen to register a handler on a non- +// fatal signal like SIGWINCH or something, and then +// exit, it'll end up firing `process.emit('exit')`, so +// the handler will be fired anyway. + +module.exports = [ + 'SIGABRT', + 'SIGALRM', + 'SIGBUS', + 'SIGFPE', + 'SIGHUP', + 'SIGILL', + 'SIGINT', + 'SIGIOT', + 'SIGPIPE', + 'SIGPROF', + 'SIGQUIT', + 'SIGSEGV', + 'SIGSYS', + 'SIGTERM', + 'SIGTRAP', + 'SIGUSR2', + 'SIGVTALRM', + 'SIGXCPU', + 'SIGXFSZ' +] + +if (process.platform === 'linux') { + module.exports.push( + 'SIGIO', + 'SIGPOLL', + 'SIGPWR', + 'SIGSTKFLT', + 'SIGUNUSED' + ) +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/all-integration-test.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/all-integration-test.js new file mode 100644 index 000000000..db76adf7c --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/all-integration-test.js @@ -0,0 +1,94 @@ +/* global describe, it */ + +var exec = require('child_process').exec, + assert = require('assert') + +require('chai').should() +require('tap').mochaGlobals() + +var onSignalExit = require('../') + +describe('all-signals-integration-test', function () { + + // These are signals that are aliases for other signals, so + // the result will sometimes be one of the others. For these, + // we just verify that we GOT a signal, not what it is. + function weirdSignal (sig) { + return sig === 'SIGIOT' || + sig === 'SIGIO' || + sig === 'SIGSYS' || + sig === 'SIGIOT' || + sig === 'SIGABRT' || + sig === 'SIGPOLL' || + sig === 'SIGUNUSED' + } + + // Exhaustively test every signal, and a few numbers. + var signals = onSignalExit.signals() + signals.concat('', 0, 1, 2, 3, 54).forEach(function (sig) { + var node = process.execPath + var js = require.resolve('./fixtures/exiter.js') + it('exits properly: ' + sig, function (done) { + // travis has issues with SIGUSR1 on Node 0.x.10. + if (process.env.TRAVIS && sig === 'SIGUSR1') return done() + + exec(node + ' ' + js + ' ' + sig, function (err, stdout, stderr) { + if (sig) { + assert(err) + if (!isNaN(sig)) { + assert.equal(err.code, sig) + } else if (!weirdSignal(sig)) { + if (!process.env.TRAVIS) err.signal.should.equal(sig) + } else if (sig) { + if (!process.env.TRAVIS) assert(err.signal) + } + } else { + assert.ifError(err) + } + + try { + var data = JSON.parse(stdout) + } catch (er) { + console.error('invalid json: %j', stdout, stderr) + throw er + } + + if (weirdSignal(sig)) { + data.wanted[1] = true + data.found[1] = !!data.found[1] + } + assert.deepEqual(data.found, data.wanted) + done() + }) + }) + }) + + signals.forEach(function (sig) { + var node = process.execPath + var js = require.resolve('./fixtures/parent.js') + it('exits properly: (external sig) ' + sig, function (done) { + // travis has issues with SIGUSR1 on Node 0.x.10. + if (process.env.TRAVIS && sig === 'SIGUSR1') return done() + + var cmd = node + ' ' + js + ' ' + sig + exec(cmd, function (err, stdout, stderr) { + assert.ifError(err) + try { + var data = JSON.parse(stdout) + } catch (er) { + console.error('invalid json: %j', stdout, stderr) + throw er + } + + if (weirdSignal(sig)) { + data.wanted[1] = true + data.found[1] = !!data.found[1] + data.external[1] = !!data.external[1] + } + assert.deepEqual(data.found, data.wanted) + assert.deepEqual(data.external, data.wanted) + done() + }) + }) + }) +}) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/awaiter.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/awaiter.js new file mode 100644 index 000000000..5bc3f6823 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/awaiter.js @@ -0,0 +1,35 @@ +var expectSignal = process.argv[2] + +if (!expectSignal || !isNaN(expectSignal)) { + throw new Error('signal not provided') +} + +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + // some signals don't always get recognized properly, because + // they have the same numeric code. + if (wanted[1] === true) { + signal = !!signal + } + console.log('%j', { + found: [ code, signal ], + wanted: wanted + }) +}) + +var wanted +switch (expectSignal) { + case 'SIGIOT': + case 'SIGUNUSED': + case 'SIGPOLL': + wanted = [ null, true ] + break + default: + wanted = [ null, expectSignal ] + break +} + +console.error('want', wanted) + +setTimeout(function () {}, 1000) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code-expect.json b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code-expect.json new file mode 100644 index 000000000..7eeeb4cbb --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code-expect.json @@ -0,0 +1,800 @@ +{ + "explicit 0 nochange sigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "explicit 0 nochange nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "explicit 0 change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit 0 change nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit 0 code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "explicit 0 code nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "explicit 0 twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit 0 twice nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit 0 twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "explicit 0 twicecode nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "explicit 2 nochange sigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 2, + "actualSignal": null, + "stderr": [ + "first code=2", + "second code=2" + ] + }, + "explicit 2 nochange nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 2, + "actualSignal": null, + "stderr": [ + "first code=2", + "second code=2" + ] + }, + "explicit 2 change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "explicit 2 change nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "explicit 2 code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "second code=2" + ] + }, + "explicit 2 code nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "second code=2" + ] + }, + "explicit 2 twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "explicit 2 twice nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "explicit 2 twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "set code from 5 to 6" + ] + }, + "explicit 2 twicecode nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "set code from 5 to 6" + ] + }, + "explicit null nochange sigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "explicit null nochange nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "explicit null change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit null change nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit null code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "explicit null code nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "explicit null twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit null twice nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "explicit null twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "explicit null twicecode nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "code 0 nochange sigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "code 0 nochange nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "code 0 change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code 0 change nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code 0 code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "code 0 code nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "code 0 twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code 0 twice nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code 0 twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "code 0 twicecode nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "code 2 nochange sigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 2, + "actualSignal": null, + "stderr": [ + "first code=2", + "second code=2" + ] + }, + "code 2 nochange nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 2, + "actualSignal": null, + "stderr": [ + "first code=2", + "second code=2" + ] + }, + "code 2 change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "code 2 change nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "code 2 code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "second code=2" + ] + }, + "code 2 code nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "second code=2" + ] + }, + "code 2 twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "code 2 twice nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5" + ] + }, + "code 2 twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "set code from 5 to 6" + ] + }, + "code 2 twicecode nosigexit": { + "code": 2, + "signal": null, + "exitCode": 2, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=2", + "set code from 2 to 5", + "set code from 5 to 6" + ] + }, + "code null nochange sigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "code null nochange nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "code null change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code null change nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code null code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "code null code nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "code null twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code null twice nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "code null twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "code null twicecode nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "normal 0 nochange sigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "normal 0 nochange nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 0, + "actualSignal": null, + "stderr": [ + "first code=0", + "second code=0" + ] + }, + "normal 0 change sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "normal 0 change nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "normal 0 code sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "normal 0 code nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "second code=0" + ] + }, + "normal 0 twice sigexit": { + "code": 5, + "signal": null, + "exitCode": 5, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "normal 0 twice nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 5, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5" + ] + }, + "normal 0 twicecode sigexit": { + "code": 6, + "signal": null, + "exitCode": 6, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + }, + "normal 0 twicecode nosigexit": { + "code": 0, + "signal": null, + "exitCode": 0, + "actualCode": 6, + "actualSignal": null, + "stderr": [ + "first code=0", + "set code from 0 to 5", + "set code from 5 to 6" + ] + } +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code.js new file mode 100644 index 000000000..daa6ae88b --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/change-code.js @@ -0,0 +1,96 @@ +if (process.argv.length === 2) { + var types = [ 'explicit', 'code', 'normal' ] + var codes = [ 0, 2, 'null' ] + var changes = [ 'nochange', 'change', 'code', 'twice', 'twicecode'] + var handlers = [ 'sigexit', 'nosigexit' ] + var opts = [] + types.forEach(function (type) { + var testCodes = type === 'normal' ? [ 0 ] : codes + testCodes.forEach(function (code) { + changes.forEach(function (change) { + handlers.forEach(function (handler) { + opts.push([type, code, change, handler].join(' ')) + }) + }) + }) + }) + + var results = {} + + var exec = require('child_process').exec + run(opts.shift()) +} else { + var type = process.argv[2] + var code = +process.argv[3] + var change = process.argv[4] + var sigexit = process.argv[5] !== 'nosigexit' + + if (sigexit) { + var onSignalExit = require('../../') + onSignalExit(listener) + } else { + process.on('exit', listener) + } + + process.on('exit', function (code) { + console.error('first code=%j', code) + }) + + if (change !== 'nochange') { + process.once('exit', function (code) { + console.error('set code from %j to %j', code, 5) + if (change === 'code' || change === 'twicecode') { + process.exitCode = 5 + } else { + process.exit(5) + } + }) + if (change === 'twicecode' || change === 'twice') { + process.once('exit', function (code) { + code = process.exitCode || code + console.error('set code from %j to %j', code, code + 1) + process.exit(code + 1) + }) + } + } + + process.on('exit', function (code) { + console.error('second code=%j', code) + }) + + if (type === 'explicit') { + if (code || code === 0) { + process.exit(code) + } else { + process.exit() + } + } else if (type === 'code') { + process.exitCode = +code || 0 + } +} + +function listener (code, signal) { + signal = signal || null + console.log('%j', { code: code, signal: signal, exitCode: process.exitCode || 0 }) +} + +function run (opt) { + console.error(opt) + exec(process.execPath + ' ' + __filename + ' ' + opt, function (err, stdout, stderr) { + var res = JSON.parse(stdout) + if (err) { + res.actualCode = err.code + res.actualSignal = err.signal + } else { + res.actualCode = 0 + res.actualSignal = null + } + res.stderr = stderr.trim().split('\n') + results[opt] = res + if (opts.length) { + run(opts.shift()) + } else { + console.log(JSON.stringify(results, null, 2)) + } + }) +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/end-of-execution.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/end-of-execution.js new file mode 100644 index 000000000..8b8f245ab --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/end-of-execution.js @@ -0,0 +1,5 @@ +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + console.log('reached end of execution, ' + code + ', ' + signal) +}) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit-last.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit-last.js new file mode 100644 index 000000000..899f475f7 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit-last.js @@ -0,0 +1,14 @@ +var onSignalExit = require('../../') +var counter = 0 + +onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) + +onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit.js new file mode 100644 index 000000000..c1aab3e40 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exit.js @@ -0,0 +1,7 @@ +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + console.log('exited with process.exit(), ' + code + ', ' + signal) +}) + +process.exit(32) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exiter.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exiter.js new file mode 100644 index 000000000..906ec490a --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/exiter.js @@ -0,0 +1,45 @@ +var exit = process.argv[2] || 0 + +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + // some signals don't always get recognized properly, because + // they have the same numeric code. + if (wanted[1] === true) { + signal = !!signal + } + console.log('%j', { + found: [ code, signal ], + wanted: wanted + }) +}) + +var wanted +if (isNaN(exit)) { + switch (exit) { + case 'SIGIOT': + case 'SIGUNUSED': + case 'SIGPOLL': + wanted = [ null, true ] + break + default: + wanted = [ null, exit ] + break + } + + try { + process.kill(process.pid, exit) + setTimeout(function () {}, 1000) + } catch (er) { + wanted = [ 0, null ] + } + +} else { + exit = +exit + wanted = [ exit, null ] + // If it's explicitly requested 0, then explicitly call it. + // "no arg" = "exit naturally" + if (exit || process.argv[2]) { + process.exit(exit) + } +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/load-unload.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/load-unload.js new file mode 100644 index 000000000..5509e2ef0 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/load-unload.js @@ -0,0 +1,7 @@ +// just be silly with calling these functions a bunch +// mostly just to get coverage of the guard branches +var onSignalExit = require('../../') +onSignalExit.load() +onSignalExit.load() +onSignalExit.unload() +onSignalExit.unload() diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/multiple-load.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/multiple-load.js new file mode 100644 index 000000000..623c4f144 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/multiple-load.js @@ -0,0 +1,52 @@ +// simulate cases where the module could be loaded from multiple places +var onSignalExit = require('../../') +var counter = 0 + +onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) + +onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) + +delete require('module')._cache[require.resolve('../../')] +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) + +onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) + +// Lastly, some that should NOT be shown +delete require('module')._cache[require.resolve('../../')] +var onSignalExit = require('../../') + +var unwrap = onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) +unwrap() + +unwrap = onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) + +unwrap() + +process.kill(process.pid, 'SIGHUP') +setTimeout(function () {}, 1000) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/parent.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/parent.js new file mode 100644 index 000000000..5dcc382df --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/parent.js @@ -0,0 +1,51 @@ +var signal = process.argv[2] +var gens = +process.argv[3] || 0 + +if (!signal || !isNaN(signal)) { + throw new Error('signal not provided') +} + +var spawn = require('child_process').spawn +var file = require.resolve('./awaiter.js') +console.error(process.pid, signal, gens) + +if (gens > 0) { + file = __filename +} + +var child = spawn(process.execPath, [file, signal, gens - 1], { + stdio: [ 0, 'pipe', 'pipe' ] +}) + +if (!gens) { + child.stderr.on('data', function () { + child.kill(signal) + }) +} + +var result = '' +child.stdout.on('data', function (c) { + result += c +}) + +child.on('close', function (code, sig) { + try { + result = JSON.parse(result) + } catch (er) { + console.log('%j', { + error: 'failed to parse json\n' + er.message, + result: result, + pid: process.pid, + child: child.pid, + gens: gens, + expect: [ null, signal ], + actual: [ code, sig ] + }) + return + } + if (result.wanted[1] === true) { + sig = !!sig + } + result.external = result.external || [ code, sig ] + console.log('%j', result) +}) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigint.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigint.js new file mode 100644 index 000000000..769a07641 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigint.js @@ -0,0 +1,11 @@ +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + console.log('exited with sigint, ' + code + ', ' + signal) +}) + +// For some reason, signals appear to not always be fast enough +// to come in before the process exits. Just a few ticks needed. +setTimeout(function () {}, 1000) + +process.kill(process.pid, 'SIGINT') diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigkill.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigkill.js new file mode 100644 index 000000000..88492d2d3 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigkill.js @@ -0,0 +1,19 @@ +// SIGKILL can't be caught, and in fact, even trying to add the +// listener will throw an error. +// We handle that nicely. +// +// This is just here to get another few more lines of test +// coverage. That's also why it lies about being on a linux +// platform so that we pull in those other event types. + +Object.defineProperty(process, 'platform', { + value: 'linux', + writable: false, + enumerable: true, + configurable: true +}) + +var signals = require('../../signals.js') +signals.push('SIGKILL') +var onSignalExit = require('../../') +onSignalExit.load() diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-default.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-default.js new file mode 100644 index 000000000..2598f803c --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-default.js @@ -0,0 +1,99 @@ +// This fixture is not used in any tests. It is here merely as a way to +// do research into the various signal behaviors on Linux and Darwin. +// Run with no args to cycle through every signal type. Run with a signal +// arg to learn about how that signal behaves. + +if (process.argv[2]) { + child(process.argv[2]) +} else { + var signals = [ + 'SIGABRT', + 'SIGALRM', + 'SIGBUS', + 'SIGCHLD', + 'SIGCLD', + 'SIGCONT', + 'SIGEMT', + 'SIGFPE', + 'SIGHUP', + 'SIGILL', + 'SIGINFO', + 'SIGINT', + 'SIGIO', + 'SIGIOT', + 'SIGKILL', + 'SIGLOST', + 'SIGPIPE', + 'SIGPOLL', + 'SIGPROF', + 'SIGPWR', + 'SIGQUIT', + 'SIGSEGV', + 'SIGSTKFLT', + 'SIGSTOP', + 'SIGSYS', + 'SIGTERM', + 'SIGTRAP', + 'SIGTSTP', + 'SIGTTIN', + 'SIGTTOU', + 'SIGUNUSED', + 'SIGURG', + 'SIGUSR1', + 'SIGUSR2', + 'SIGVTALRM', + 'SIGWINCH', + 'SIGXCPU', + 'SIGXFSZ' + ] + + var spawn = require('child_process').spawn + ;(function test (signal) { + if (!signal) { + return + } + var child = spawn(process.execPath, [__filename, signal], { stdio: 'inherit' }) + var timer = setTimeout(function () { + console.log('requires SIGCONT') + process.kill(child.pid, 'SIGCONT') + }, 750) + + child.on('close', function (code, signal) { + console.log('code=%j signal=%j\n', code, signal) + clearTimeout(timer) + test(signals.pop()) + }) + })(signals.pop()) +} + +function child (signal) { + console.log('signal=%s', signal) + + // set a timeout so we know whether or not the process terminated. + setTimeout(function () { + console.log('not terminated') + }, 200) + + process.on('exit', function (code) { + console.log('emit exit code=%j', code) + }) + + try { + process.on(signal, function fn () { + console.log('signal is catchable', signal) + process.removeListener(signal, fn) + setTimeout(function () { + console.error('signal again') + process.kill(process.pid, signal) + }) + }) + } catch (er) { + console.log('not listenable') + } + + try { + process.kill(process.pid, signal) + } catch (er) { + console.log('not issuable') + } +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-last.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-last.js new file mode 100644 index 000000000..9e7dec83d --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-last.js @@ -0,0 +1,17 @@ +var onSignalExit = require('../../') +var counter = 0 + +onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) + +onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) + +process.kill(process.pid, 'SIGHUP') +setTimeout(function () {}, 1000) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-listener.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-listener.js new file mode 100644 index 000000000..5a84d1267 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/signal-listener.js @@ -0,0 +1,23 @@ +var onSignalExit = require('../../') + +setTimeout(function () {}) + +var calledListener = 0 +onSignalExit(function (code, signal) { + console.log('exited calledListener=%j, code=%j, signal=%j', + calledListener, code, signal) +}) + +process.on('SIGHUP', listener) +process.kill(process.pid, 'SIGHUP') + +function listener () { + calledListener++ + if (calledListener > 3) { + process.removeListener('SIGHUP', listener) + } + + setTimeout(function () { + process.kill(process.pid, 'SIGHUP') + }) +} diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigpipe.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigpipe.js new file mode 100644 index 000000000..169faed29 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigpipe.js @@ -0,0 +1,8 @@ +var onSignalExit = require('../..') +onSignalExit(function (code, signal) { + console.error('onSignalExit(%j,%j)', code, signal) +}) +setTimeout(function () { + console.log('hello') +}) +process.kill(process.pid, 'SIGPIPE') diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigterm.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigterm.js new file mode 100644 index 000000000..85b598a7e --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/sigterm.js @@ -0,0 +1,9 @@ +var onSignalExit = require('../../') + +onSignalExit(function (code, signal) { + console.log('exited with sigterm, ' + code + ', ' + signal) +}) + +setTimeout(function () {}, 1000) + +process.kill(process.pid, 'SIGTERM') diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/unwrap.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/unwrap.js new file mode 100644 index 000000000..8d8b1ad23 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/fixtures/unwrap.js @@ -0,0 +1,37 @@ +// simulate cases where the module could be loaded from multiple places + +// Need to lie about this a little bit, since nyc uses this module +// for its coverage wrap-up handling +if (process.env.NYC_CWD) { + var emitter = process.__signal_exit_emitter__ + var listeners = emitter.listeners('afterexit') + process.removeAllListeners('SIGHUP') + delete process.__signal_exit_emitter__ + delete require('module')._cache[require.resolve('../../')] +} + +var onSignalExit = require('../../') +var counter = 0 + +var unwrap = onSignalExit(function (code, signal) { + counter++ + console.log('last counter=%j, code=%j, signal=%j', + counter, code, signal) +}, {alwaysLast: true}) +unwrap() + +unwrap = onSignalExit(function (code, signal) { + counter++ + console.log('first counter=%j, code=%j, signal=%j', + counter, code, signal) +}) +unwrap() + +if (global.__coverage__ && listeners && listeners.length) { + listeners.forEach(function (fn) { + onSignalExit(fn, { alwaysLast: true }) + }) +} + +process.kill(process.pid, 'SIGHUP') +setTimeout(function () {}, 1000) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/multi-exit.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/multi-exit.js new file mode 100644 index 000000000..271edd46a --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/multi-exit.js @@ -0,0 +1,58 @@ +var exec = require('child_process').exec, + t = require('tap') + +var fixture = require.resolve('./fixtures/change-code.js') +var expect = require('./fixtures/change-code-expect.json') + +// process.exitCode has problems prior to: +// https://github.com/joyent/node/commit/c0d81f90996667a658aa4403123e02161262506a +function isZero10 () { + return /^v0\.10\..+$/.test(process.version) +} + +// process.exit(code), process.exitCode = code, normal exit +var types = [ 'explicit', 'normal' ] +if (!isZero10()) types.push('code') + +// initial code that is set. Note, for 'normal' exit, there's no +// point doing these, because we just exit without modifying code +var codes = [ 0, 2, 'null' ] + +// do not change, change to 5 with exit(), change to 5 with exitCode, +// change to 5 and then to 2 with exit(), change twice with exitcode +var changes = [ 'nochange', 'change', 'twice'] +if (!isZero10()) changes.push('code', 'twicecode') + +// use signal-exit, use process.on('exit') +var handlers = [ 'sigexit', 'nosigexit' ] + +var opts = [] +types.forEach(function (type) { + var testCodes = type === 'normal' ? [0] : codes + testCodes.forEach(function (code) { + changes.forEach(function (change) { + handlers.forEach(function (handler) { + opts.push([type, code, change, handler].join(' ')) + }) + }) + }) +}) + +opts.forEach(function (opt) { + t.test(opt, function (t) { + var cmd = process.execPath + ' ' + fixture + ' ' + opt + exec(cmd, function (err, stdout, stderr) { + var res = JSON.parse(stdout) + if (err) { + res.actualCode = err.code + res.actualSignal = err.signal + } else { + res.actualCode = 0 + res.actualSignal = null + } + res.stderr = stderr.trim().split('\n') + t.same(res, expect[opt]) + t.end() + }) + }) +}) diff --git a/node_modules/spawn-wrap/node_modules/signal-exit/test/signal-exit-test.js b/node_modules/spawn-wrap/node_modules/signal-exit/test/signal-exit-test.js new file mode 100644 index 000000000..4929f85f9 --- /dev/null +++ b/node_modules/spawn-wrap/node_modules/signal-exit/test/signal-exit-test.js @@ -0,0 +1,108 @@ +/* global describe, it */ + +var exec = require('child_process').exec, + expect = require('chai').expect, + assert = require('assert') + +require('chai').should() +require('tap').mochaGlobals() + +describe('signal-exit', function () { + + it('receives an exit event when a process exits normally', function (done) { + exec(process.execPath + ' ./test/fixtures/end-of-execution.js', function (err, stdout, stderr) { + expect(err).to.equal(null) + stdout.should.match(/reached end of execution, 0, null/) + done() + }) + }) + + it('receives an exit event when a process is terminated with sigint', function (done) { + exec(process.execPath + ' ./test/fixtures/sigint.js', function (err, stdout, stderr) { + assert(err) + stdout.should.match(/exited with sigint, null, SIGINT/) + done() + }) + }) + + it('receives an exit event when a process is terminated with sigterm', function (done) { + exec(process.execPath + ' ./test/fixtures/sigterm.js', function (err, stdout, stderr) { + assert(err) + stdout.should.match(/exited with sigterm, null, SIGTERM/) + done() + }) + }) + + it('receives an exit event when process.exit() is called', function (done) { + exec(process.execPath + ' ./test/fixtures/exit.js', function (err, stdout, stderr) { + err.code.should.equal(32) + stdout.should.match(/exited with process\.exit\(\), 32, null/) + done() + }) + }) + + it('does not exit if user handles signal', function (done) { + exec(process.execPath + ' ./test/fixtures/signal-listener.js', function (err, stdout, stderr) { + assert(err) + assert.equal(stdout, 'exited calledListener=4, code=null, signal="SIGHUP"\n') + done() + }) + }) + + it('ensures that if alwaysLast=true, the handler is run last (signal)', function (done) { + exec(process.execPath + ' ./test/fixtures/signal-last.js', function (err, stdout, stderr) { + assert(err) + stdout.should.match(/first counter=1/) + stdout.should.match(/last counter=2/) + done() + }) + }) + + it('ensures that if alwaysLast=true, the handler is run last (normal exit)', function (done) { + exec(process.execPath + ' ./test/fixtures/exit-last.js', function (err, stdout, stderr) { + assert.ifError(err) + stdout.should.match(/first counter=1/) + stdout.should.match(/last counter=2/) + done() + }) + }) + + it('works when loaded multiple times', function (done) { + exec(process.execPath + ' ./test/fixtures/multiple-load.js', function (err, stdout, stderr) { + assert(err) + stdout.should.match(/first counter=1, code=null, signal="SIGHUP"/) + stdout.should.match(/first counter=2, code=null, signal="SIGHUP"/) + stdout.should.match(/last counter=3, code=null, signal="SIGHUP"/) + stdout.should.match(/last counter=4, code=null, signal="SIGHUP"/) + done() + }) + }) + + // TODO: test on a few non-OSX machines. + it('removes handlers when fully unwrapped', function (done) { + exec(process.execPath + ' ./test/fixtures/unwrap.js', function (err, stdout, stderr) { + // on Travis CI no err.signal is populated but + // err.code is 129 (which I think tends to be SIGHUP). + var expectedCode = process.env.TRAVIS ? 129 : null + + assert(err) + if (!process.env.TRAVIS) err.signal.should.equal('SIGHUP') + expect(err.code).to.equal(expectedCode) + done() + }) + }) + + it('does not load() or unload() more than once', function (done) { + exec(process.execPath + ' ./test/fixtures/load-unload.js', function (err, stdout, stderr) { + assert.ifError(err) + done() + }) + }) + + it('handles uncatchable signals with grace and poise', function (done) { + exec(process.execPath + ' ./test/fixtures/sigkill.js', function (err, stdout, stderr) { + assert.ifError(err) + done() + }) + }) +}) diff --git a/node_modules/spawn-wrap/package.json b/node_modules/spawn-wrap/package.json new file mode 100644 index 000000000..e093f5ab4 --- /dev/null +++ b/node_modules/spawn-wrap/package.json @@ -0,0 +1,30 @@ +{ + "name": "spawn-wrap", + "version": "1.2.4", + "description": "Wrap all spawned Node.js child processes by adding environs and arguments ahead of the main JavaScript file argument.", + "main": "index.js", + "dependencies": { + "foreground-child": "^1.3.3", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.3.3", + "signal-exit": "^2.0.0", + "which": "^1.2.4" + }, + "scripts": { + "test": "tap test/*.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/spawn-wrap.git" + }, + "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", + "license": "ISC", + "bugs": { + "url": "https://github.com/isaacs/spawn-wrap/issues" + }, + "homepage": "https://github.com/isaacs/spawn-wrap#readme", + "devDependencies": { + "tap": "^2.3.0" + } +} diff --git a/node_modules/spawn-wrap/shim.js b/node_modules/spawn-wrap/shim.js new file mode 100644 index 000000000..96437215c --- /dev/null +++ b/node_modules/spawn-wrap/shim.js @@ -0,0 +1,139 @@ +// This module should *only* be loaded as a main script +// by child processes wrapped by spawn-wrap. It sets up +// argv to include the injected argv (including the user's +// wrapper script) and any environment variables specified. +// +// If any argv were passed in (ie, if it's used to force +// a wrapper script, and not just ensure that an env is kept +// around through all the child procs), then we also set up +// a require('spawn-wrap').runMain() function that will strip +// off the injected arguments and run the main file. + +if (module !== require.main) { + throw new Error('spawn-wrap: cli wrapper invoked as non-main script') +} + +// require('fs').createWriteStream('/dev/tty').write('WRAP ' + process.argv.slice(2).join(' ') + '\n') +var Module = require('module') +var assert = require('assert') +var path = require('path') +var node = process.execPath + +var settings = require('./settings.json') +var foregroundChild = require(settings.deps.foregroundChild) +var argv = settings.argv +var nargs = argv.length +var env = settings.env + +for (var key in env) { + process.env[key] = env[key] +} + +var needExecArgv = settings.execArgv || [] + +// If the user added their OWN wrapper pre-load script, then +// this will pop that off of the argv, and load the "real" main +function runMain () { + process.argv.splice(1, nargs) + process.argv[1] = path.resolve(process.argv[1]) + Module.runMain() +} + +// Argv coming in looks like: +// bin shim execArgv main argv +// +// Turn it into: +// bin settings.execArgv execArgv settings.argv main argv +// +// If we don't have a main script, then just run with the necessary +// execArgv +var hasMain = false +for (var a = 2; !hasMain && a < process.argv.length; a++) { + switch (process.argv[a]) { + case '-i': + case '--interactive': + case '--eval': + case '-e': + case '-pe': + hasMain = false + a = process.argv.length + continue + + case '-r': + case '--require': + a += 1 + continue + + default: + if (process.argv[a].match(/^-/)) { + continue + } else { + hasMain = a + a = process.argv.length + break + } + } +} + +if (hasMain > 2) { + // if the main file is above #2, then it means that there + // was a --exec_arg *before* it. This means that we need + // to slice everything from 2 to hasMain, and pass that + // directly to node. This also splices out the user-supplied + // execArgv from the argv. + var addExecArgv = process.argv.splice(2, hasMain - 2) + needExecArgv.push.apply(needExecArgv, addExecArgv) +} + +if (!hasMain) { + // we got loaded by mistake for a `node -pe script` or something. + var args = process.execArgv.concat(needExecArgv, process.argv.slice(2)) + foregroundChild(node, args) + return +} + +// If there are execArgv, and they're not the same as how this module +// was executed, then we need to inject those. This is for stuff like +// --harmony or --use_strict that needs to be *before* the main +// module. +if (needExecArgv.length) { + var pexec = process.execArgv + if (JSON.stringify(pexec) !== JSON.stringify(needExecArgv)) { + var spawn = require('child_process').spawn + var sargs = pexec.concat(needExecArgv).concat(process.argv.slice(1)) + foregroundChild(node, sargs) + return + } +} + +// At this point, we've verified that we got the correct execArgv, +// and that we have a main file, and that the main file is sitting at +// argv[2]. Splice this shim off the list so it looks like the main. +var spliceArgs = [1, 1].concat(argv) +process.argv.splice.apply(process.argv, spliceArgs) + +// Unwrap the PATH environment var so that we're not mucking +// with the environment. It'll get re-added if they spawn anything +var isWindows = ( + process.platform === 'win32' || + process.env.OSTYPE === 'cygwin' || + process.env.OSTYPE === 'msys' +) + +if (isWindows) { + for (var i in process.env) { + if (i.match(/^path$/i)) { + process.env[i] = process.env[i].replace(__dirname + ';', '') + } + } +} else { + process.env.PATH = process.env.PATH.replace(__dirname + ':', '') +} + +var spawnWrap = require(settings.module) +if (nargs) { + spawnWrap.runMain = runMain +} +spawnWrap(argv, env, __dirname) + +Module.runMain() diff --git a/node_modules/spawn-wrap/t.js b/node_modules/spawn-wrap/t.js new file mode 100755 index 000000000..fa0ce2b7a --- /dev/null +++ b/node_modules/spawn-wrap/t.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node +if (process.env.xyz) { + console.log('in t.js, xyz=%j', process.env.xyz) + console.log('gc is a', typeof gc) + console.log('%j', process.argv) + console.log('about to run the main file\u001b[32m') + require('./index.js').runMain() + console.log('\u001b[31mran wrapped main') + return +} + +var wrap = require('./index.js') + +var unwrap = wrap(['--expose_gc', __filename, ' a $ b '], { xyz: 'ABC' }) + +console.log('about to run child process') +console.log('gc is a', typeof gc) +var cp = require('child_process') +var child = cp.exec(process.execPath + ' $(which tap ) -h', { env: { foo: 'asdf', PATH:process.env.PATH } }, function (er, out, err) { + console.error('returned') + console.error('error = ', er) + console.error('outlen=', out.length) + console.error('\u001b[31m' + out + '\u001b[m') + console.error('errlen=', err.length) + process.stderr.write(err) +}) diff --git a/node_modules/spawn-wrap/test/abs-shebang.js b/node_modules/spawn-wrap/test/abs-shebang.js new file mode 100644 index 000000000..f254683df --- /dev/null +++ b/node_modules/spawn-wrap/test/abs-shebang.js @@ -0,0 +1,68 @@ +var path = require('path') +var fs = require('fs') +var spawn = require('child_process').spawn +var t = require('tap') +var node = process.execPath +var wrap = require.resolve('./fixtures/wrap.js') +var rimraf = require('rimraf') +var mkdirp = require('mkdirp') +var fs = require('fs') + +if (process.platform === 'win32') { + t.plan(0, 'No proper shebang support on windows, so skip this') + process.exit(0) +} + +var expect = + 'before in shim\n' + + 'shebang main foo,bar\n' + + 'after in shim\n' + + 'before in shim\n' + + 'shebang main foo,bar\n' + + 'after in shim\n' + +var fixdir = path.resolve(__dirname, 'fixtures', 'shebangs') + +t.test('setup', function (t) { + rimraf.sync(fixdir) + mkdirp.sync(fixdir) + t.end() +}) + +t.test('absolute', function (t) { + var file = path.resolve(fixdir, 'absolute.js') + runTest(file, process.execPath, t) +}) + +t.test('env', function (t) { + var file = path.resolve(fixdir, 'env.js') + runTest(file, '/usr/bin/env node', t) +}) + +function runTest (file, shebang, t) { + var content = '#!' + shebang + '\n' + + 'console.log("shebang main " + process.argv.slice(2))\n' + fs.writeFileSync(file, content, 'utf8') + fs.chmodSync(file, '0755') + var child = spawn(node, [wrap, file, 'foo', 'bar']) + var out = '' + var err = '' + child.stdout.on('data', function (c) { + out += c + }) + child.stderr.on('data', function (c) { + err += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + // console.error(err) + t.end() + }) +} + +t.test('cleanup', function (t) { + rimraf.sync(fixdir) + t.end() +}) diff --git a/node_modules/spawn-wrap/test/basic.js b/node_modules/spawn-wrap/test/basic.js new file mode 100644 index 000000000..969310088 --- /dev/null +++ b/node_modules/spawn-wrap/test/basic.js @@ -0,0 +1,419 @@ +var sw = require('../') +var isWindows = require('../lib/is-windows.js')() +var winNoShebang = isWindows && 'no shebang execution on windows' +var winNoSig = isWindows && 'no signals get through cmd' + +var onExit = require('signal-exit') +var cp = require('child_process') +var fixture = require.resolve('./fixtures/script.js') +var npmFixture = require.resolve('./fixtures/npm') +var fs = require('fs') +var path = require('path') + +if (process.argv[2] === 'parent') { + // hang up once + process.once('SIGHUP', function onHup () { + console.log('SIGHUP') + }) + // handle sigints forever + process.on('SIGINT', function onInt () { + console.log('SIGINT') + }) + onExit(function (code, signal) { + console.log('EXIT %j', [code, signal]) + }) + var argv = process.argv.slice(3).map(function (arg) { + if (arg === fixture) { + return '{{FIXTURE}}' + } + return arg + }) + console.log('WRAP %j', process.execArgv.concat(argv)) + sw.runMain() + return +} + +var t = require('tap') +var unwrap = sw([__filename, 'parent']) + +var expect = 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'EXIT [0,null]\n' + +// dummy for node v0.10 +if (!cp.spawnSync) { + cp.spawnSync = function () { + return { + status: 0, + signal: null, + stdout: expect + } + } +} + +t.test('spawn execPath', function (t) { + t.plan(4) + + t.test('basic', function (t) { + var child = cp.spawn(process.execPath, [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + t.end() + }) + }) + + t.test('basic sync', function (t) { + var child = cp.spawnSync(process.execPath, [fixture, 'xyz']) + + t.equal(child.status, 0) + t.equal(child.signal, null) + t.equal(child.stdout.toString(), expect) + t.end() + }) + + t.test('SIGINT', { skip: winNoSig }, function (t) { + var child = cp.spawn(process.execPath, [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.stdout.once('data', function () { + child.kill('SIGINT') + }) + child.stderr.on('data', function (t) { + console.error(t) + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGINT\n' + + 'EXIT [0,null]\n') + t.end() + }) + }) + + t.test('SIGHUP', { skip: winNoSig }, function (t) { + var child = cp.spawn(process.execPath, [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + child.kill('SIGHUP') + }) + child.on('close', function (code, signal) { + t.equal(signal, 'SIGHUP') + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGHUP\n' + + 'EXIT [null,"SIGHUP"]\n') + t.end() + }) + }) +}) + +t.test('spawn node', function (t) { + t.plan(4) + + t.test('basic', function (t) { + var child = cp.spawn('node', [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + t.end() + }) + }) + + t.test('basic sync', function (t) { + var child = cp.spawnSync('node', [fixture, 'xyz']) + + t.equal(child.status, 0) + t.equal(child.signal, null) + t.equal(child.stdout.toString(), expect) + t.end() + }) + + t.test('SIGINT', { skip: winNoSig }, function (t) { + var child = cp.spawn('node', [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.stdout.once('data', function () { + child.kill('SIGINT') + }) + child.stderr.on('data', function (t) { + console.error(t) + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGINT\n' + + 'EXIT [0,null]\n') + t.end() + }) + }) + + t.test('SIGHUP', { skip: winNoSig }, function (t) { + var child = cp.spawn('node', [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + child.kill('SIGHUP') + }) + child.on('close', function (code, signal) { + t.equal(signal, 'SIGHUP') + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGHUP\n' + + 'EXIT [null,"SIGHUP"]\n') + t.end() + }) + }) +}) + +t.test('exec execPath', function (t) { + t.plan(4) + + t.test('basic', function (t) { + var opt = isWindows ? null : { shell: '/bin/bash' } + var child = cp.exec(process.execPath + ' ' + fixture + ' xyz', opt) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + t.end() + }) + }) + + t.test('execPath wrapped with quotes', function (t) { + var opt = isWindows ? null : { shell: '/bin/bash' } + var child = cp.exec(JSON.stringify(process.execPath) + ' ' + fixture + + ' xyz', opt) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + t.end() + }) + }) + + t.test('SIGINT', { skip: winNoSig }, function (t) { + var child = cp.exec(process.execPath + ' ' + fixture + ' xyz', { shell: '/bin/bash' }) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.stdout.once('data', function () { + child.kill('SIGINT') + }) + child.stderr.on('data', function (t) { + console.error(t) + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGINT\n' + + 'EXIT [0,null]\n') + t.end() + }) + }) + + t.test('SIGHUP', { skip: winNoSig }, function (t) { + var child = cp.exec(process.execPath + ' ' + fixture + ' xyz', { shell: '/bin/bash' }) + + var out = '' + child.stdout.on('data', function (c) { + out += c + child.kill('SIGHUP') + }) + child.on('close', function (code, signal) { + t.equal(signal, 'SIGHUP') + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGHUP\n' + + 'EXIT [null,"SIGHUP"]\n') + t.end() + }) + }) +}) + +t.test('exec shebang', { skip: winNoShebang }, function (t) { + t.plan(3) + + t.test('basic', function (t) { + var child = cp.exec(fixture + ' xyz', { shell: '/bin/bash' }) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + t.end() + }) + }) + + t.test('SIGHUP', function (t) { + var child = cp.exec(fixture + ' xyz', { shell: '/bin/bash' }) + + var out = '' + child.stdout.on('data', function (c) { + out += c + child.kill('SIGHUP') + }) + child.on('close', function (code, signal) { + t.equal(signal, 'SIGHUP') + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGHUP\n' + + 'EXIT [null,"SIGHUP"]\n') + t.end() + }) + }) + + t.test('SIGINT', function (t) { + var child = cp.exec(fixture + ' xyz', { shell: '/bin/bash' }) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.stdout.once('data', function () { + child.kill('SIGINT') + }) + child.stderr.on('data', function (t) { + console.error(t) + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, 'WRAP ["{{FIXTURE}}","xyz"]\n' + + '[]\n' + + '["xyz"]\n' + + 'SIGINT\n' + + 'EXIT [0,null]\n') + t.end() + }) + }) +}) + +// see: https://github.com/bcoe/nyc/issues/190 +t.test('Node 5.8.x + npm 3.7.x - spawn', { skip: winNoShebang }, function (t) { + var npmdir = path.dirname(npmFixture) + process.env.PATH = npmdir + ':' + (process.env.PATH || '') + var child = cp.spawn('npm', ['xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.true(~out.indexOf('xyz')) + t.end() + }) +}) + +t.test('Node 5.8.x + npm 3.7.x - shell', { skip: winNoShebang }, function (t) { + var npmdir = path.dirname(npmFixture) + process.env.PATH = npmdir + ':' + (process.env.PATH || '') + var child = cp.exec('npm xyz') + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.true(~out.indexOf('xyz')) + t.end() + }) +}) + +t.test('--harmony', function (t) { + var node = process.execPath + var child = cp.spawn(node, ['--harmony', fixture, 'xyz']) + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, 'WRAP ["--harmony","{{FIXTURE}}","xyz"]\n' + + '["--harmony"]\n' + + '["xyz"]\n' + + 'EXIT [0,null]\n') + t.end() + }) +}) + +t.test('node exe with different name', function(t) { + var fp = path.join(__dirname, 'fixtures', 'exething.exe') + var data = fs.readFileSync(process.execPath) + fs.writeFileSync(fp, data) + fs.chmodSync(fp, '0775') + var child = cp.spawn(process.execPath, [fixture, 'xyz']) + + var out = '' + child.stdout.on('data', function (c) { + out += c + }) + child.on('close', function (code, signal) { + t.equal(code, 0) + t.equal(signal, null) + t.equal(out, expect) + fs.unlinkSync(fp) + t.end() + }) +}) + +t.test('unwrap', function (t) { + unwrap() + t.end() +}) diff --git a/node_modules/spawn-wrap/test/fixtures/node_modules/npm/bin/npm-cli.js b/node_modules/spawn-wrap/test/fixtures/node_modules/npm/bin/npm-cli.js new file mode 100644 index 000000000..b3c400768 --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/node_modules/npm/bin/npm-cli.js @@ -0,0 +1,4 @@ +'use strict'; +console.log('%j', process.execArgv) +console.log('%j', process.argv.slice(2)) +setTimeout(function () {}, 100) diff --git a/node_modules/spawn-wrap/test/fixtures/npm b/node_modules/spawn-wrap/test/fixtures/npm new file mode 100755 index 000000000..4e026deb9 --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/npm @@ -0,0 +1,5 @@ +#!/bin/sh +// 2>/dev/null; exec "`dirname "$0"`/node" "$0" "$@" +console.log('%j', process.execArgv) +console.log('%j', process.argv.slice(2)) +setTimeout(function () {}, 100) diff --git a/node_modules/spawn-wrap/test/fixtures/npm.cmd b/node_modules/spawn-wrap/test/fixtures/npm.cmd new file mode 100644 index 000000000..521ec9e4d --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/npm.cmd @@ -0,0 +1,2 @@ +@echo This code should never be executed. +exit /B 1 diff --git a/node_modules/spawn-wrap/test/fixtures/script.js b/node_modules/spawn-wrap/test/fixtures/script.js new file mode 100755 index 000000000..3ccd0c9ea --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/script.js @@ -0,0 +1,6 @@ +#!/usr/bin/env node +console.log('%j', process.execArgv) +console.log('%j', process.argv.slice(2)) + +// Keep the event loop alive long enough to receive signals. +setTimeout(function() {}, 100) diff --git a/node_modules/spawn-wrap/test/fixtures/test-shim.js b/node_modules/spawn-wrap/test/fixtures/test-shim.js new file mode 100644 index 000000000..b6baf69ee --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/test-shim.js @@ -0,0 +1,3 @@ +console.log('before in shim') +require('../..').runMain() +console.log('after in shim') diff --git a/node_modules/spawn-wrap/test/fixtures/wrap.js b/node_modules/spawn-wrap/test/fixtures/wrap.js new file mode 100644 index 000000000..a8af6e471 --- /dev/null +++ b/node_modules/spawn-wrap/test/fixtures/wrap.js @@ -0,0 +1,27 @@ +#!/usr/bin/env node +var sw = require('../..') + +sw([require.resolve('./test-shim.js')]) + +var path = require('path') +var spawn = require('child_process').spawn + +spawn(path.resolve(process.argv[2]), process.argv.slice(3), { + stdio: 'inherit' +}).on('close', function (code, signal) { + if (code || signal) { + throw new Error('failed with ' + (code || signal)) + } + + // now run using PATH + process.env.PATH = path.resolve(path.dirname(process.argv[2])) + + ':' + process.env.PATH + + spawn(path.basename(process.argv[2]), process.argv.slice(3), { + stdio: 'inherit', + }, function (code, signal) { + if (code || signal) { + throw new Error('failed with ' + (code || signal)) + } + }) +}) diff --git a/node_modules/spawn-wrap/test/win-rebase.js b/node_modules/spawn-wrap/test/win-rebase.js new file mode 100644 index 000000000..b9d09fe0b --- /dev/null +++ b/node_modules/spawn-wrap/test/win-rebase.js @@ -0,0 +1,40 @@ +var t = require('tap') +var winRebase = require('../lib/win-rebase') + +t.test('it replaces path to node bin', function (t) { + var result = winRebase('C:\\Program Files\\nodejs\\node.exe', 'C:\\foo') + t.equal(result, 'C:\\foo') + t.done() +}) + +t.test('it does not replace path if it references an unknown bin', function (t) { + var result = winRebase('C:\\Program Files\\nodejs\\banana', 'C:\\foo') + t.equal(result, 'C:\\Program Files\\nodejs\\banana') + t.done() +}) + +t.test('replaces node bin and leaves the script being executed', function (t) { + var result = winRebase('C:\\Program Files\\nodejs\\node.exe foo.js', 'C:\\foo') + t.equal(result, 'C:\\foo foo.js') + t.done() +}) + +t.test('handles a quote', function (t) { + var result = winRebase('"C:\\Program Files\\nodejs\\node.exe" "foo.js"', 'C:\\foo') + t.equal(result, '"C:\\foo" "foo.js"') + t.end() +}) + +t.test('handles many quotes', function (t) { + var result = winRebase('""C:\\Program Files\\nodejs\\node.exe" "foo.js""', 'C:\\foo') + t.equal(result, '""C:\\foo" "foo.js""') + t.end() +}) + +t.test('handles npm invocations', function (t) { + var result = winRebase('""npm" "install""', + 'C:\\foo', + function() { return 'C:\\path-to-npm\\npm' }) + t.equal(result, '""C:\\foo "C:\\path-to-npm\\node_modules\\npm\\bin\\npm-cli.js"" "install""') + t.end() +}) |