@travetto/pack
Version:
Code packing utilities
96 lines (81 loc) • 3.29 kB
text/typescript
import fs from 'node:fs/promises';
import { spawn, type SpawnOptions } from 'node:child_process';
import path from 'node:path';
import { AppError, ExecUtil, Runtime, RuntimeIndex } from '@travetto/runtime';
import { ActiveShellCommand } from './shell.ts';
export class PackUtil {
/**
* Generate .env file
*/
static buildEnvFile(env: Record<string, string | number | boolean | undefined>): string[] {
return Object.entries(env)
.filter(([, value]) => (value !== undefined))
.map(([key, value]) => `${key}=${value}`);
}
/**
* Remove directory, determine if errors should be ignored
* @param sourceDirectory The folder to copy
* @param destinationDirectory The folder to copy to
*/
static async copyRecursive(sourceDirectory: string, destinationDirectory: string, inclusive: boolean = false, ignoreFailure = false): Promise<void> {
try {
let final = destinationDirectory;
if (!inclusive) {
final = path.resolve(destinationDirectory, path.basename(sourceDirectory));
}
await fs.mkdir(final, { recursive: true });
await fs.cp(sourceDirectory, final, { recursive: true });
} catch {
if (!ignoreFailure) {
throw new Error(`Failed to copy ${sourceDirectory} to ${destinationDirectory}`);
}
}
}
/**
* Finalize eject output
*/
static async writeEjectOutput(workspace: string, module: string, output: AsyncIterable<string>, file: string): Promise<void> {
const repoRoot = Runtime.workspaceRelative('.');
const vars = { ROOT: path.resolve(), TRV_OUT: RuntimeIndex.outputRoot, REPO_ROOT: repoRoot, DIST: workspace, MODULE: module };
const replaceArgs = (text: string): string => Object.entries(vars)
.reduce((result, [key, value]) => result.replaceAll(value, ActiveShellCommand.var(key)), text);
const preamble = ActiveShellCommand.script(
Object.entries(vars).map(([key, value]) => ActiveShellCommand.export(key, value).join(' ')),
).contents;
let stream: fs.FileHandle | undefined;
if (!(file === '-' || file === '/dev/stdout')) {
await fs.mkdir(path.dirname(file), { recursive: true });
await fs.writeFile(file, '', 'utf8');
stream = await fs.open(file, 'w', 0o755);
}
const write = (text: string): Promise<unknown> | unknown => stream ? stream.write(`${text}\n`) : process.stdout.write(`${text}\n`);
for (const line of preamble) {
write(line);
}
for await (const line of output) {
await write(replaceArgs(line));
}
await write('\n');
await stream?.close();
}
/**
* Track result response
*/
static async runCommand(cmd: string[], options: SpawnOptions = {}): Promise<string> {
const { valid, code, stderr, message, stdout } = await ExecUtil.getResult(spawn(cmd[0], cmd.slice(1), {
stdio: [0, 'pipe', 'pipe'],
...options,
}), { catch: true });
if (!valid) {
process.exitCode = code;
throw new AppError(stderr || message || 'An unexpected error has occurred');
}
return stdout;
}
/**
* Write a file directly
*/
static async writeRawFile(file: string, contents: string[], mode?: string): Promise<void> {
await fs.writeFile(file, contents.join('\n'), { encoding: 'utf8', mode });
}
}