213 lines
5.7 KiB
TypeScript
213 lines
5.7 KiB
TypeScript
import { getCliConfigValue } from "./helpers.smartconfig.js";
|
|
|
|
export type TCliOutputMode = "human" | "plain" | "json";
|
|
|
|
export interface ICliMode {
|
|
output: TCliOutputMode;
|
|
interactive: boolean;
|
|
json: boolean;
|
|
plain: boolean;
|
|
quiet: boolean;
|
|
yes: boolean;
|
|
help: boolean;
|
|
agent: boolean;
|
|
checkUpdates: boolean;
|
|
isTty: boolean;
|
|
command?: string;
|
|
}
|
|
|
|
interface ICliConfigSettings {
|
|
interactive?: boolean;
|
|
output?: TCliOutputMode;
|
|
checkUpdates?: boolean;
|
|
}
|
|
|
|
type TArgSource = Record<string, any> & { _?: string[] };
|
|
|
|
const camelCase = (value: string): string => {
|
|
return value.replace(/-([a-z])/g, (_match, group: string) =>
|
|
group.toUpperCase(),
|
|
);
|
|
};
|
|
|
|
const getArgValue = (argvArg: TArgSource, key: string): any => {
|
|
const keyVariants = [key, camelCase(key), key.replace(/-/g, "")];
|
|
for (const keyVariant of keyVariants) {
|
|
if (argvArg[keyVariant] !== undefined) {
|
|
return argvArg[keyVariant];
|
|
}
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const parseRawArgv = (argv: string[]): TArgSource => {
|
|
const parsedArgv: TArgSource = { _: [] };
|
|
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const currentArg = argv[i];
|
|
|
|
if (currentArg.startsWith("--no-")) {
|
|
const key = currentArg.slice(5);
|
|
parsedArgv[key] = false;
|
|
parsedArgv[camelCase(key)] = false;
|
|
continue;
|
|
}
|
|
|
|
if (currentArg.startsWith("--")) {
|
|
const withoutPrefix = currentArg.slice(2);
|
|
const [rawKey, inlineValue] = withoutPrefix.split("=", 2);
|
|
if (inlineValue !== undefined) {
|
|
parsedArgv[rawKey] = inlineValue;
|
|
parsedArgv[camelCase(rawKey)] = inlineValue;
|
|
continue;
|
|
}
|
|
|
|
const nextArg = argv[i + 1];
|
|
if (nextArg && !nextArg.startsWith("-")) {
|
|
parsedArgv[rawKey] = nextArg;
|
|
parsedArgv[camelCase(rawKey)] = nextArg;
|
|
i++;
|
|
} else {
|
|
parsedArgv[rawKey] = true;
|
|
parsedArgv[camelCase(rawKey)] = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (currentArg.startsWith("-") && currentArg.length > 1) {
|
|
for (const shortFlag of currentArg.slice(1).split("")) {
|
|
parsedArgv[shortFlag] = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
parsedArgv._ = parsedArgv._ || [];
|
|
parsedArgv._.push(currentArg);
|
|
}
|
|
|
|
return parsedArgv;
|
|
};
|
|
|
|
const normalizeOutputMode = (value: unknown): TCliOutputMode | undefined => {
|
|
if (value === "human" || value === "plain" || value === "json") {
|
|
return value;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
const resolveCliMode = (
|
|
argvArg: TArgSource,
|
|
cliConfig: ICliConfigSettings,
|
|
): ICliMode => {
|
|
const isTty = Boolean(process.stdout?.isTTY && process.stdin?.isTTY);
|
|
const agentMode = Boolean(getArgValue(argvArg, "agent"));
|
|
const outputOverride = normalizeOutputMode(getArgValue(argvArg, "output"));
|
|
|
|
let output: TCliOutputMode =
|
|
normalizeOutputMode(cliConfig.output) || (isTty ? "human" : "plain");
|
|
if (agentMode || getArgValue(argvArg, "json")) {
|
|
output = "json";
|
|
} else if (getArgValue(argvArg, "plain")) {
|
|
output = "plain";
|
|
} else if (outputOverride) {
|
|
output = outputOverride;
|
|
}
|
|
|
|
const interactiveSetting = getArgValue(argvArg, "interactive");
|
|
let interactive = cliConfig.interactive ?? isTty;
|
|
if (interactiveSetting === true) {
|
|
interactive = true;
|
|
} else if (interactiveSetting === false) {
|
|
interactive = false;
|
|
}
|
|
if (!isTty || output !== "human" || agentMode) {
|
|
interactive = false;
|
|
}
|
|
|
|
const checkUpdatesSetting = getArgValue(argvArg, "check-updates");
|
|
let checkUpdates = cliConfig.checkUpdates ?? output === "human";
|
|
if (checkUpdatesSetting === true) {
|
|
checkUpdates = true;
|
|
} else if (checkUpdatesSetting === false) {
|
|
checkUpdates = false;
|
|
}
|
|
if (output !== "human" || agentMode) {
|
|
checkUpdates = false;
|
|
}
|
|
|
|
return {
|
|
output,
|
|
interactive,
|
|
json: output === "json",
|
|
plain: output === "plain",
|
|
quiet: Boolean(
|
|
getArgValue(argvArg, "quiet") ||
|
|
getArgValue(argvArg, "q") ||
|
|
output === "json",
|
|
),
|
|
yes: Boolean(getArgValue(argvArg, "yes") || getArgValue(argvArg, "y")),
|
|
help: Boolean(
|
|
getArgValue(argvArg, "help") ||
|
|
getArgValue(argvArg, "h") ||
|
|
argvArg._?.[0] === "help",
|
|
),
|
|
agent: agentMode,
|
|
checkUpdates,
|
|
isTty,
|
|
command: argvArg._?.[0],
|
|
};
|
|
};
|
|
|
|
const getCliModeConfig = async (): Promise<ICliConfigSettings> => {
|
|
return await getCliConfigValue<ICliConfigSettings>("cli", {});
|
|
};
|
|
|
|
export const getCliMode = async (
|
|
argvArg: TArgSource = {},
|
|
): Promise<ICliMode> => {
|
|
const cliConfig = await getCliModeConfig();
|
|
return resolveCliMode(argvArg, cliConfig);
|
|
};
|
|
|
|
export const getRawCliMode = async (): Promise<ICliMode> => {
|
|
const cliConfig = await getCliModeConfig();
|
|
const rawArgv = parseRawArgv(process.argv.slice(2));
|
|
return resolveCliMode(rawArgv, cliConfig);
|
|
};
|
|
|
|
export const printJson = (data: unknown): void => {
|
|
console.log(JSON.stringify(data, null, 2));
|
|
};
|
|
|
|
export const runWithSuppressedOutput = async <T>(
|
|
fn: () => Promise<T>,
|
|
): Promise<T> => {
|
|
const originalConsole = {
|
|
log: console.log,
|
|
info: console.info,
|
|
warn: console.warn,
|
|
error: console.error,
|
|
};
|
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
const noop = () => undefined;
|
|
|
|
console.log = noop;
|
|
console.info = noop;
|
|
console.warn = noop;
|
|
console.error = noop;
|
|
process.stdout.write = (() => true) as typeof process.stdout.write;
|
|
process.stderr.write = (() => true) as typeof process.stderr.write;
|
|
|
|
try {
|
|
return await fn();
|
|
} finally {
|
|
console.log = originalConsole.log;
|
|
console.info = originalConsole.info;
|
|
console.warn = originalConsole.warn;
|
|
console.error = originalConsole.error;
|
|
process.stdout.write = originalStdoutWrite;
|
|
process.stderr.write = originalStderrWrite;
|
|
}
|
|
};
|