@mail-core/cli
Version:
Инструментарий для написания cli-скриптов
212 lines • 8.02 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCLI = exports.execCommand = exports.getActiveConsole = exports.createCommand = void 0;
const fs_1 = require("fs");
const color_1 = require("../color");
const path_1 = require("path");
const yargs = require("yargs");
const interactive_1 = require("../interactive/interactive");
const exit_1 = require("../process/exit");
const console_1 = require("../console/console");
const spinner_1 = require("../console/spinner");
const pkg_1 = require("../pkg");
const boxen = require("boxen");
const summary_1 = require("../process/summary");
const Chalk = require("chalk");
const { command, parse, demandCommand, recommendCommands } = yargs;
const { strictCommands } = yargs;
const defaultActiveConsole = console_1.createPackageExtendedConsole(__dirname, process.argv.includes('--yes'));
let activeCommand = null;
let activeCommandArgv = null;
let activeCommandEnv = null;
function createCommand(cmd) {
console_1.verbose(`[createCommand] ${cmd.name} (${cmd.describe})`);
return cmd;
}
exports.createCommand = createCommand;
function getActiveConsole() {
return (activeCommandEnv === null || activeCommandEnv === void 0 ? void 0 : activeCommandEnv.console) || defaultActiveConsole;
}
exports.getActiveConsole = getActiveConsole;
async function execCommand(cmd, argv) {
if (Array.isArray(argv)) {
console_1.verbose(`[execCommand.raw.arv] ${cmd.name} (${cmd.describe}):`, argv);
argv = parse(argv);
}
console_1.verbose(`[execCommand] ${cmd.name} (${cmd.describe}):`, argv);
const env = createEnv(argv, cmd);
cmd.check && cmd.check(argv);
await cmd.handler(argv, env);
}
exports.execCommand = execCommand;
function printErrorReason(console, reason) {
console.hr();
if (reason instanceof Error) {
console.verbose('error:', reason);
console.raw.error(color_1.bold.red(reason.stack || `${reason.name}: ${reason.message}`));
}
else if (reason && reason.stderr) {
if (reason.stdout) {
console.raw.log(reason.stdout);
console.hr();
}
console.raw.error(color_1.red(reason.stderr));
process.exit(reason.code || 1);
}
else {
console.raw.error(typeof reason === 'string' ? color_1.red(reason) : reason);
}
}
function initCLI(env, ...scheme) {
const pkg = pkg_1.readPackageJson(env.__dirname);
const showProcessSummary = summary_1.startProcessSummary();
console_1.verbose(`[initCLI] env:`, env, scheme);
console_1.verbose(`[initCLI] process.version:`, process.version);
// v12+
if (!/^v([0-9]+[2-9]|[2-9][0-9]+)/.test(process.version)) {
console.warn(boxen(color_1.bold.yellow(`${color_1.underline(pkg.name)}: NodeJS ${process.version} not supported, try: \`nvm use 12\``), { padding: 1, borderStyle: 'bold', borderColor: 'yellow', margin: 1 }));
}
scheme.forEach(cmdOrMap => {
if (isCommandDescriptor(cmdOrMap)) {
initCommand(cmdOrMap);
}
else {
Object.entries(cmdOrMap).forEach(([scope, list]) => {
list.forEach((cmd) => {
initCommand(cmd, scope);
});
});
}
});
demandCommand(1, '');
recommendCommands();
strictCommands();
exit_1.addProcessExitListener((evt) => {
const console = getActiveConsole();
const exit = () => {
if (evt.type === 'uncaughtException') {
console.verbose('uncaughtException:', evt.reason);
showProcessSummary();
printErrorReason(console, evt.reason);
process.exit(1);
}
else if (evt.type !== 'exit') {
console.verbose(`Command exit by "${evt.type}": ${evt.signal}`);
showProcessSummary();
process.exit();
}
else {
showProcessSummary();
}
};
if (activeCommand && activeCommand.exitHandler && activeCommandArgv && activeCommandEnv) {
const val = activeCommand.exitHandler(evt, activeCommandArgv, activeCommandEnv);
val && val
.catch((reason) => {
printErrorReason(console, reason);
})
.then(() => {
showProcessSummary();
process.exit(0);
});
}
else {
exit();
}
});
return parse();
}
exports.initCLI = initCLI;
function isCommandDescriptor(x) {
return typeof x === 'object' && !!x && ('options' in x);
}
function initCommand(cmd, scope) {
console_1.verbose(`[initCommand] ${cmd.name} (${cmd.describe}), scope: ${scope}`);
const options = Object.entries(cmd.options);
const name = (scope ? `${scope}-${cmd.name}` : cmd.name) + options.reduce((list, [name, opt]) => {
if ('positional' in opt) {
if (opt.demandOption) {
return `${list} <${name}${opt.array ? '..' : ''}>`;
}
return `${list} [${name}${opt.array ? '..' : ''}]`;
}
return list;
}, '');
command({
command: name,
aliases: cmd.aliases,
builder(yargs) {
options.forEach(([name, opt]) => {
if ('positional' in opt) {
yargs.positional(name, opt);
}
else {
yargs.option(name, opt);
}
});
return yargs
.check(cmd.check || (() => true))
.alias('help', 'h');
},
describe: cmd.hidden ? undefined : cmd.describe || '<<description undefined>>',
async handler(argv) {
const env = createEnv(argv, cmd);
env.console.verbose(`[handler] ${cmd.name} (${cmd.describe}):`, argv);
activeCommand = cmd;
activeCommandArgv = argv;
activeCommandEnv = env;
try {
await interactiveOptions(argv, options);
await cmd.handler(argv, env);
}
catch (err) {
spinner_1.stopAllSpinners('fail');
env.console.fail(`Command "${cmd.name}" failed`);
printErrorReason(env.console, err);
throw err;
}
},
});
}
async function interactiveOptions(argv, options) {
for (const [key, opt] of options) {
const type = opt.type === 'string' ? 'input' : 'confirm';
if (opt.interactive && !argv[key]) {
do {
const { value } = await interactive_1.interactive({
value: {
message: `Enter "${opt.describe || key}":`,
type,
value: opt.default,
},
});
argv[key] = value;
} while (type === 'input' && !argv[key]);
}
}
}
function createEnv(argv, cmd) {
let cwd = process.cwd();
if (argv.$0) {
if (fs_1.existsSync(argv.$0)) {
cwd = fs_1.realpathSync(argv.$0);
}
else {
cwd = fs_1.realpathSync(argv.$0.split(path_1.sep).slice(0, -1).join(path_1.sep));
}
}
console_1.verbose(`[createEnv] ${cmd.name} (${cmd.describe}): cwd = ${cwd}`);
const console = console_1.createPackageExtendedConsole(cwd ? fs_1.realpathSync(cwd) : cwd, !!argv.yes);
const options = Object
.entries(cmd.options)
.reduce((map, [name, props]) => (Object.assign(Object.assign({}, map), { [name]: Object.assign(Object.assign({}, props), { name, value: argv[name] }) })), {});
;
return Object.assign(Object.assign({ style: Chalk, console,
exit(code) {
process.exit(code);
}, fail(err) {
console.fail(`→ ${cmd.describe} failed:`, err);
process.exit(1);
} }, cmd), { options });
}
//# sourceMappingURL=command.js.map