mocha 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. #!/usr/bin/env node
  2. 'use strict';
  3. /**
  4. * This wrapper executable checks for known node flags and appends them when found, before invoking the "real" _mocha(1) executable.
  5. *
  6. * @module bin/mocha
  7. * @private
  8. */
  9. const {deprecate, warn} = require('../lib/utils');
  10. const {spawn} = require('child_process');
  11. const {loadOptions} = require('../lib/cli/options');
  12. const {
  13. unparseNodeFlags,
  14. isNodeFlag,
  15. impliesNoTimeouts
  16. } = require('../lib/cli/node-flags');
  17. const unparse = require('yargs-unparser');
  18. const debug = require('debug')('mocha:cli:mocha');
  19. const {aliases} = require('../lib/cli/run-option-metadata');
  20. const nodeEnv = require('node-environment-flags');
  21. const mochaPath = require.resolve('./_mocha');
  22. const mochaArgs = {};
  23. const nodeArgs = {};
  24. const opts = loadOptions(process.argv.slice(2));
  25. debug('loaded opts', opts);
  26. /**
  27. * Given option/command `value`, disable timeouts if applicable
  28. * @param {string} [value] - Value to check
  29. * @ignore
  30. */
  31. const disableTimeouts = value => {
  32. if (impliesNoTimeouts(value)) {
  33. debug(`option "${value}" disabled timeouts`);
  34. mochaArgs.timeout = 0;
  35. delete mochaArgs.timeouts;
  36. delete mochaArgs.t;
  37. }
  38. };
  39. /**
  40. * If `value` begins with `v8-` and is not explicitly `v8-options`, remove prefix
  41. * @param {string} [value] - Value to check
  42. * @returns {string} `value` with prefix (maybe) removed
  43. * @ignore
  44. */
  45. const trimV8Option = value =>
  46. value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value;
  47. // sort options into "node" and "mocha" buckets
  48. Object.keys(opts).forEach(opt => {
  49. if (isNodeFlag(opt)) {
  50. nodeArgs[trimV8Option(opt)] = opts[opt];
  51. disableTimeouts(opt);
  52. } else {
  53. mochaArgs[opt] = opts[opt];
  54. }
  55. });
  56. // Native debugger handling
  57. // see https://nodejs.org/api/debugger.html#debugger_debugger
  58. // look for 'debug' or 'inspect' that would launch this debugger,
  59. // remove it from Mocha's opts and prepend it to Node's opts.
  60. // also coerce depending on Node.js version.
  61. // A deprecation warning will be printed by node, if applicable.
  62. // (mochaArgs._ are "positional" arguments, not prefixed with - or --)
  63. if (/^(debug|inspect)$/.test(mochaArgs._[0])) {
  64. const command = mochaArgs._.shift();
  65. disableTimeouts(command);
  66. // don't conflict with inspector
  67. ['debug', 'inspect', 'debug-brk', 'inspect-brk']
  68. .filter(opt => opt in nodeArgs || opt in mochaArgs)
  69. .forEach(opt => {
  70. warn(`command "${command}" provided; --${opt} ignored`);
  71. delete nodeArgs[opt];
  72. delete mochaArgs[opt];
  73. });
  74. nodeArgs._ = [
  75. parseInt(
  76. process.version
  77. .slice(1)
  78. .split('.')
  79. .shift(),
  80. 10
  81. ) >= 8
  82. ? 'inspect'
  83. : 'debug'
  84. ];
  85. }
  86. // allow --debug to invoke --inspect on Node.js v8 or newer.
  87. ['debug', 'debug-brk']
  88. .filter(opt => opt in nodeArgs && !nodeEnv.has(opt))
  89. .forEach(opt => {
  90. const newOpt = opt === 'debug' ? 'inspect' : 'inspect-brk';
  91. warn(
  92. `"--${opt}" is not available in Node.js ${
  93. process.version
  94. }; use "--${newOpt}" instead.`
  95. );
  96. nodeArgs[newOpt] = nodeArgs[opt];
  97. mochaArgs.timeout = false;
  98. debug(`--${opt} -> ${newOpt}`);
  99. delete nodeArgs[opt];
  100. });
  101. // historical
  102. if (nodeArgs.gc) {
  103. deprecate(
  104. '"-gc" is deprecated and will be removed from a future version of Mocha. Use "--gc-global" instead.'
  105. );
  106. nodeArgs['gc-global'] = nodeArgs.gc;
  107. delete nodeArgs.gc;
  108. }
  109. debug('final node args', nodeArgs);
  110. const args = [].concat(
  111. unparseNodeFlags(nodeArgs),
  112. mochaPath,
  113. unparse(mochaArgs, {alias: aliases})
  114. );
  115. debug(`exec ${process.execPath} w/ args:`, args);
  116. const proc = spawn(process.execPath, args, {
  117. stdio: 'inherit'
  118. });
  119. proc.on('exit', (code, signal) => {
  120. process.on('exit', () => {
  121. if (signal) {
  122. process.kill(process.pid, signal);
  123. } else {
  124. process.exit(code);
  125. }
  126. });
  127. });
  128. // terminate children.
  129. process.on('SIGINT', () => {
  130. proc.kill('SIGINT'); // calls runner.abort()
  131. proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
  132. });