| #!/usr/bin/env node |
| |
| 'use strict'; |
| |
| /** |
| * This wrapper executable checks for known node flags and appends them when found, |
| * before invoking the "real" executable (`lib/cli/cli.js`) |
| * |
| * @module bin/mocha |
| * @private |
| */ |
| |
| const {deprecate, warn} = require('../lib/utils'); |
| const {loadOptions} = require('../lib/cli/options'); |
| const { |
| unparseNodeFlags, |
| isNodeFlag, |
| impliesNoTimeouts |
| } = require('../lib/cli/node-flags'); |
| const unparse = require('yargs-unparser'); |
| const debug = require('debug')('mocha:cli:mocha'); |
| const {aliases} = require('../lib/cli/run-option-metadata'); |
| const nodeEnv = require('node-environment-flags'); |
| |
| const mochaArgs = {}; |
| const nodeArgs = {}; |
| |
| const opts = loadOptions(process.argv.slice(2)); |
| debug('loaded opts', opts); |
| |
| /** |
| * Given option/command `value`, disable timeouts if applicable |
| * @param {string} [value] - Value to check |
| * @ignore |
| */ |
| const disableTimeouts = value => { |
| if (impliesNoTimeouts(value)) { |
| debug(`option "${value}" disabled timeouts`); |
| mochaArgs.timeout = 0; |
| delete mochaArgs.timeouts; |
| delete mochaArgs.t; |
| } |
| }; |
| |
| /** |
| * If `value` begins with `v8-` and is not explicitly `v8-options`, remove prefix |
| * @param {string} [value] - Value to check |
| * @returns {string} `value` with prefix (maybe) removed |
| * @ignore |
| */ |
| const trimV8Option = value => |
| value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value; |
| |
| // sort options into "node" and "mocha" buckets |
| Object.keys(opts).forEach(opt => { |
| if (isNodeFlag(opt)) { |
| nodeArgs[trimV8Option(opt)] = opts[opt]; |
| disableTimeouts(opt); |
| } else { |
| mochaArgs[opt] = opts[opt]; |
| } |
| }); |
| |
| // Native debugger handling |
| // see https://nodejs.org/api/debugger.html#debugger_debugger |
| // look for 'debug' or 'inspect' that would launch this debugger, |
| // remove it from Mocha's opts and prepend it to Node's opts. |
| // also coerce depending on Node.js version. |
| // A deprecation warning will be printed by node, if applicable. |
| // (mochaArgs._ are "positional" arguments, not prefixed with - or --) |
| if (/^(debug|inspect)$/.test(mochaArgs._[0])) { |
| const command = mochaArgs._.shift(); |
| disableTimeouts(command); |
| // don't conflict with inspector |
| ['debug', 'inspect', 'debug-brk', 'inspect-brk'] |
| .filter(opt => opt in nodeArgs || opt in mochaArgs) |
| .forEach(opt => { |
| warn(`command "${command}" provided; --${opt} ignored`); |
| delete nodeArgs[opt]; |
| delete mochaArgs[opt]; |
| }); |
| nodeArgs._ = [ |
| parseInt( |
| process.version |
| .slice(1) |
| .split('.') |
| .shift(), |
| 10 |
| ) >= 8 |
| ? 'inspect' |
| : 'debug' |
| ]; |
| } |
| |
| // allow --debug to invoke --inspect on Node.js v8 or newer. |
| ['debug', 'debug-brk'] |
| .filter(opt => opt in nodeArgs && !nodeEnv.has(opt)) |
| .forEach(opt => { |
| const newOpt = opt === 'debug' ? 'inspect' : 'inspect-brk'; |
| warn( |
| `"--${opt}" is not available in Node.js ${ |
| process.version |
| }; use "--${newOpt}" instead.` |
| ); |
| nodeArgs[newOpt] = nodeArgs[opt]; |
| mochaArgs.timeout = false; |
| debug(`--${opt} -> ${newOpt}`); |
| delete nodeArgs[opt]; |
| }); |
| |
| // historical |
| if (nodeArgs.gc) { |
| deprecate( |
| '"-gc" is deprecated and will be removed from a future version of Mocha. Use "--gc-global" instead.' |
| ); |
| nodeArgs['gc-global'] = nodeArgs.gc; |
| delete nodeArgs.gc; |
| } |
| |
| if (Object.keys(nodeArgs).length) { |
| const {spawn} = require('child_process'); |
| const mochaPath = require.resolve('../lib/cli/cli.js'); |
| |
| debug('final node args', nodeArgs); |
| |
| const args = [].concat( |
| unparseNodeFlags(nodeArgs), |
| mochaPath, |
| unparse(mochaArgs, {alias: aliases}) |
| ); |
| |
| debug(`exec ${process.execPath} w/ args:`, args); |
| |
| const proc = spawn(process.execPath, args, { |
| stdio: 'inherit' |
| }); |
| |
| proc.on('exit', (code, signal) => { |
| process.on('exit', () => { |
| if (signal) { |
| process.kill(process.pid, signal); |
| } else { |
| process.exit(code); |
| } |
| }); |
| }); |
| |
| // terminate children. |
| process.on('SIGINT', () => { |
| proc.kill('SIGINT'); // calls runner.abort() |
| proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die. |
| }); |
| } else { |
| require('../lib/cli/cli').main(unparse(mochaArgs, {alias: aliases})); |
| } |