@oclif/core
Advanced tools
Comparing version 0.5.13 to 0.5.14
@@ -5,2 +5,9 @@ # Changelog | ||
### [0.5.14](https://github.com/oclif/core/compare/v0.5.13...v0.5.14) (2021-06-17) | ||
### Features | ||
* help improvements and customizability ([#184](https://github.com/oclif/core/issues/184)) ([cb2109b](https://github.com/oclif/core/commit/cb2109b113864534ceb08978ae1b209be7ae70d8)) | ||
### [0.5.13](https://github.com/oclif/core/compare/v0.5.12...v0.5.13) (2021-06-09) | ||
@@ -7,0 +14,0 @@ |
@@ -11,16 +11,23 @@ import * as Interfaces from './interfaces'; | ||
static _base: string; | ||
/** A command ID, used mostly in error or verbose reporting */ | ||
/** A command ID, used mostly in error or verbose reporting. */ | ||
static id: string; | ||
static title: string | undefined; | ||
/** | ||
* The tweet-sized description for your class, used in a parent-commands | ||
* sub-command listing and as the header for the command help | ||
* sub-command listing and as the header for the command help. | ||
*/ | ||
static summary?: string; | ||
/** | ||
* A full description of how to use the command. | ||
* | ||
* If no summary, the first line of the description will be used as the summary. | ||
*/ | ||
static description: string | undefined; | ||
/** hide the command from help? */ | ||
/** Hide the command from help? */ | ||
static hidden: boolean; | ||
/** An override string (or strings) for the default usage documentation */ | ||
/** | ||
* An override string (or strings) for the default usage documentation. | ||
*/ | ||
static usage: string | string[] | undefined; | ||
static help: string | undefined; | ||
/** An array of aliases for this command */ | ||
/** An array of aliases for this command. */ | ||
static aliases: string[]; | ||
@@ -33,4 +40,17 @@ /** When set to false, allows a variable amount of arguments */ | ||
static plugin: Interfaces.Plugin | undefined; | ||
/** An array of example strings to show at the end of the command's help */ | ||
static examples: string[] | undefined; | ||
/** | ||
* An array of examples to show at the end of the command's help. | ||
* | ||
* IF only a string is provided, it will try to look for a line that starts | ||
* with the cmd.bin as the example command and the rest as the description. | ||
* If found, the command will be formatted appropriately. | ||
* | ||
* ``` | ||
* EXAMPLES: | ||
* A description of a particular use case. | ||
* | ||
* $ <%= config.bin => command flags | ||
* ``` | ||
*/ | ||
static examples: Interfaces.Example[]; | ||
static parserOptions: {}; | ||
@@ -37,0 +57,0 @@ static disableJsonFlag: boolean | undefined; |
@@ -144,3 +144,3 @@ "use strict"; | ||
Command._base = `${pjson.name}@${pjson.version}`; | ||
/** An array of aliases for this command */ | ||
/** An array of aliases for this command. */ | ||
Command.aliases = []; | ||
@@ -172,3 +172,4 @@ /** When set to false, allows a variable amount of arguments */ | ||
description: 'format output as json', | ||
helpGroup: 'GLOBAL', | ||
}), | ||
}; |
@@ -84,2 +84,2 @@ import { Options, Plugin as IPlugin } from '../interfaces/plugin'; | ||
} | ||
export declare function toCached(c: Command.Class, plugin?: IPlugin): Command; | ||
export declare function toCached(c: Command.Class, plugin?: IPlugin): Promise<Command>; |
@@ -251,3 +251,3 @@ "use strict"; | ||
if (name && !topics.find(t => t.name === name)) { | ||
topics.push({ name, description: c.description }); | ||
topics.push({ name, description: c.summary || c.description }); | ||
} | ||
@@ -392,29 +392,11 @@ parts.pop(); | ||
exports.Config = Config; | ||
function toCached(c, plugin) { | ||
return { | ||
id: c.id, | ||
description: c.description, | ||
usage: c.usage, | ||
pluginName: plugin && plugin.name, | ||
pluginType: plugin && plugin.type, | ||
hidden: c.hidden, | ||
aliases: c.aliases || [], | ||
examples: c.examples || c.example, | ||
flags: util_2.mapValues(c.flags || {}, (flag, name) => { | ||
if (flag.type === 'boolean') { | ||
return { | ||
name, | ||
type: flag.type, | ||
char: flag.char, | ||
description: flag.description, | ||
hidden: flag.hidden, | ||
required: flag.required, | ||
helpLabel: flag.helpLabel, | ||
allowNo: flag.allowNo, | ||
}; | ||
} | ||
return { | ||
async function toCached(c, plugin) { | ||
const flags = {}; | ||
for (const [name, flag] of Object.entries(c.flags || {})) { | ||
if (flag.type === 'boolean') { | ||
flags[name] = { | ||
name, | ||
type: flag.type, | ||
char: flag.char, | ||
summary: flag.summary, | ||
description: flag.description, | ||
@@ -424,17 +406,48 @@ hidden: flag.hidden, | ||
helpLabel: flag.helpLabel, | ||
helpGroup: flag.helpGroup, | ||
allowNo: flag.allowNo, | ||
}; | ||
} | ||
else { | ||
flags[name] = { | ||
name, | ||
type: flag.type, | ||
char: flag.char, | ||
summary: flag.summary, | ||
description: flag.description, | ||
hidden: flag.hidden, | ||
required: flag.required, | ||
helpLabel: flag.helpLabel, | ||
helpValue: flag.helpValue, | ||
helpGroup: flag.helpGroup, | ||
multiple: flag.multiple, | ||
options: flag.options, | ||
default: typeof flag.default === 'function' ? flag.default({ options: {}, flags: {} }) : flag.default, | ||
// eslint-disable-next-line no-await-in-loop | ||
default: typeof flag.default === 'function' ? await flag.default({ options: {}, flags: {} }) : flag.default, | ||
}; | ||
}), | ||
args: c.args ? c.args.map(a => ({ | ||
name: a.name, | ||
description: a.description, | ||
required: a.required, | ||
options: a.options, | ||
default: typeof a.default === 'function' ? a.default({}) : a.default, | ||
hidden: a.hidden, | ||
})) : [], | ||
} | ||
} | ||
const argsPromise = (c.args || []).map(async (a) => ({ | ||
name: a.name, | ||
description: a.description, | ||
required: a.required, | ||
options: a.options, | ||
default: typeof a.default === 'function' ? await a.default({}) : a.default, | ||
hidden: a.hidden, | ||
})); | ||
const args = await Promise.all(argsPromise); | ||
return { | ||
id: c.id, | ||
summary: c.summary, | ||
description: c.description, | ||
usage: c.usage, | ||
pluginName: plugin && plugin.name, | ||
pluginType: plugin && plugin.type, | ||
hidden: c.hidden, | ||
aliases: c.aliases || [], | ||
examples: c.examples || c.example, | ||
flags, | ||
args, | ||
}; | ||
} | ||
exports.toCached = toCached; |
@@ -215,3 +215,3 @@ "use strict"; | ||
try { | ||
return [id, config_1.toCached(await this.findCommand(id, { must: true }), this)]; | ||
return [id, await config_1.toCached(await this.findCommand(id, { must: true }), this)]; | ||
} | ||
@@ -218,0 +218,0 @@ catch (error) { |
@@ -13,4 +13,4 @@ import { OptionFlag, Definition, BooleanFlag, EnumFlagOptions } from './interfaces'; | ||
export { stringFlag as string }; | ||
export { boolean, integer } from './parser'; | ||
export { boolean, integer, url } from './parser'; | ||
export declare const version: (opts?: Partial<BooleanFlag<boolean>>) => BooleanFlag<void>; | ||
export declare const help: (opts?: Partial<BooleanFlag<boolean>>) => BooleanFlag<void>; |
@@ -25,2 +25,3 @@ "use strict"; | ||
exports.integer = parser_1.integer; | ||
exports.url = parser_1.url; | ||
exports.version = (opts = {}) => { | ||
@@ -27,0 +28,0 @@ return Parser.flags.boolean(Object.assign(Object.assign({ |
import * as Interfaces from '../interfaces'; | ||
export default class CommandHelp { | ||
import { Example } from '../interfaces/command'; | ||
import { HelpFormatter } from './formatter'; | ||
export declare type HelpSection = { | ||
header: string; | ||
body: string | [string, string | undefined][] | undefined; | ||
} | undefined; | ||
export declare type HelpSectionRenderer = (data: { | ||
cmd: Interfaces.Command; | ||
flags: Interfaces.Command.Flag[]; | ||
args: Interfaces.Command.Arg[]; | ||
}, header: string) => HelpSection[] | string | undefined; | ||
export declare class CommandHelp extends HelpFormatter { | ||
command: Interfaces.Command; | ||
config: Interfaces.Config; | ||
opts: Interfaces.HelpOptions; | ||
render: (input: string) => string; | ||
constructor(command: Interfaces.Command, config: Interfaces.Config, opts: Interfaces.HelpOptions); | ||
generate(): string; | ||
protected groupFlags(flags: Interfaces.Command.Flag[]): { | ||
mainFlags: Interfaces.Command.Flag[]; | ||
flagGroups: { | ||
[index: string]: Interfaces.Command.Flag[]; | ||
}; | ||
}; | ||
protected sections(): Array<{ | ||
header: string; | ||
generate: HelpSectionRenderer; | ||
}>; | ||
protected usage(flags: Interfaces.Command.Flag[]): string; | ||
@@ -13,6 +33,9 @@ protected defaultUsage(_: Interfaces.Command.Flag[]): string; | ||
protected aliases(aliases: string[] | undefined): string | undefined; | ||
protected examples(examples: string[] | undefined | string): string | undefined; | ||
protected args(args: Interfaces.Command['args']): string | undefined; | ||
protected examples(examples: Example[] | undefined | string): string | undefined; | ||
protected args(args: Interfaces.Command['args']): [string, string | undefined][] | undefined; | ||
protected arg(arg: Interfaces.Command['args'][0]): string; | ||
protected flags(flags: Interfaces.Command.Flag[]): string | undefined; | ||
protected flagHelpLabel(flag: Interfaces.Command.Flag, showOptions?: boolean): string; | ||
protected flags(flags: Interfaces.Command.Flag[]): [string, string | undefined][] | undefined; | ||
protected flagsDescriptions(flags: Interfaces.Command.Flag[]): string | undefined; | ||
} | ||
export default CommandHelp; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Chalk = require("chalk"); | ||
const indent = require("indent-string"); | ||
const stripAnsi = require("strip-ansi"); | ||
const list_1 = require("./list"); | ||
const util_1 = require("../util"); | ||
const util_2 = require("./util"); | ||
const { underline, bold, } = Chalk; | ||
const formatter_1 = require("./formatter"); | ||
// Don't use os.EOL because we need to ensure that a string | ||
// written on any platform, that may use \r\n or \n, will be | ||
// split on any platform, not just the os specific EOL at runtime. | ||
const POSSIBLE_LINE_FEED = /\r\n|\n/; | ||
const { underline, } = Chalk; | ||
let { dim, } = Chalk; | ||
@@ -14,9 +16,8 @@ if (process.env.ConEmuANSI === 'ON') { | ||
} | ||
const wrap = require('wrap-ansi'); | ||
class CommandHelp { | ||
class CommandHelp extends formatter_1.HelpFormatter { | ||
constructor(command, config, opts) { | ||
super(config, opts); | ||
this.command = command; | ||
this.config = config; | ||
this.opts = opts; | ||
this.render = util_2.template(this); | ||
} | ||
@@ -32,14 +33,75 @@ generate() { | ||
const args = (cmd.args || []).filter(a => !a.hidden); | ||
let output = util_1.compact([ | ||
this.usage(flags), | ||
this.args(args), | ||
this.flags(flags), | ||
this.description(), | ||
this.aliases(cmd.aliases), | ||
this.examples(cmd.examples || cmd.example), | ||
]).join('\n\n'); | ||
if (this.opts.stripAnsi) | ||
output = stripAnsi(output); | ||
const output = util_1.compact(this.sections().map(({ header, generate }) => { | ||
const body = generate({ cmd, flags, args }, header); | ||
// Generate can return a list of sections | ||
if (Array.isArray(body)) { | ||
return body.map(helpSection => helpSection && helpSection.body && this.section(helpSection.header, helpSection.body)).join('\n\n'); | ||
} | ||
return body && this.section(header, body); | ||
})).join('\n\n'); | ||
return output; | ||
} | ||
groupFlags(flags) { | ||
const mainFlags = []; | ||
const flagGroups = {}; | ||
for (const flag of flags) { | ||
const group = flag.helpGroup; | ||
if (group) { | ||
if (!flagGroups[group]) | ||
flagGroups[group] = []; | ||
flagGroups[group].push(flag); | ||
} | ||
else { | ||
mainFlags.push(flag); | ||
} | ||
} | ||
return { mainFlags, flagGroups }; | ||
} | ||
sections() { | ||
return [ | ||
{ | ||
header: this.opts.usageHeader || 'USAGE', | ||
generate: ({ flags }) => this.usage(flags), | ||
}, | ||
{ | ||
header: 'ARGUMENTS', | ||
generate: ({ args }, header) => [{ header, body: this.args(args) }], | ||
}, | ||
{ | ||
header: 'FLAGS', | ||
generate: ({ flags }, header) => { | ||
const { mainFlags, flagGroups } = this.groupFlags(flags); | ||
const flagSections = []; | ||
const mainFlagBody = this.flags(mainFlags); | ||
if (mainFlagBody) | ||
flagSections.push({ header, body: mainFlagBody }); | ||
Object.entries(flagGroups).forEach(([name, flags]) => { | ||
const body = this.flags(flags); | ||
if (body) | ||
flagSections.push({ header: `${name.toUpperCase()} ${header}`, body }); | ||
}); | ||
return util_1.compact(flagSections); | ||
}, | ||
}, | ||
{ | ||
header: 'DESCRIPTION', | ||
generate: () => this.description(), | ||
}, | ||
{ | ||
header: 'ALIASES', | ||
generate: ({ cmd }) => this.aliases(cmd.aliases), | ||
}, | ||
{ | ||
header: 'EXAMPLES', | ||
generate: ({ cmd }) => { | ||
const examples = cmd.examples || cmd.example; | ||
return this.examples(examples); | ||
}, | ||
}, | ||
{ | ||
header: 'FLAG DESCRIPTIONS', | ||
generate: ({ flags }) => this.flagsDescriptions(flags), | ||
}, | ||
]; | ||
} | ||
usage(flags) { | ||
@@ -50,6 +112,3 @@ const usage = this.command.usage; | ||
.join('\n'); | ||
return [ | ||
bold('USAGE'), | ||
indent(wrap(this.render(body), this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
return this.wrap(body); | ||
} | ||
@@ -64,9 +123,17 @@ defaultUsage(_) { | ||
const cmd = this.command; | ||
const description = cmd.description && this.render(cmd.description).split('\n').slice(1).join('\n'); | ||
if (!description) | ||
return; | ||
return [ | ||
bold('DESCRIPTION'), | ||
indent(wrap(description.trim(), this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
let description; | ||
if (this.opts.hideCommandSummaryInDescription) { | ||
description = (cmd.description || '').split(POSSIBLE_LINE_FEED).slice(1); | ||
} | ||
else if (cmd.description) { | ||
description = [ | ||
...(cmd.summary || '').split(POSSIBLE_LINE_FEED), | ||
...(cmd.description || '').split(POSSIBLE_LINE_FEED), | ||
]; | ||
} | ||
if (description) { | ||
// Lines separated with only one newline or more than 2 can be hard to read in the terminal. | ||
// Always separate by two newlines. | ||
return this.wrap(util_1.compact(description).join('\n\n')); | ||
} | ||
} | ||
@@ -77,6 +144,3 @@ aliases(aliases) { | ||
const body = aliases.map(a => ['$', this.config.bin, a].join(' ')).join('\n'); | ||
return [ | ||
bold('ALIASES'), | ||
indent(wrap(body, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
return body; | ||
} | ||
@@ -86,7 +150,43 @@ examples(examples) { | ||
return; | ||
const body = util_1.castArray(examples).map(a => this.render(a)).join('\n'); | ||
return [ | ||
bold('EXAMPLE' + (examples.length > 1 ? 'S' : '')), | ||
indent(wrap(body, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
const formatIfCommand = (example) => { | ||
example = this.render(example); | ||
if (example.startsWith(this.config.bin)) | ||
return dim(`$ ${example}`); | ||
if (example.startsWith(`$ ${this.config.bin}`)) | ||
return dim(example); | ||
return example; | ||
}; | ||
const isCommand = (example) => stripAnsi(formatIfCommand(example)).startsWith(`$ ${this.config.bin}`); | ||
const body = util_1.castArray(examples).map(a => { | ||
let description; | ||
let command; | ||
if (typeof a === 'string') { | ||
const lines = a | ||
.split(POSSIBLE_LINE_FEED) | ||
.filter(line => Boolean(line)); | ||
// If the example is <description>\n<command> then format correctly | ||
if (lines.length === 2 && !isCommand(lines[0]) && isCommand(lines[1])) { | ||
description = lines[0]; | ||
command = lines[1]; | ||
} | ||
else { | ||
return lines.map(line => formatIfCommand(line)).join('\n'); | ||
} | ||
} | ||
else { | ||
description = a.description; | ||
command = a.command; | ||
} | ||
const multilineSeparator = this.config.platform === 'win32' ? | ||
this.config.shell.includes('powershell') ? '`' : '^' : | ||
'\\'; | ||
// The command will be indented in the section, which is also indented | ||
const finalIndentedSpacing = this.indentSpacing * 2; | ||
// First indent keeping room for escaped newlines | ||
const multilineCommand = this.indent(this.wrap(formatIfCommand(command), finalIndentedSpacing + 4)) | ||
// Then add the escaped newline | ||
.split(POSSIBLE_LINE_FEED).join(` ${multilineSeparator}\n `); | ||
return `${this.wrap(description, finalIndentedSpacing)}\n\n${multilineCommand}`; | ||
}).join('\n\n'); | ||
return body; | ||
} | ||
@@ -96,3 +196,3 @@ args(args) { | ||
return; | ||
const body = list_1.renderList(args.map(a => { | ||
return args.map(a => { | ||
const name = a.name.toUpperCase(); | ||
@@ -105,7 +205,3 @@ let description = a.description || ''; | ||
return [name, description ? dim(description) : undefined]; | ||
}), { stripAnsi: this.opts.stripAnsi, maxWidth: this.opts.maxWidth - 2 }); | ||
return [ | ||
bold('ARGUMENTS'), | ||
indent(body, 2), | ||
].join('\n'); | ||
}); | ||
} | ||
@@ -118,31 +214,40 @@ arg(arg) { | ||
} | ||
flagHelpLabel(flag, showOptions = false) { | ||
let label = flag.helpLabel; | ||
if (!label) { | ||
const labels = []; | ||
if (flag.char) | ||
labels.push(`-${flag.char[0]}`); | ||
if (flag.name) { | ||
if (flag.type === 'boolean' && flag.allowNo) { | ||
labels.push(`--[no-]${flag.name.trim()}`); | ||
} | ||
else { | ||
labels.push(`--${flag.name.trim()}`); | ||
} | ||
} | ||
label = labels.join(', '); | ||
} | ||
if (flag.type === 'option') { | ||
let value = flag.helpValue || (this.opts.showFlagNameInTitle ? flag.name : '<value>'); | ||
if (!flag.helpValue && flag.options) { | ||
if (showOptions || this.opts.showFlagOptionsInTitle) | ||
value = `${flag.options.join('|')}`; | ||
else | ||
value = '<option>'; | ||
} | ||
if (flag.multiple) | ||
value += '...'; | ||
if (!value.includes('|')) | ||
value = underline(value); | ||
label += `=${value}`; | ||
} | ||
return label; | ||
} | ||
flags(flags) { | ||
if (flags.length === 0) | ||
return; | ||
const body = list_1.renderList(flags.map(flag => { | ||
let left = flag.helpLabel; | ||
if (!left) { | ||
const label = []; | ||
if (flag.char) | ||
label.push(`-${flag.char[0]}`); | ||
if (flag.name) { | ||
if (flag.type === 'boolean' && flag.allowNo) { | ||
label.push(`--[no-]${flag.name.trim()}`); | ||
} | ||
else { | ||
label.push(`--${flag.name.trim()}`); | ||
} | ||
} | ||
left = label.join(', '); | ||
} | ||
if (flag.type === 'option') { | ||
let value = flag.helpValue || flag.name; | ||
if (!flag.helpValue && flag.options) { | ||
value = flag.options.join('|'); | ||
} | ||
if (!value.includes('|')) | ||
value = underline(value); | ||
left += `=${value}`; | ||
} | ||
let right = flag.description || ''; | ||
return flags.map(flag => { | ||
const left = this.flagHelpLabel(flag); | ||
let right = flag.summary || flag.description || ''; | ||
if (flag.type === 'option' && flag.default) { | ||
@@ -153,10 +258,28 @@ right = `[default: ${flag.default}] ${right}`; | ||
right = `(required) ${right}`; | ||
if (flag.type === 'option' && flag.options && !flag.helpValue && !this.opts.showFlagOptionsInTitle) { | ||
right += `\n<options: ${flag.options.join('|')}>`; | ||
} | ||
return [left, dim(right.trim())]; | ||
}), { stripAnsi: this.opts.stripAnsi, maxWidth: this.opts.maxWidth - 2 }); | ||
return [ | ||
bold('OPTIONS'), | ||
indent(body, 2), | ||
].join('\n'); | ||
}); | ||
} | ||
flagsDescriptions(flags) { | ||
const flagsWithExtendedDescriptions = flags.filter(flag => flag.summary && flag.description); | ||
if (flagsWithExtendedDescriptions.length === 0) | ||
return; | ||
const body = flagsWithExtendedDescriptions.map(flag => { | ||
// Guaranteed to be set because of the filter above, but make ts happy | ||
const summary = flag.summary || ''; | ||
let flagHelp = this.flagHelpLabel(flag, true); | ||
if (flagHelp.length + summary.length + 2 < this.opts.maxWidth) { | ||
flagHelp += ' ' + summary; | ||
} | ||
else { | ||
flagHelp += '\n\n' + this.indent(this.wrap(summary, this.indentSpacing * 2)); | ||
} | ||
return `${flagHelp}\n\n${this.indent(this.wrap(flag.description || '', this.indentSpacing * 2))}`; | ||
}).join('\n\n'); | ||
return body; | ||
} | ||
} | ||
exports.CommandHelp = CommandHelp; | ||
exports.default = CommandHelp; |
import * as Interfaces from '../interfaces'; | ||
import CommandHelp from './command'; | ||
import { HelpFormatter } from './formatter'; | ||
export { CommandHelp } from './command'; | ||
export { standardizeIDFromArgv, loadHelpClass } from './util'; | ||
export declare abstract class HelpBase { | ||
export declare abstract class HelpBase extends HelpFormatter { | ||
constructor(config: Interfaces.Config, opts?: Partial<Interfaces.HelpOptions>); | ||
protected config: Interfaces.Config; | ||
protected opts: Interfaces.HelpOptions; | ||
/** | ||
@@ -20,3 +21,3 @@ * Show help, used in multi-command CLIs | ||
export declare class Help extends HelpBase { | ||
render: (input: string) => string; | ||
protected CommandHelpClass: typeof CommandHelp; | ||
private get _topics(); | ||
@@ -33,2 +34,4 @@ protected get sortedCommands(): Interfaces.Command.Plugin[]; | ||
protected formatCommands(commands: Interfaces.Command[]): string; | ||
protected summary(c: Interfaces.Command): string | undefined; | ||
protected description(c: Interfaces.Command): string; | ||
protected formatTopic(topic: Interfaces.Topic): string; | ||
@@ -35,0 +38,0 @@ protected formatTopics(topics: Interfaces.Topic[]): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Chalk = require("chalk"); | ||
const indent = require("indent-string"); | ||
const stripAnsi = require("strip-ansi"); | ||
const errors_1 = require("../errors"); | ||
const command_1 = require("./command"); | ||
const list_1 = require("./list"); | ||
const root_1 = require("./root"); | ||
const screen_1 = require("../screen"); | ||
const util_1 = require("../util"); | ||
const util_2 = require("./util"); | ||
const formatter_1 = require("./formatter"); | ||
var command_2 = require("./command"); | ||
exports.CommandHelp = command_2.CommandHelp; | ||
var util_3 = require("./util"); | ||
exports.standardizeIDFromArgv = util_3.standardizeIDFromArgv; | ||
exports.loadHelpClass = util_3.loadHelpClass; | ||
const wrap = require('wrap-ansi'); | ||
const { bold, } = Chalk; | ||
function getHelpSubject(args) { | ||
@@ -29,8 +26,7 @@ for (const arg of args) { | ||
} | ||
class HelpBase { | ||
class HelpBase extends formatter_1.HelpFormatter { | ||
constructor(config, opts = {}) { | ||
super(config, opts); | ||
if (!config.topicSeparator) | ||
config.topicSeparator = ':'; // back-support @oclif/config | ||
this.config = config; | ||
this.opts = Object.assign({ maxWidth: screen_1.stdtermwidth }, opts); | ||
} | ||
@@ -42,3 +38,3 @@ } | ||
super(config, opts); | ||
this.render = util_2.template(this); | ||
this.CommandHelpClass = command_1.default; | ||
} | ||
@@ -102,5 +98,5 @@ /* | ||
const subCommands = this.sortedCommands.filter(c => c.id.startsWith(name + ':') && c.id.split(':').length === depth + 1); | ||
const title = command.description && this.render(command.description).split('\n')[0]; | ||
if (title) | ||
console.log(title + '\n'); | ||
const summary = this.summary(command); | ||
if (summary) | ||
console.log(summary + '\n'); | ||
console.log(this.formatCommand(command)); | ||
@@ -160,3 +156,3 @@ console.log(''); | ||
} | ||
const help = new command_1.default(command, this.config, this.opts); | ||
const help = new this.CommandHelpClass(command, this.config, this.opts); | ||
return help.generate(); | ||
@@ -167,3 +163,3 @@ } | ||
return ''; | ||
const body = list_1.renderList(commands.map(c => { | ||
const body = this.renderList(commands.map(c => { | ||
if (this.config.topicSeparator !== ':') | ||
@@ -173,3 +169,3 @@ c.id = c.id.replace(/:/g, this.config.topicSeparator); | ||
c.id, | ||
c.description && this.render(c.description.split('\n')[0]), | ||
this.summary(c), | ||
]; | ||
@@ -179,12 +175,21 @@ }), { | ||
stripAnsi: this.opts.stripAnsi, | ||
maxWidth: this.opts.maxWidth - 2, | ||
indentation: 2, | ||
}); | ||
return [ | ||
bold('COMMANDS'), | ||
indent(body, 2), | ||
].join('\n'); | ||
return this.section('COMMANDS', body); | ||
} | ||
summary(c) { | ||
if (c.summary) | ||
return this.render(c.summary.split('\n')[0]); | ||
return c.description && this.render(c.description).split('\n')[0]; | ||
} | ||
description(c) { | ||
const description = this.render(c.description || ''); | ||
if (c.summary) { | ||
return description; | ||
} | ||
return description.split('\n').slice(1).join('\n'); | ||
} | ||
formatTopic(topic) { | ||
let description = this.render(topic.description || ''); | ||
const title = description.split('\n')[0]; | ||
const summary = description.split('\n')[0]; | ||
description = description.split('\n').slice(1).join('\n'); | ||
@@ -195,11 +200,5 @@ let topicID = `${topic.name}:COMMAND`; | ||
let output = util_1.compact([ | ||
title, | ||
[ | ||
bold('USAGE'), | ||
indent(wrap(`$ ${this.config.bin} ${topicID}`, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'), | ||
description && ([ | ||
bold('DESCRIPTION'), | ||
indent(wrap(description, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n')), | ||
summary, | ||
this.section(this.opts.usageHeader || 'USAGE', `$ ${this.config.bin} ${topicID}`), | ||
description && this.section('DESCRIPTION', this.wrap(description)), | ||
]).join('\n\n'); | ||
@@ -213,3 +212,3 @@ if (this.opts.stripAnsi) | ||
return ''; | ||
const body = list_1.renderList(topics.map(c => { | ||
const body = this.renderList(topics.map(c => { | ||
if (this.config.topicSeparator !== ':') | ||
@@ -224,8 +223,5 @@ c.name = c.name.replace(/:/g, this.config.topicSeparator); | ||
stripAnsi: this.opts.stripAnsi, | ||
maxWidth: this.opts.maxWidth - 2, | ||
indentation: 2, | ||
}); | ||
return [ | ||
bold('TOPICS'), | ||
indent(body, 2), | ||
].join('\n'); | ||
return this.section('TOPICS', body); | ||
} | ||
@@ -232,0 +228,0 @@ /** |
import * as Interfaces from '../interfaces'; | ||
export default class RootHelp { | ||
import { HelpFormatter } from './formatter'; | ||
export default class RootHelp extends HelpFormatter { | ||
config: Interfaces.Config; | ||
opts: Interfaces.HelpOptions; | ||
render: (input: string) => string; | ||
constructor(config: Interfaces.Config, opts: Interfaces.HelpOptions); | ||
@@ -7,0 +7,0 @@ root(): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const Chalk = require("chalk"); | ||
const indent = require("indent-string"); | ||
const stripAnsi = require("strip-ansi"); | ||
const util_1 = require("../util"); | ||
const util_2 = require("./util"); | ||
const wrap = require('wrap-ansi'); | ||
const { bold, } = Chalk; | ||
class RootHelp { | ||
const formatter_1 = require("./formatter"); | ||
class RootHelp extends formatter_1.HelpFormatter { | ||
constructor(config, opts) { | ||
super(config, opts); | ||
this.config = config; | ||
this.opts = opts; | ||
this.render = util_2.template(this); | ||
} | ||
@@ -31,6 +27,3 @@ root() { | ||
usage() { | ||
return [ | ||
bold('USAGE'), | ||
indent(wrap(`$ ${this.config.bin} [COMMAND]`, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
return this.section(this.opts.usageHeader || 'USAGE', this.wrap(`$ ${this.config.bin} [COMMAND]`)); | ||
} | ||
@@ -43,14 +36,8 @@ description() { | ||
return; | ||
return [ | ||
bold('DESCRIPTION'), | ||
indent(wrap(description, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
return this.section('DESCRIPTION', this.wrap(description)); | ||
} | ||
version() { | ||
return [ | ||
bold('VERSION'), | ||
indent(wrap(this.config.userAgent, this.opts.maxWidth - 2, { trim: false, hard: true }), 2), | ||
].join('\n'); | ||
return this.section('VERSION', this.wrap(this.config.userAgent)); | ||
} | ||
} | ||
exports.default = RootHelp; |
import { Config, LoadOptions } from './config'; | ||
import { ArgInput, FlagInput } from './parser'; | ||
export interface Command { | ||
import { ArgInput, BooleanFlagProps, FlagInput, OptionFlagProps } from './parser'; | ||
export declare type Example = string | { | ||
description: string; | ||
command: string; | ||
}; | ||
export interface CommandProps { | ||
/** A command ID, used mostly in error or verbose reporting. */ | ||
id: string; | ||
/** Hide the command from help? */ | ||
hidden: boolean; | ||
/** An array of aliases for this command. */ | ||
aliases: string[]; | ||
/** | ||
* The tweet-sized description for your class, used in a parent-commands | ||
* sub-command listing and as the header for the command help. | ||
*/ | ||
summary?: string; | ||
/** | ||
* A full description of how to use the command. | ||
* | ||
* If no summary, the first line of the description will be used as the summary. | ||
*/ | ||
description?: string; | ||
/** | ||
* An override string (or strings) for the default usage documentation. | ||
*/ | ||
usage?: string | string[]; | ||
examples?: string[]; | ||
/** | ||
* An array of examples to show at the end of the command's help. | ||
* | ||
* IF only a string is provide, it will try to look for a line that starts | ||
* with the cmd.bin as the example command and the rest as the description. | ||
* If found, the command will be formatted appropriately. | ||
* | ||
* ``` | ||
* EXAMPLES: | ||
* A description of a particular use case. | ||
* | ||
* $ <%= config.bin => command flags | ||
* ``` | ||
*/ | ||
examples?: Example[]; | ||
} | ||
export interface Command extends CommandProps { | ||
type?: string; | ||
@@ -29,33 +65,10 @@ pluginName?: string; | ||
namespace Flag { | ||
interface Boolean { | ||
type: 'boolean'; | ||
name: string; | ||
required?: boolean; | ||
char?: string; | ||
hidden?: boolean; | ||
description?: string; | ||
helpLabel?: string; | ||
allowNo?: boolean; | ||
interface Boolean extends BooleanFlagProps { | ||
} | ||
interface Option { | ||
type: 'option'; | ||
name: string; | ||
required?: boolean; | ||
char?: string; | ||
hidden?: boolean; | ||
description?: string; | ||
helpLabel?: string; | ||
helpValue?: string; | ||
interface Option extends OptionFlagProps { | ||
default?: string; | ||
options?: string[]; | ||
} | ||
} | ||
interface Base { | ||
interface Base extends CommandProps { | ||
_base: string; | ||
id: string; | ||
hidden: boolean; | ||
aliases: string[]; | ||
description?: string; | ||
usage?: string | string[]; | ||
examples?: string[]; | ||
} | ||
@@ -62,0 +75,0 @@ interface Class extends Base { |
@@ -5,2 +5,28 @@ export interface HelpOptions { | ||
stripAnsi?: boolean; | ||
/** | ||
* By default, option values on flags are shown in the flag's description. This is because | ||
* long options list ruin the formatting of help. If a CLI knows all commands will not | ||
* do this, it can be turned off at a help level using this property. An individual flag | ||
* can set this using `flag.helpValue=options.join('|')`. | ||
*/ | ||
showFlagOptionsInTitle?: boolean; | ||
/** | ||
* By default, titles show flag values as `<value>`. Some CLI developers may prefer titles | ||
* to show the flag name as the value. i.e. `--myflag=myflag` instead of `--myflag=<value>`. | ||
* An individual flag can set this using `flag.helpValue=flag.name`. | ||
*/ | ||
showFlagNameInTitle?: boolean; | ||
/** | ||
* By default, the command summary is show at the top of the help and as the first line in | ||
* the command description. Repeating the summary in the command description improves readability | ||
* especially for long command help output. If there is no `command.summary`, the first line of | ||
* the description is treated as the summary. Some CLIs, especially with very simple commands, may | ||
* not want the duplication. | ||
*/ | ||
hideCommandSummaryInDescription?: boolean; | ||
/** | ||
* Use USAGE, but some may want to use USAGE as used in gnu man pages. See help recommendations at | ||
* http://www.gnu.org/software/help2man/#--help-recommendations | ||
*/ | ||
usageHeader?: string; | ||
} |
export { AlphabetLowercase, AlphabetUppercase } from './alphabet'; | ||
export { Config, ArchTypes, PlatformTypes, LoadOptions } from './config'; | ||
export { Command } from './command'; | ||
export { Command, Example } from './command'; | ||
export { OclifError, PrettyPrintableError } from './errors'; | ||
@@ -5,0 +5,0 @@ export { HelpOptions } from './help'; |
@@ -88,9 +88,39 @@ import { AlphabetLowercase, AlphabetUppercase } from './alphabet'; | ||
export declare type Default<T> = T | ((context: DefaultContext<T>) => Promise<T>); | ||
export declare type FlagBase<T, I> = { | ||
export declare type FlagProps = { | ||
name: string; | ||
char?: AlphabetLowercase | AlphabetUppercase; | ||
/** | ||
* A short summary of flag usage to show in the flag list. | ||
* If not provided, description will be used. | ||
*/ | ||
summary?: string; | ||
/** | ||
* A description of flag usage. If summary is provided, the description | ||
* is assumed to be a longer description and will be shown in a separate | ||
* section within help. | ||
*/ | ||
description?: string; | ||
/** | ||
* The flag label to show in help. Defaults to "[-<char>] --<name>" where -<char> is | ||
* only displayed if the char is defined. | ||
*/ | ||
helpLabel?: string; | ||
/** | ||
* Shows this flag in a separate list in the help. | ||
*/ | ||
helpGroup?: string; | ||
hidden?: boolean; | ||
required?: boolean; | ||
}; | ||
export declare type BooleanFlagProps = FlagProps & { | ||
type: 'boolean'; | ||
allowNo: boolean; | ||
}; | ||
export declare type OptionFlagProps = FlagProps & { | ||
type: 'option'; | ||
helpValue?: string; | ||
options?: string[]; | ||
multiple: boolean; | ||
}; | ||
export declare type FlagBase<T, I> = FlagProps & { | ||
dependsOn?: string[]; | ||
@@ -105,5 +135,3 @@ exclusive?: string[]; | ||
}; | ||
export declare type BooleanFlag<T> = FlagBase<T, boolean> & { | ||
type: 'boolean'; | ||
allowNo: boolean; | ||
export declare type BooleanFlag<T> = FlagBase<T, boolean> & BooleanFlagProps & { | ||
/** | ||
@@ -114,9 +142,5 @@ * specifying a default of false is the same not specifying a default | ||
}; | ||
export declare type OptionFlag<T> = FlagBase<T, string> & { | ||
type: 'option'; | ||
helpValue?: string; | ||
export declare type OptionFlag<T> = FlagBase<T, string> & OptionFlagProps & { | ||
default?: Default<T | undefined>; | ||
multiple: boolean; | ||
input: string[]; | ||
options?: string[]; | ||
}; | ||
@@ -123,0 +147,0 @@ export declare type Definition<T> = { |
@@ -0,1 +1,2 @@ | ||
import { HelpOptions } from './help'; | ||
export interface PJSON { | ||
@@ -16,3 +17,2 @@ [k: string]: any; | ||
schema?: number; | ||
title?: string; | ||
description?: string; | ||
@@ -28,2 +28,3 @@ topicSeparator?: ':' | ' '; | ||
helpClass?: string; | ||
helpOptions?: HelpOptions; | ||
aliases?: { | ||
@@ -30,0 +31,0 @@ [name: string]: string | null; |
@@ -53,3 +53,3 @@ "use strict"; | ||
const Help = await help_1.loadHelpClass(config); | ||
const help = new Help(config); | ||
const help = new Help(config, config.pjson.helpOptions); | ||
await help.showHelp(argv); | ||
@@ -56,0 +56,0 @@ return; |
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
import { URL } from 'url'; | ||
import { Definition, OptionFlag, BooleanFlag } from '../interfaces'; | ||
@@ -8,2 +10,7 @@ export declare function build<T>(defaults: { | ||
export declare const integer: Definition<number>; | ||
/** | ||
* Initializes a string as a URL. Throws an error | ||
* if the string is not a valid URL. | ||
*/ | ||
export declare const url: Definition<URL>; | ||
export declare function option<T>(options: { | ||
@@ -10,0 +17,0 @@ parse: OptionFlag<T>['parse']; |
"use strict"; | ||
// tslint:disable interface-over-type-literal | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const url_1 = require("url"); | ||
function build(defaults) { | ||
@@ -21,2 +22,16 @@ return (options = {}) => { | ||
}); | ||
/** | ||
* Initializes a string as a URL. Throws an error | ||
* if the string is not a valid URL. | ||
*/ | ||
exports.url = build({ | ||
parse: async (input) => { | ||
try { | ||
return new url_1.URL(input); | ||
} | ||
catch (_a) { | ||
throw new Error(`Expected a valid url but received: ${input}`); | ||
} | ||
}, | ||
}); | ||
function option(options) { | ||
@@ -23,0 +38,0 @@ return build(options)(); |
@@ -21,3 +21,3 @@ "use strict"; | ||
const usage = flag.type === 'option' ? ` ${flag.name.toUpperCase()}` : ''; | ||
let description = flag.description || ''; | ||
let description = flag.summary || flag.description || ''; | ||
if (options.displayRequired && flag.required) | ||
@@ -24,0 +24,0 @@ description = `(required) ${description}`; |
@@ -0,1 +1,2 @@ | ||
/// <reference types="node" /> | ||
import * as args from './args'; | ||
@@ -10,4 +11,3 @@ import * as flags from './flags'; | ||
}>(argv: string[], options: Input<TFlags>): Promise<ParserOutput<TFlags, TArgs>>; | ||
declare const boolean: typeof flags.boolean; | ||
declare const integer: import("../interfaces").Definition<number>; | ||
export { boolean, integer }; | ||
declare const boolean: typeof flags.boolean, integer: import("../interfaces").Definition<number>, url: import("../interfaces").Definition<import("url").URL>; | ||
export { boolean, integer, url }; |
@@ -31,5 +31,5 @@ "use strict"; | ||
exports.parse = parse; | ||
const boolean = flags.boolean; | ||
const { boolean, integer, url } = flags; | ||
exports.boolean = boolean; | ||
const integer = flags.integer; | ||
exports.integer = integer; | ||
exports.url = url; |
@@ -203,9 +203,9 @@ "use strict"; | ||
this.metaData.flags[k] = { setFromDefault: true }; | ||
if (typeof flag.default === 'function') { | ||
// eslint-disable-next-line no-await-in-loop | ||
const defaultValue = (typeof flag.default === 'function' ? await flag.default(Object.assign({ options: flag, flags }, this.context)) : flag.default); | ||
const parsedValue = flag.type === 'option' && flag.parse ? | ||
// eslint-disable-next-line no-await-in-loop | ||
flags[k] = await flag.default(Object.assign({ options: flag, flags }, this.context)); | ||
} | ||
else { | ||
flags[k] = flag.default; | ||
} | ||
await flag.parse(defaultValue, this.context) : | ||
defaultValue; | ||
flags[k] = parsedValue; | ||
} | ||
@@ -212,0 +212,0 @@ } |
{ | ||
"name": "@oclif/core", | ||
"description": "base library for oclif CLIs", | ||
"version": "0.5.13", | ||
"version": "0.5.14", | ||
"author": "Jeff Dickey @jdxcode", | ||
@@ -6,0 +6,0 @@ "bugs": "https://github.com/oclif/core/issues", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
183789
4751
10
0
104