| #!/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})); | |
| } |