appcenter-cli
Version:
Command line tool for Visual Studio App Center
189 lines (188 loc) • 8.35 kB
JavaScript
;
// Help system - displays help for categories and commands
Object.defineProperty(exports, "__esModule", { value: true });
exports.runHelp = void 0;
const _ = require("lodash");
const os = require("os");
const tty_1 = require("tty");
const chalk = require("chalk");
const debug = require("debug")("appcenter-cli:util:commandline:help");
const Table = require("cli-table3");
const option_decorators_1 = require("./option-decorators");
const interaction_1 = require("../interaction");
const misc_1 = require("../misc");
const usageConst = "Usage: ";
function runHelp(commandPrototype, commandObj) {
const commandExample = getCommandExample(commandPrototype, commandObj);
const commandHelp = getCommandHelp(commandObj);
const optionsHelpTable = getOptionsHelpTable(commandPrototype);
const commonSwitchOptionsHelpTable = getCommonSwitchOptionsHelpTable(commandPrototype);
interaction_1.out.help();
interaction_1.out.help(commandHelp);
interaction_1.out.help();
interaction_1.out.help(usageConst + chalk.bold(commandExample));
if (optionsHelpTable.length > 0) {
interaction_1.out.help();
interaction_1.out.help("Options:");
interaction_1.out.help(optionsHelpTable.toString());
}
if (commonSwitchOptionsHelpTable.length > 0) {
interaction_1.out.help();
interaction_1.out.help("Common Options (works on all commands):");
interaction_1.out.help(commonSwitchOptionsHelpTable.toString());
}
}
exports.runHelp = runHelp;
function getCommandHelp(commandObj) {
const helpString = option_decorators_1.getClassHelpText(commandObj.constructor);
return !!helpString ? helpString : "No help text for command. Dev, fix it!";
}
function toSwitchOptionHelp(option) {
return {
shortName: option.shortName ? `-${option.shortName}` : "",
longName: option.longName ? `--${option.longName}` : "",
helpText: option.helpText || "",
argName: option.hasArg ? "<arg>" : "",
};
}
function getOptionsHelpTable(commandPrototype) {
const nonCommonSwitchOpts = getSwitchOptionsHelp(commandPrototype, false);
const posOpts = getPositionalOptionsHelp(commandPrototype);
return getStyledOptionsHelpTable(nonCommonSwitchOpts.concat(posOpts));
}
function getCommonSwitchOptionsHelpTable(commandPrototype) {
const commonSwitchOpts = getSwitchOptionsHelp(commandPrototype, true);
return getStyledOptionsHelpTable(commonSwitchOpts);
}
function getStyledOptionsHelpTable(options) {
const opts = styleOptsTable(options);
// Calculate max length of the strings from the first column (switches/positional parameters) - it will be a width for the first column;
const firstColumnWidth = opts.reduce((contenderMaxWidth, optRow) => Math.max(optRow[0].length, contenderMaxWidth), 0);
// Creating a help table object
const helpTableObject = new Table(interaction_1.out.getOptionsForTwoColumnTableWithNoBorders(firstColumnWidth));
opts.forEach((opt) => helpTableObject.push(opt));
return helpTableObject;
}
function getSwitchOptionsHelp(commandPrototype, isCommon) {
const switchOptions = option_decorators_1.getOptionsDescription(commandPrototype);
const filteredSwitchOptions = filterOptionDescriptions(_.values(switchOptions), isCommon);
const options = sortOptionDescriptions(filteredSwitchOptions).map(toSwitchOptionHelp);
debug(`Command has ${options.length} switch options:`);
debug(options.map((o) => `${o.shortName}|${o.longName}`).join("/"));
return options.map((optionHelp) => [` ${switchText(optionHelp)} `, optionHelp.helpText]);
}
function toPositionalOptionHelp(option) {
return {
name: option.name,
helpText: option.helpText,
};
}
function getPositionalOptionsHelp(commandPrototype) {
const options = option_decorators_1.getPositionalOptionsDescription(commandPrototype).map(toPositionalOptionHelp);
debug(`Command has ${options.length} positional options:`);
debug(options.map((o) => o.name).join("/"));
return options.map((optionsHelp) => [` ${optionsHelp.name} `, optionsHelp.helpText]);
}
function switchText(switchOption) {
// Desired formats look like:
//
// -x
// -x|--xopt
// --xopt
// -y <arg>
// -y|--yopt <arg>
// --yopt <arg>
const start = switchOption.shortName ? [switchOption.shortName] : [" "];
const sep = switchOption.shortName && switchOption.longName ? ["|"] : [" "];
const long = switchOption.longName ? [switchOption.longName] : [];
const arg = switchOption.argName ? [" " + switchOption.argName] : [];
return start.concat(sep).concat(long).concat(arg).join("");
}
function terminalWidth() {
// If stdout is a terminal, return the width
if (tty_1.isatty(1)) {
return process.stdout.columns;
}
// Otherwise return something useful.
return 80;
}
function getCommandExample(commandPrototype, commandObj) {
const commandName = getCommandName(commandObj);
const lines = [];
const lastLinesLeftMargin = " "; // 2 spaces
const linesSeparator = os.EOL + lastLinesLeftMargin;
let currentLine = `${misc_1.scriptName} ${commandName}`;
const maxWidth = terminalWidth();
const separatorLength = os.EOL.length;
const firstLineFreeSpace = maxWidth - usageConst.length - separatorLength;
const freeSpace = firstLineFreeSpace - lastLinesLeftMargin.length;
const leftMargin = _.repeat(" ", usageConst.length);
getAllOptionExamples(commandPrototype).forEach((example) => {
if (currentLine.length + example.length + 1 > (lines.length ? freeSpace : firstLineFreeSpace)) {
lines.push(currentLine);
currentLine = leftMargin + example;
}
else {
currentLine += ` ${example}`;
}
});
lines.push(currentLine);
return lines.join(linesSeparator);
}
function getCommandName(commandObj) {
const commandParts = commandObj.command;
let script = commandParts[commandParts.length - 1];
const extIndex = script.lastIndexOf(".");
if (extIndex > -1) {
script = script.slice(0, extIndex);
}
commandParts[commandParts.length - 1] = script;
return commandParts.join(" ");
}
function getAllOptionExamples(commandPrototype) {
return getSwitchOptionExamples(commandPrototype, false).concat(getPositionalOptionExamples(commandPrototype));
}
function getSwitchOptionExamples(commandPrototype, includeCommon = true) {
const switchOptions = option_decorators_1.getOptionsDescription(commandPrototype);
const switchOptionDescriptions = includeCommon ? _.values(switchOptions) : filterOptionDescriptions(_.values(switchOptions), false);
return sortOptionDescriptions(switchOptionDescriptions).map((description) => {
const result = [];
result.push(description.shortName ? `-${description.shortName}` : "");
result.push(description.shortName && description.longName ? "|" : "");
result.push(description.longName ? `--${description.longName}` : "");
result.push(description.hasArg ? " <arg>" : "");
if (!description.required) {
result.unshift("[");
result.push("]");
}
return result.join("");
});
}
function getPositionalOptionExamples(commandPrototype) {
const positionalOptions = option_decorators_1.getPositionalOptionsDescription(commandPrototype);
return _.sortBy(positionalOptions, "position").map((description) => {
if (description.position !== null) {
return `<${description.name}>`;
}
// Output for "rest" parameter. sortBy will push it to the end.
return `<${description.name}...>`;
});
}
function styleOptsTable(table) {
return table.map((row) => [chalk.bold(row[0])].concat(row.slice(1)));
}
function sortOptionDescriptions(options) {
return _(options)
.reverse() // options from a top prototype are added first, reversing order
.sortBy([(opt) => (opt.required ? 0 : 1)]) // required options should be shown first
.value();
}
function filterOptionDescriptions(options, isCommon) {
return isCommon
? options.filter((option) => {
return option.common;
})
: options.filter((option) => {
return !option.common;
});
}