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 |
|
12 | const {deprecate, warn} = require('../lib/utils');
|
13 | const {spawn} = require('child_process');
|
14 | const {loadOptions} = require('../lib/cli/options');
|
15 | const {
|
16 | unparseNodeFlags,
|
17 | isNodeFlag,
|
18 | impliesNoTimeouts
|
19 | } = require('../lib/cli/node-flags');
|
20 | const unparse = require('yargs-unparser');
|
21 | const debug = require('debug')('mocha:cli:mocha');
|
22 | const {aliases} = require('../lib/cli/run-option-metadata');
|
23 | const nodeEnv = require('node-environment-flags');
|
24 |
|
25 | const mochaPath = require.resolve('./_mocha');
|
26 | const mochaArgs = {};
|
27 | const nodeArgs = {};
|
28 |
|
29 | const opts = loadOptions(process.argv.slice(2));
|
30 | debug('loaded opts', opts);
|
31 |
|
32 | /**
|
33 | * Given option/command `value`, disable timeouts if applicable
|
34 | * @param {string} [value] - Value to check
|
35 | * @ignore
|
36 | */
|
37 | const 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 | */
|
52 | const trimV8Option = value =>
|
53 | value !== 'v8-options' && /^v8-/.test(value) ? value.slice(3) : value;
|
54 |
|
55 | // sort options into "node" and "mocha" buckets
|
56 | Object.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 --)
|
72 | if (/^(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
|
113 | if (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 |
|
121 | debug('final node args', nodeArgs);
|
122 |
|
123 | const args = [].concat(
|
124 | unparseNodeFlags(nodeArgs),
|
125 | mochaPath,
|
126 | unparse(mochaArgs, {alias: aliases})
|
127 | );
|
128 |
|
129 | debug(`exec ${process.execPath} w/ args:`, args);
|
130 |
|
131 | const proc = spawn(process.execPath, args, {
|
132 | stdio: 'inherit'
|
133 | });
|
134 |
|
135 | proc.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.
|
146 | process.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 | });
|