File size: 2,062 Bytes
a6b96c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
'use strict';

/**
 * Error that carries a process exit code. CLI logic throws this instead of
 * calling process.exit() (banned by n/no-process-exit); runMain() translates it
 * into process.exitCode at the entrypoint.
 *
 * @param {number} code  exit code (default 1)
 * @param {string} [message]  optional human message; when set and code != 0 it is
 *   written to stderr by runMain before the process exits.
 */
class ExitError extends Error {
  constructor(code = 1, message) {
    super(message === undefined ? `process exit ${code}` : message);
    this.name = 'ExitError';
    this.code = code;
    // Whether runMain should print this.message to stderr (only when a real
    // message was provided, not the synthetic default).
    this.hasUserMessage = message !== undefined;
  }
}

/**
 * Run a CLI main function and translate its outcome into process.exitCode
 * (never process.exit(), so n/no-process-exit stays satisfied). Supports sync or
 * async main.
 *   - main returns a number  -> process.exitCode = that number
 *   - main throws/rejects ExitError -> process.exitCode = err.code, and if
 *       err.hasUserMessage && err.code !== 0, err.message is written to stderr
 *   - main throws/rejects anything else -> the stack is written to stderr and
 *       process.exitCode = 1
 * Letting the event loop drain (vs process.exit) means buffered stdout/stderr is
 * flushed and process.on('exit') cleanup handlers still fire.
 *
 * @param {() => (number|void|Promise<number|void>)} main
 */
function runMain(main) {
  Promise.resolve()
    .then(() => main())
    .then((code) => {
      if (typeof code === 'number') process.exitCode = code;
    })
    .catch((err) => {
      if (err instanceof ExitError) {
        if (err.hasUserMessage && err.code !== 0) {
          process.stderr.write(`${err.message}\n`);
        }
        process.exitCode = err.code;
        return;
      }
      process.stderr.write(`${err && err.stack ? err.stack : String(err)}\n`);
      process.exitCode = 1;
    });
}

module.exports = { ExitError, runMain };