avocadowastaken/rapidbundle

View on GitHub
src/tasks/bundleGitHubAction.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
import esbuild, { BuildOptions } from "esbuild";
import type TaskTree from "tasktree-cli";
import type { ActionYML } from "../manifests/ActionYML";
import { git } from "../utils/git";
import { getDistDir, resolveEntry, toModuleID } from "../utils/path";
import { runTask } from "../utils/task";
import { ValidationError } from "../utils/validation";

export async function ensureUnchanged(directory: string): Promise<void> {
  await git("add", directory);
  const diff = await git(
    "diff",
    "--cached",
    "--minimal",
    "--unified=0",
    directory
  );

  if (diff) {
    await git("reset", directory);
    const formattedDiff = diff
      .replace(/^diff --git (.+)/gm, "")
      .replace(/^\w+ file mode \d+\n/gm, "")
      .replace(/^index \w{7}\.\.\w{7}( \w{6})?\n/gm, "")
      .replace(/^(-{3}|\+{3}) \/dev\/null\n/gm, "")
      .trimStart();

    throw new ValidationError(`Found build difference:\n${formattedDiff}`);
  }
}

export async function bundleGitHubAction(
  cwd: string,
  isCI: boolean,
  tree: TaskTree,
  { runs: { main, pre, post, using } }: ActionYML
): Promise<void> {
  // TODO: Bundle each entry in separate task.
  const task = tree.add("Making bundle from 'action.yml'");

  return runTask(task, async () => {
    const distDir = getDistDir(cwd);
    const options = {
      logLevel: "silent",

      bundle: true,
      outdir: distDir,
      keepNames: true,
      platform: "node",
      absWorkingDir: cwd,
      target: using,
      entryPoints: [] as string[],

      external: [
        // Optional dependency of the `node-fetch`.
        "encoding",
      ],

      treeShaking: true,
      define: {
        "process.env.NODE_ENV": JSON.stringify("production"),
      },
    } satisfies BuildOptions;

    for (const [name, entryPath] of Object.entries({ main, pre, post })) {
      if (entryPath) {
        const entry = await resolveEntry(cwd, entryPath);
        options.entryPoints.push(entry);
        task.log(`Using '.runs.${name}' entry: ${toModuleID(entry)}`);
      }
    }

    task.log(`Using '.runs.using' entry: ${options.target}`);
    await esbuild.build(options);

    if (isCI) {
      task.log("Checking build difference");
      await ensureUnchanged(distDir);
    }
  });
}