#!/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 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._)) {

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;

}

// –require/-r is treated as Mocha flag except when 'esm' is preloaded if (mochaArgs.require && mochaArgs.require.includes('esm')) {

nodeArgs.require = ['esm'];
mochaArgs.require = mochaArgs.require.filter(mod => mod !== 'esm');
if (!mochaArgs.require.length) {
  delete mochaArgs.require;
}
delete mochaArgs.r;

}

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}));

}