UNPKG

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