| #!/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 {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 mochaArgs = {}; |
| const nodeArgs = {}; |
| let hasInspect = false; |
| |
| 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 %s disabled timeouts', value); |
| mochaArgs.timeout = 0; |
| } |
| }; |
| |
| /** |
| * 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]; |
| } else { |
| mochaArgs[opt] = opts[opt]; |
| } |
| }); |
| |
| // disable 'timeout' for debugFlags |
| Object.keys(nodeArgs).forEach(opt => disableTimeouts(opt)); |
| mochaArgs['node-option'] && |
| mochaArgs['node-option'].forEach(opt => disableTimeouts(opt)); |
| |
| // Native debugger handling |
| // see https://nodejs.org/api/debugger.html#debugger_debugger |
| // look for 'inspect' that would launch this debugger, |
| // remove it from Mocha's opts and prepend it to Node's opts. |
| // A deprecation warning will be printed by node, if applicable. |
| // (mochaArgs._ are "positional" arguments, not prefixed with - or --) |
| if (mochaArgs._) { |
| const i = mochaArgs._.findIndex(val => val === 'inspect'); |
| if (i > -1) { |
| mochaArgs._.splice(i, 1); |
| disableTimeouts('inspect'); |
| hasInspect = true; |
| } |
| } |
| |
| if (mochaArgs['node-option'] || Object.keys(nodeArgs).length || hasInspect) { |
| const {spawn} = require('child_process'); |
| const mochaPath = require.resolve('../lib/cli/cli.js'); |
| |
| const nodeArgv = |
| (mochaArgs['node-option'] && mochaArgs['node-option'].map(v => '--' + v)) || |
| unparseNodeFlags(nodeArgs); |
| |
| if (hasInspect) nodeArgv.unshift('inspect'); |
| delete mochaArgs['node-option']; |
| |
| debug('final node argv', nodeArgv); |
| |
| const args = [].concat( |
| nodeArgv, |
| mochaPath, |
| unparse(mochaArgs, {alias: aliases}) |
| ); |
| |
| debug( |
| 'forking child process via command: %s %s', |
| process.execPath, |
| args.join(' ') |
| ); |
| |
| 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', () => { |
| // XXX: a previous comment said this would abort the runner, but I can't see that it does |
| // anything with the default runner. |
| debug('main process caught SIGINT'); |
| proc.kill('SIGINT'); |
| // if running in parallel mode, we will have a proper SIGINT handler, so the below won't |
| // be needed. |
| if (!args.parallel || args.jobs < 2) { |
| // win32 does not support SIGTERM, so use next best thing. |
| if (require('os').platform() === 'win32') { |
| proc.kill('SIGKILL'); |
| } else { |
| // using SIGKILL won't cleanly close the output streams, which can result |
| // in cut-off text or a befouled terminal. |
| debug('sending SIGTERM to child process'); |
| proc.kill('SIGTERM'); |
| } |
| } |
| }); |
| } else { |
| debug('running Mocha in-process'); |
| require('../lib/cli/cli').main([], mochaArgs); |
| } |