@sap/cli-core
Version:
Command-Line Interface (CLI) Core Module
178 lines (177 loc) • 7.05 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildCommand = exports.buildOption = exports.registerLongName = exports.isOptionAlreadyRegistered = exports.getShortFlagForLongName = exports.createCommand = void 0;
exports.getAbsoluteCommandName = getAbsoluteCommandName;
const commander_1 = require("commander");
const lodash_1 = __importDefault(require("lodash"));
const types_1 = require("../types");
const root_1 = require("../commands/handler/root");
const logger_1 = require("../logger");
const registeredOptions = {};
const getLogger = () => (0, logger_1.get)("utils.commands");
const checkEmpty = (name, value) => {
if (value.trim() === "") {
throw new Error(`missing value for parameter ${name}`);
}
};
const checkCommand = (command) => {
const { command: name, description } = command;
try {
checkEmpty("command", name);
checkEmpty("description", description);
}
catch (err) {
const { trace } = getLogger();
trace(`failed to check command ${JSON.stringify(command, null, 2)} because of`, err);
throw err;
}
};
const createCommand = (name) => {
const { output } = getLogger();
const command = new commander_1.Command(name);
command.enablePositionalOptions();
command.exitOverride();
command.configureOutput({ writeErr: output, writeOut: output });
command.allowUnknownOption();
return command;
};
exports.createCommand = createCommand;
function getAbsoluteCommandName(command) {
if (!command.parent) {
return command.name();
}
return `${getAbsoluteCommandName(command.parent)}.${command.name()}`;
}
const getShortFlagForLongName = (command, option) => {
const commandName = getAbsoluteCommandName(command);
for (const s of Object.keys(registeredOptions[commandName] ?? {})) {
if (registeredOptions[commandName][s] === option.longName) {
return s;
}
}
throw new Error(`no short flag registered for command ${commandName}, long name ${option.longName}`);
};
exports.getShortFlagForLongName = getShortFlagForLongName;
const isOptionAlreadyRegistered = ({ longName }, command) => {
const opts = command.options?.map((o) => o.long?.replace("--", ""));
return !!opts?.includes(longName);
};
exports.isOptionAlreadyRegistered = isOptionAlreadyRegistered;
function shortFlagFromAnyCommand(optionLongName) {
let shortFlag;
Object.keys(registeredOptions).find((c) => {
shortFlag = Object.entries(registeredOptions[c]).find(([_command, longName]) => optionLongName === longName)?.[0];
return !!shortFlag;
});
return shortFlag;
}
function removeUsedShortFlags(possibleShortFlags) {
const usedShortFlags = lodash_1.default.uniq(Object.keys(registeredOptions).reduce((p1, c1) => {
return p1.concat(Object.keys(registeredOptions[c1]));
}, []));
return possibleShortFlags.filter((s) => !usedShortFlags.includes(s));
}
const getInitialShortFlagOptions = (option) => {
return [
option.longName[0],
option.longName[0].toUpperCase() === option.longName[0]
? option.longName[0].toLowerCase()
: option.longName[0].toUpperCase(),
...types_1.CHARACTERS,
];
};
const registerLongName = (command, option) => {
try {
return (0, exports.getShortFlagForLongName)(command, option);
}
catch (err) {
let possibleShortFlags = getInitialShortFlagOptions(option);
possibleShortFlags = removeUsedShortFlags(possibleShortFlags);
if (possibleShortFlags.length === 0) {
// no short flag is left (possibleShortFlags.length === 0),
// fall back to the initial set of options
possibleShortFlags = getInitialShortFlagOptions(option);
}
const alreadyUsedShortFlag = shortFlagFromAnyCommand(option.longName);
if (alreadyUsedShortFlag) {
possibleShortFlags.unshift(alreadyUsedShortFlag);
}
const commandName = getAbsoluteCommandName(command);
for (const c of possibleShortFlags) {
if (!registeredOptions[commandName]?.[c]) {
registeredOptions[commandName] = registeredOptions[commandName] ?? {};
registeredOptions[commandName][c] = option.longName;
return c;
}
}
throw new Error(`no short flag found for name ${option.longName}, command ${commandName}`);
}
};
exports.registerLongName = registerLongName;
const buildOption = async (command, option) => {
const mandatory = !!option.required;
const shortFlag = (0, exports.registerLongName)(command, option);
let newDescr = option.description;
if (!mandatory) {
newDescr = newDescr ? `${newDescr} (optional)` : "(optional)";
}
const argStr = option.args
? option.args.reduce((p, c) => (c.optional ? `${p} [${c.name}]` : `${p} <${c.name}>`), "")
: "";
const cOption = new commander_1.Option(`-${shortFlag}, --${option.longName}${argStr}`, newDescr)
.makeOptionMandatory(mandatory)
.hideHelp(!!option.hidden);
if (option.default) {
const defaultStr = typeof option.default === "function"
? await option.default()
: option.default;
cOption.default(defaultStr);
}
let choicesArr = [];
if (option.choices) {
choicesArr =
typeof option.choices === "function"
? await option.choices()
: option.choices;
}
if (choicesArr.length > 0) {
cOption.choices(choicesArr);
}
return cOption;
};
exports.buildOption = buildOption;
const buildCommand = async (topCommand, command) => {
[]
.concat([command], command.type === "topCommand" ? command.subCommands : [])
.forEach((c) => checkCommand(c));
const { command: name, description, options } = command;
const comm = (0, exports.createCommand)(name);
topCommand.addCommand(comm);
comm.description(description);
comm.enablePositionalOptions();
for (const option of options ?? []) {
// eslint-disable-next-line no-await-in-loop
comm.addOption(await (0, exports.buildOption)(comm, option));
}
if (command.type === "topCommand") {
const { subCommands } = command;
for (const subCommand of subCommands) {
// eslint-disable-next-line no-await-in-loop
await (0, exports.buildCommand)(comm, subCommand);
}
}
else {
// command.type === "command"
const { args } = command;
if (args) {
args.forEach((a) => comm.argument(`<${a.argument}>`, a.description));
}
const rootHandler = await (0, root_1.create)(command, `Failed to ${command.description}`)(comm);
comm.action(rootHandler);
}
return comm;
};
exports.buildCommand = buildCommand;