auto-print / files / src / util.ts
util.ts
a6d0aed2 2026-10-24 Pavel Safronov | 1 | // Small helpers shared by the other modules. |
| 2 | ||
| 3 | import { spawnSync } from "node:child_process"; | |
| 4 | import path from "node:path"; | |
| 5 | ||
| 6 | export function fail(message: string): never { | |
| 7 | throw new Error(message); | |
| 8 | } | |
| 9 | ||
| 10 | /** Runs a command and returns its output. Fails with the command's error output if it does not succeed. */ | |
| 11 | export function run(command: string, args: string[], input?: string, timeoutMs = 180_000): string { | |
| 12 | const result = spawnSync(command, args, { input, encoding: "utf8", timeout: timeoutMs, maxBuffer: 64 * 1024 * 1024 }); | |
| 13 | if (result.error) { | |
| 14 | const code = (result.error as NodeJS.ErrnoException).code; | |
| 15 | if (code === "ENOENT") fail(`"${command}" is not installed or not on the PATH.`); | |
| 16 | fail(`${path.basename(command)} failed: ${result.error.message}`); | |
| 17 | } | |
| 18 | if (result.status !== 0) { | |
| 19 | fail(`${path.basename(command)} exited with code ${result.status}:\n${result.stderr.trim()}`); | |
| 20 | } | |
| 21 | return result.stdout; | |
| 22 | } | |
| 23 | ||
| 24 | /** How a file is named in messages: relative to the current folder when it is inside it, otherwise in full. */ | |
| 25 | export function displayPath(filePath: string): string { | |
| 26 | const relative = path.relative(process.cwd(), filePath); | |
| 27 | return relative && !relative.startsWith("..") && !path.isAbsolute(relative) ? relative : filePath; | |
| 28 | } | |
| 29 | ||
| 30 | export function sleep(milliseconds: number): Promise<void> { | |
| 31 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); | |
| 32 | } |