All files / bin fatal.js

89.87% Statements 71/79
75% Branches 6/8
75% Functions 3/4
89.87% Lines 71/79

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 26x 26x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 252x 252x 252x 252x 252x 252x 26x 26x 26x 25x 25x 1x 1x 1x   26x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x 252x                
import { red, gray, bold, yellow } from './colors.js';
 
/**
 * Marks the process as failed.
 *
 * `process.exitCode` is set rather than calling `process.exit()`, because
 * `process.exit()` tears the process down immediately and can truncate stdout
 * and stderr that have not flushed yet — which in CI means a build that failed
 * without printing the reason. Setting the code lets Node exit normally once
 * the output has drained.
 * @param {number} [code] - The exit code to fail with.
 */
export function failProcess(code = 1) {
  process.exitCode = code;
}
 
/**
 * Reports a fatal error and marks the process as failed.
 *
 * Errors carrying an Avenx code (AVX_C03, AVX_W03 and friends) are diagnosed
 * conditions: the message already names the file, explains the problem and
 * often carries a code frame, so it is printed on its own. A stack trace there
 * would only point at the line of the compiler that raised it.
 *
 * Anything else is a bug rather than a diagnosis, so the stack is printed —
 * that is the only useful information such an error carries.
 * @param {Error|any} error - The failure.
 * @param {string} [action] - What was being attempted, for the headline.
 */
export function reportFatal(error, action = 'Build') {
  console.error('');
  console.error(bold(red(`✖ ${action} failed`)));
  console.error('');
 
  console.error(red(describe(error)));
 
  console.error('');
  console.error(gray('The command exits with a non-zero status.'));
 
  failProcess(1);
}
 
/**
 * Formats a failure for display, without the stack for diagnosed errors.
 * @param {Error|any} error - The failure.
 * @returns {string} The text to print.
 */
function describe(error) {
  const isDiagnosed = Boolean(error && typeof error.code === 'string' && error.code.startsWith('AVX_'));
  if (isDiagnosed) {
    return error.message;
  }
  if (error instanceof Error) {
    return error.stack || error.message;
  }
  return String(error);
}
 
/**
 * Reports a failed rebuild inside a watch loop.
 *
 * A watch session is interactive and long-running: the next save usually fixes
 * the problem. So the error is shown and watching continues, and crucially the
 * process exit code is left alone — a typo at 11am must not make the eventual
 * Ctrl-C report failure.
 *
 * This is the one place a build error does not fail the process, and it is
 * safe precisely because `avenx serve` and `avenx watch` never gate a
 * deployment. `avenx build` is unaffected.
 * @param {Error|any} error - The failure.
 */
export function reportRebuildFailure(error) {
  console.error('');
  console.error(bold(yellow('✖ Rebuild failed — the previous output is still in place')));
  console.error('');
  console.error(red(describe(error)));
  console.error('');
  console.error(gray('Watching for changes...'));
}