UNPKG

execli

Version:

Generate task-oriented CLIs declaratively

90 lines 3.16 kB
import { EOL } from "node:os"; import { relative } from "node:path"; import { cwd, platform, stderr, stdout } from "node:process"; import { execa, } from "execa"; import { hideSecrets } from "./context.js"; const isExecaError = (error) => error instanceof Error && "command" in error && "isCanceled" in error; export class ExecError extends Error { static fromExecaError({ shortMessage: error, stderr, stdout }) { return new ExecError({ error, stderr, stdout, }); } error; stderr; stdout; constructor({ error, stderr, stdout, }) { super("Command failed"); this.error = error; this.stderr = stderr.trim(); this.stdout = stdout.trim(); } toDetailedError({ withOutputs }) { return new Error([ this.error, ...(withOutputs ? [ ...(this.stdout.length > 0 ? ["STDOUT:", this.stdout] : []), ...(this.stderr.length > 0 ? ["STDERR:", this.stderr] : []), ] : []), ].join(EOL)); } } export const createSubprocess = (command, context, options = {}) => { const [file, ...arguments_] = command; try { const subprocess = execa(file, arguments_, options); if (context.concurrency === 0 && !options.silent) { if (subprocess.stdout) { subprocess.stdout.pipe(stdout); } if (subprocess.stderr) { subprocess.stderr.pipe(stderr); } } return subprocess; } catch (error) { if (isExecaError(error)) { throw ExecError.fromExecaError(error); } throw error; } }; const getEnvironmentString = ({ env }) => { if (env === undefined || Object.keys(env).length === 0) { return ""; } return `${Object.entries(env) .map(([key, value]) => `${platform === "win32" ? "SET " : ""}${key}=${value ?? "false"}`) .join(platform === "win32" ? "&&" : " ")}${platform === "win32" ? "&&" : " "}`; }; const getCommandRelativeDirectory = (commandDirectory) => { if (commandDirectory instanceof URL) { throw new TypeError(`Directories of type URL are not supported: ${commandDirectory}`); } return relative(cwd(), commandDirectory); }; export const getCommandString = (command, context, options = {}) => hideSecrets(context, `${options.cwd ? `cd ${getCommandRelativeDirectory(options.cwd)} && ` : ""}${getEnvironmentString(options)}${command .map((part) => (part.includes(" ") ? `"${part}"` : part)) .join(" ")}`); export const createExec = (context, outputLine) => async (command, options = {}) => { if (!options.silent) { outputLine(`$ ${getCommandString(command, context, options)}`); } const subprocess = createSubprocess(command, context, options); try { const result = await subprocess; return result; } catch (error) { if (isExecaError(error)) { throw ExecError.fromExecaError(error); } throw error; } }; //# sourceMappingURL=exec.js.map