All files / src/lib/compiler buildRunner.ts

82.22% Statements 37/45
53.57% Branches 15/28
100% Functions 7/7
83.72% Lines 36/43

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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 1113x 3x     3x                       3x           8x   8x 8x 8x 8x 8x 8x   8x 8x         8x 8x       1x         1x     7x 1x 1x 1x   1x                   7x 2x                   7x 4x 2x 2x 1x           1x                 8x 4x     4x 4x 4x       8x          
import Path from "path";
import { Worker } from "worker_threads";
import { BuildTaskResponse, BuildWorkerData } from "./buildWorker";
import type { BuildWorkerResult } from "./buildResult";
import { getL10NData } from "shared/lib/lang/l10n";
 
type BuilderRunnerResult = {
  kill: () => void;
  result: Promise<BuildWorkerResult>;
};
 
type BuildRunnerOptions = Omit<BuildWorkerData, "l10nData"> & {
  progress: (msg: string) => void;
  warnings: (msg: string) => void;
};
 
export const buildRunner = ({
  progress,
  warnings,
  ...options
}: BuildRunnerOptions): BuilderRunnerResult => {
  let worker: Worker | undefined;
  let cancelling = false;
 
  const buildResult = new Promise<BuildWorkerResult>((resolve) => {
    let settled = false;
    const resolveResult = (result: BuildWorkerResult) => {
      Iif (settled) return;
      settled = true;
      resolve(result);
    };
    const workerPath = Path.resolve(__dirname, "./buildWorker.js");
    const workerData: BuildWorkerData = {
      ...options,
      l10nData: getL10NData(),
    };
 
    try {
      worker = new Worker(workerPath, {
        workerData,
      });
    } catch (error) {
      resolveResult({
        status: "failed",
        stage: "prepare",
        error: error instanceof Error ? error.toString() : String(error),
      });
      return;
    }
 
    worker.on("message", (message: BuildTaskResponse) => {
      Eif (cancelling) {
        Eif (message.action === "complete") {
          resolveResult({ status: "cancelled" });
        }
        return;
      }
      if (message.action === "progress") {
        progress(message.payload.message);
      } else if (message.action === "warning") {
        warnings(message.payload.message);
      } else if (message.action === "complete") {
        resolveResult(message.payload);
      }
    });
    worker.on("error", (error) => {
      resolveResult(
        cancelling
          ? { status: "cancelled" }
          : {
              status: "failed",
              stage: "prepare",
              error: error.toString(),
            },
      );
    });
    worker.on("exit", (code) => {
      if (cancelling) {
        resolveResult({ status: "cancelled" });
      } else if (code !== 0) {
        resolveResult({
          status: "failed",
          stage: "prepare",
          error: `Build worker exited with code ${code ?? 1}`,
        });
      } else {
        resolveResult({
          status: "failed",
          stage: "prepare",
          error: "Build worker exited before returning a result",
        });
      }
    });
  });
 
  const kill = () => {
    Iif (cancelling) {
      return;
    }
    cancelling = true;
    Eif (worker) {
      worker.postMessage({ action: "terminate" });
    }
  };
 
  return {
    kill,
    result: buildResult,
  };
};