UNPKG

@sap/cli-core

Version:

Command-Line Interface (CLI) Core Module

178 lines (177 loc) 7.05 kB
"use strict"; 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;