All files / utils ExecFailure.ts

100% Statements 12/12
100% Branches 22/22
100% Functions 3/3
100% Lines 10/10

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                                      58x 58x 1x             57x                             57x 11x   46x 6x         114x 11x          
import IExecFailure from "./types/IExecFailure";
 
/**
 * Reads a failed `execFileSync` / `execSync` / promisified `execFile` from the
 * `unknown` its `catch` receives.
 *
 * Every caller needs the same three facts -- the captured streams and the exit
 * status -- and each used to assert them into existence with its own inline
 * cast (`error as { stderr?: string; ... }`), each in its own shape, or reach
 * them through `any`. This narrows instead of asserting: a field is read
 * only if the value has it, as text, so a non-Error throw or a Buffer stream
 * is handled rather than assumed away (#1489).
 *
 * Which stream a caller reports first stays with the caller. That is the tool's
 * policy -- flawfinder writes findings to stdout, cppcheck to stderr -- not a
 * fact about the failure.
 */
class ExecFailure {
  static of(error: unknown): IExecFailure {
    const message = error instanceof Error ? error.message : String(error);
    if (typeof error !== "object" || error === null) {
      return {
        message,
        stdout: undefined,
        stderr: undefined,
        status: undefined,
      };
    }
    return {
      message,
      stdout: ExecFailure.text("stdout" in error ? error.stdout : undefined),
      stderr: ExecFailure.text("stderr" in error ? error.stderr : undefined),
      status: ExecFailure.exitCode(error),
    };
  }
 
  /**
   * `execFileSync` and `execSync` report the exit code as `status`; callback
   * and promisified `execFile` report it as `code`. Both put a string there
   * instead (`ENOENT`) when the spawn itself failed, and the sync form sets
   * `status` to null, so only a number is an exit code.
   */
  private static exitCode(error: object): number | undefined {
    if ("status" in error && typeof error.status === "number") {
      return error.status;
    }
    if ("code" in error && typeof error.code === "number") return error.code;
    return undefined;
  }
 
  /** A captured stream is a string, or a Buffer when no `encoding` was set. */
  private static text(stream: unknown): string | undefined {
    if (typeof stream === "string") return stream;
    return Buffer.isBuffer(stream) ? stream.toString("utf-8") : undefined;
  }
}
 
export default ExecFailure;