All files / packages/icons/scripts build-previewer.ts

0% Statements 0/40
0% Branches 0/8
0% Functions 0/8
0% Lines 0/40

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 111 112 113                                                                                                                                                                                                                                 
/*
 * Builds the icons previewer (Next.js static export).
 *
 * The `app/workflow` and `app/api/workflow` routes cannot be part of this build
 * (they require a server runtime and break the static export), so this script
 * temporarily moves them out of the tree, runs `next build`, and then always
 * restores them — even if the build fails.
 */
import { spawn } from 'node:child_process';
import fs from 'node:fs/promises';
import path from 'node:path';
 
const rootDir = path.resolve(import.meta.dirname, '..');
const backupRoot = path.join(rootDir, '.workflow-build-backup');
 
const workflowTargets = [path.join(rootDir, 'app', 'workflow'), path.join(rootDir, 'app', 'api', 'workflow')];
 
/*
 * Maps a target folder to its flat, collision-free stash location under
 * backupRoot (e.g. app/workflow -> .workflow-build-backup/app__workflow).
 * Pure path computation — does no file I/O.
 */
function backupPath(targetPath: string): string {
  const relative = path.relative(rootDir, targetPath).replace(/[\\/]/g, '__');
  return path.join(backupRoot, relative);
}
 
/* Returns true if the path exists on disk, false otherwise (never throws). */
async function pathExists(targetPath: string): Promise<boolean> {
  try {
    await fs.access(targetPath);
    return true;
  } catch {
    return false;
  }
}
 
/*
 * Moves each existing workflow route folder out to its backup location so it is
 * excluded from the build. Returns the list of targets that were moved, for
 * restoreWorkflowRoutes to reverse.
 */
async function disableWorkflowRoutes(): Promise<string[]> {
  const moved: string[] = [];
  await fs.mkdir(backupRoot, { recursive: true });
  for (const target of workflowTargets) {
    const backup = backupPath(target);
    if (!(await pathExists(target))) {
      continue;
    }
    await fs.rm(backup, { recursive: true, force: true });
    await fs.rename(target, backup);
    moved.push(target);
  }
  return moved;
}
 
/*
 * Moves the previously stashed folders back to their original paths and removes
 * the backup root. Safe to call even if the build failed partway through.
 */
async function restoreWorkflowRoutes(movedTargets: string[]): Promise<void> {
  for (const target of movedTargets.reverse()) {
    const backup = backupPath(target);
    if (!(await pathExists(backup))) {
      continue;
    }
    await fs.rm(target, { recursive: true, force: true });
    await fs.mkdir(path.dirname(target), { recursive: true });
    await fs.rename(backup, target);
  }
  await fs.rm(backupRoot, { recursive: true, force: true });
}
 
/*
 * Runs `next build` in the package root, resolving on exit code 0 and rejecting
 * on spawn error or any non-zero exit code.
 */
function runNextBuild(): Promise<void> {
  return new Promise((resolve, reject) => {
    const child = spawn('next', ['build'], {
      cwd: rootDir,
      stdio: 'inherit',
      shell: true,
      env: process.env,
    });
 
    child.on('error', reject);
    child.on('exit', (code) => {
      if (code === 0) {
        resolve();
        return;
      }
      reject(new Error(`next build failed with exit code ${code ?? 'unknown'}`));
    });
  });
}
 
/*
 * Orchestrates the previewer build: disable the workflow routes, run the Next.js
 * build, then always restore the routes (even if the build throws).
 */
async function main(): Promise<void> {
  const movedTargets = await disableWorkflowRoutes();
  try {
    await runNextBuild();
  } finally {
    await restoreWorkflowRoutes(movedTargets);
  }
}
 
await main();