#!/usr/bin/env node 'use strict'; /** * This wrapper executable checks for known node flags and appends them when found, before invoking the "real" _mocha(1) executable. * * @module bin/mocha * @private */ const {deprecate, warn} = require('../lib/utils'); const {spawn} = require('child_process'); 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 mochaPath = require.resolve('./_mocha'); 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; } 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. });