execli
Version:
Generate task-oriented CLIs declaratively
90 lines • 3.16 kB
JavaScript
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