clime
Version:
The command-line interface framework for TypeScript.
264 lines • 11.2 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const FS = require("fs");
const Path = require("path");
const Chalk = require("chalk");
const v = require("villa");
const cli_1 = require("../cli");
const internal_util_1 = require("../../internal-util");
class HelpInfo {
constructor() {
this.texts = [];
}
get text() {
return this.texts.join('\n');
}
buildTextForSubCommands(contexts) {
return __awaiter(this, void 0, void 0, function* () {
let labels = [];
let labelToHelpItemsMap = new Map();
let helpItemMap = new Map();
for (let [groupIndex, { label, dir }] of contexts.entries()) {
let helpItems;
if (labelToHelpItemsMap.has(label)) {
helpItems = labelToHelpItemsMap.get(label);
}
else {
helpItems = [];
labelToHelpItemsMap.set(label, helpItems);
labels.push(label);
}
let definitions = yield cli_1.CLI.getSubcommandDefinitions(dir);
for (let definition of definitions) {
let { name, brief } = definition;
let aliases = definition.aliases || (definition.alias && [definition.alias]) || [];
let item;
let existingItem = helpItemMap.get(name);
if (existingItem) {
existingItem.overridden = true;
item = {
name,
brief: brief || existingItem.brief,
aliases: existingItem.aliases.concat(aliases),
group: groupIndex,
};
}
else {
item = {
name,
brief,
aliases,
group: groupIndex,
};
}
helpItems.push(item);
helpItemMap.set(name, item);
}
if (!(yield internal_util_1.existsDir(dir))) {
continue;
}
let names = yield v.call(FS.readdir, dir);
for (let name of names) {
let path = Path.join(dir, name);
let stats = yield internal_util_1.safeStat(path);
if (stats.isFile()) {
if (name === cli_1.CLI.commandModuleDefaultFileName ||
Path.extname(path) !== cli_1.CLI.commandModuleExtension) {
continue;
}
name = Path.basename(name, cli_1.CLI.commandModuleExtension);
}
else {
path = Path.join(path, cli_1.CLI.commandModuleDefaultFileName);
stats = yield internal_util_1.safeStat(path);
}
let existingItem = helpItemMap.get(name);
// `brief` already set in `subcommands` field
if (existingItem &&
existingItem.group === groupIndex &&
existingItem.brief) {
continue;
}
let commandConstructor;
let brief;
if (stats) {
let module = (yield internal_util_1.dynamicImport(path));
commandConstructor = module.default;
brief =
commandConstructor &&
(commandConstructor.brief || commandConstructor.description);
}
if (existingItem && existingItem.group === groupIndex) {
existingItem.brief = brief;
}
else {
let aliases;
if (existingItem) {
if (!commandConstructor) {
// Directory without an entry should not override existing one.
continue;
}
existingItem.overridden = true;
if (!brief) {
brief = existingItem.brief;
}
aliases = existingItem.aliases;
}
else {
aliases = [];
}
let item = {
name,
aliases,
brief,
group: groupIndex,
};
helpItems.push(item);
helpItemMap.set(name, item);
}
}
}
for (let label of labels) {
let hasAliases = false;
let rows = labelToHelpItemsMap
.get(label)
.filter((item) => {
if (item.overridden) {
return false;
}
if (!hasAliases && item.aliases.length) {
hasAliases = true;
}
return true;
})
.map(({ name, aliases, brief }) => {
if (hasAliases) {
return [
Chalk.bold(name),
aliases.length ? `[${Chalk.dim(aliases.join(','))}]` : '',
brief,
];
}
else {
return [Chalk.bold(name), brief];
}
});
let separators = hasAliases ? [' ', ' - '] : ' - ';
if (rows.length) {
this.texts.push(`\
${Chalk.green(label.toUpperCase())}\n
${internal_util_1.buildTableOutput(rows, { indent: 4, separators })}`);
}
}
});
}
print(_stdout, stderr) {
stderr.write(`\n${this.text}\n`);
}
buildDescription(description) {
if (description) {
this.texts.push(`${internal_util_1.indent(description, 2)}\n`);
}
}
buildSubcommandsUsage(sequence) {
if (sequence && sequence.length) {
this.texts.push(`\
${Chalk.green('USAGE')}\n
${Chalk.bold(sequence.join(' '))} <subcommand>\n`);
}
}
buildTextsForParamsAndOptions(TargetCommand) {
let paramDefinitions = TargetCommand.paramDefinitions;
let paramsDefinition = TargetCommand.paramsDefinition;
let parameterDescriptionRows = [];
let parameterUsageTexts = [];
if (paramDefinitions) {
parameterUsageTexts = paramDefinitions.map((definition) => {
let { name, required, description, default: defaultValue } = definition;
if (description) {
parameterDescriptionRows.push([Chalk.bold(name), description]);
}
return required
? `<${name}>`
: `[${name}${defaultValue !== undefined ? `=${defaultValue}` : ''}]`;
});
}
else {
parameterUsageTexts = [];
}
if (paramsDefinition) {
let { name, required, description } = paramsDefinition;
if (description) {
parameterDescriptionRows.push([Chalk.bold(name), description]);
}
parameterUsageTexts.push(required ? `<...${name}>` : `[...${name}]`);
}
let optionDefinitions = TargetCommand.optionDefinitions || [];
let requiredOptionUsageItems = optionDefinitions
.filter((definition) => definition.required)
.map(({ name, key, placeholder }) => `--${name} <${placeholder || key}>`);
let usageLine = [
Chalk.bold(TargetCommand.sequence.join(' ').replace(/^\/ /, '/')),
...requiredOptionUsageItems,
...(optionDefinitions.length > requiredOptionUsageItems.length
? ['[...options]']
: []),
...parameterUsageTexts,
...(TargetCommand.skippedArgsEnabled ? ['-- ...'] : []),
].join(' ');
this.texts.push(`\
${Chalk.green('USAGE')}\n
${usageLine}\n`);
if (parameterDescriptionRows.length) {
this.texts.push(`\
${Chalk.green('PARAMETERS')}\n
${internal_util_1.buildTableOutput(parameterDescriptionRows, { indent: 4, separators: ' - ' })}`);
}
if (optionDefinitions.length) {
let optionRows = optionDefinitions.map((definition) => {
let { name, key, flag, placeholder, toggle: isToggle, description, default: defaultValue, } = definition;
let triggerStr = flag ? `-${flag}, ` : '';
triggerStr += `--${name}`;
if (!isToggle) {
triggerStr += ` <${placeholder || key}>`;
}
if (defaultValue !== undefined) {
description = description
? `${description} [${defaultValue}]`
: `[${defaultValue}]`;
}
return [Chalk.bold(triggerStr), description];
});
this.texts.push(`\
${Chalk.green('OPTIONS')}\n
${internal_util_1.buildTableOutput(optionRows, { indent: 4, separators: ' - ' })}`);
}
}
/** @internal */
static build(options) {
return __awaiter(this, void 0, void 0, function* () {
let info = new HelpInfo();
if (typeof options === 'object') {
info.buildDescription(options.description);
info.buildSubcommandsUsage(options.sequence);
yield info.buildTextForSubCommands(options.contexts);
}
else {
info.buildDescription(options.description);
info.buildTextsForParamsAndOptions(options);
yield info.buildTextForSubCommands(options.helpBuildingContexts);
}
return info;
});
}
}
exports.HelpInfo = HelpInfo;
//# sourceMappingURL=help.js.map