62 lines
1.5 KiB
TypeScript
62 lines
1.5 KiB
TypeScript
import z, { ZodError } from "zod";
|
|
|
|
type RunnableCommand = {
|
|
run?: (...args: any[]) => unknown;
|
|
subCommands?: unknown;
|
|
};
|
|
|
|
const WrappedCommand = Symbol("wrappedCommand");
|
|
|
|
type WrappedCommand = RunnableCommand & {
|
|
[WrappedCommand]?: true;
|
|
};
|
|
|
|
type ErrorHandler = (error: unknown) => void;
|
|
|
|
export function formatUnknownError(error: unknown): string {
|
|
if (error instanceof ZodError) return z.prettifyError(error);
|
|
|
|
if (error instanceof Error) {
|
|
const stack = error.stack?.split("\n").slice(0, 3).join("\n");
|
|
return stack && stack.trim().length > 0 ? stack : error.message;
|
|
}
|
|
|
|
try {
|
|
return JSON.stringify(error, null, 2);
|
|
} catch {
|
|
return String(error);
|
|
}
|
|
}
|
|
|
|
function reportCommandError(error: unknown): void {
|
|
console.error(formatUnknownError(error));
|
|
process.exitCode = 1;
|
|
}
|
|
|
|
export function wrapCommandErrors(
|
|
command: RunnableCommand,
|
|
handleError: ErrorHandler = reportCommandError,
|
|
): void {
|
|
const wrapped = command as WrappedCommand;
|
|
if (wrapped[WrappedCommand]) return;
|
|
wrapped[WrappedCommand] = true;
|
|
|
|
if (command.run) {
|
|
const run = command.run;
|
|
command.run = async (...args) => {
|
|
try {
|
|
return await run(...args);
|
|
} catch (error) {
|
|
handleError(error);
|
|
}
|
|
};
|
|
}
|
|
|
|
if (!command.subCommands || typeof command.subCommands !== "object") return;
|
|
for (const subCommand of Object.values(command.subCommands)) {
|
|
if (subCommand && typeof subCommand === "object") {
|
|
wrapCommandErrors(subCommand as RunnableCommand, handleError);
|
|
}
|
|
}
|
|
}
|