| import { objFilter } from './utils/obj-filter.js'; | |
| import { YError } from './yerror.js'; | |
| import setBlocking from './utils/set-blocking.js'; | |
| function isBoolean(fail) { | |
| return typeof fail === 'boolean'; | |
| } | |
| export function usage(yargs, shim) { | |
| const __ = shim.y18n.__; | |
| const self = {}; | |
| const fails = []; | |
| self.failFn = function failFn(f) { | |
| fails.push(f); | |
| }; | |
| let failMessage = null; | |
| let showHelpOnFail = true; | |
| self.showHelpOnFail = function showHelpOnFailFn(arg1 = true, arg2) { | |
| function parseFunctionArgs() { | |
| return typeof arg1 === 'string' ? [true, arg1] : [arg1, arg2]; | |
| } | |
| const [enabled, message] = parseFunctionArgs(); | |
| failMessage = message; | |
| showHelpOnFail = enabled; | |
| return self; | |
| }; | |
| let failureOutput = false; | |
| self.fail = function fail(msg, err) { | |
| const logger = yargs.getInternalMethods().getLoggerInstance(); | |
| if (fails.length) { | |
| for (let i = fails.length - 1; i >= 0; --i) { | |
| const fail = fails[i]; | |
| if (isBoolean(fail)) { | |
| if (err) | |
| throw err; | |
| else if (msg) | |
| throw Error(msg); | |
| } | |
| else { | |
| fail(msg, err, self); | |
| } | |
| } | |
| } | |
| else { | |
| if (yargs.getExitProcess()) | |
| setBlocking(true); | |
| if (!failureOutput) { | |
| failureOutput = true; | |
| if (showHelpOnFail) { | |
| yargs.showHelp('error'); | |
| logger.error(); | |
| } | |
| if (msg || err) | |
| logger.error(msg || err); | |
| if (failMessage) { | |
| if (msg || err) | |
| logger.error(''); | |
| logger.error(failMessage); | |
| } | |
| } | |
| err = err || new YError(msg); | |
| if (yargs.getExitProcess()) { | |
| return yargs.exit(1); | |
| } | |
| else if (yargs.getInternalMethods().hasParseCallback()) { | |
| return yargs.exit(1, err); | |
| } | |
| else { | |
| throw err; | |
| } | |
| } | |
| }; | |
| let usages = []; | |
| let usageDisabled = false; | |
| self.usage = (msg, description) => { | |
| if (msg === null) { | |
| usageDisabled = true; | |
| usages = []; | |
| return self; | |
| } | |
| usageDisabled = false; | |
| usages.push([msg, description || '']); | |
| return self; | |
| }; | |
| self.getUsage = () => { | |
| return usages; | |
| }; | |
| self.getUsageDisabled = () => { | |
| return usageDisabled; | |
| }; | |
| self.getPositionalGroupName = () => { | |
| return __('Positionals:'); | |
| }; | |
| let examples = []; | |
| self.example = (cmd, description) => { | |
| examples.push([cmd, description || '']); | |
| }; | |
| let commands = []; | |
| self.command = function command(cmd, description, isDefault, aliases, deprecated = false) { | |
| if (isDefault) { | |
| commands = commands.map(cmdArray => { | |
| cmdArray[2] = false; | |
| return cmdArray; | |
| }); | |
| } | |
| commands.push([cmd, description || '', isDefault, aliases, deprecated]); | |
| }; | |
| self.getCommands = () => commands; | |
| let descriptions = {}; | |
| self.describe = function describe(keyOrKeys, desc) { | |
| if (Array.isArray(keyOrKeys)) { | |
| keyOrKeys.forEach(k => { | |
| self.describe(k, desc); | |
| }); | |
| } | |
| else if (typeof keyOrKeys === 'object') { | |
| Object.keys(keyOrKeys).forEach(k => { | |
| self.describe(k, keyOrKeys[k]); | |
| }); | |
| } | |
| else { | |
| descriptions[keyOrKeys] = desc; | |
| } | |
| }; | |
| self.getDescriptions = () => descriptions; | |
| let epilogs = []; | |
| self.epilog = msg => { | |
| epilogs.push(msg); | |
| }; | |
| let wrapSet = false; | |
| let wrap; | |
| self.wrap = cols => { | |
| wrapSet = true; | |
| wrap = cols; | |
| }; | |
| function getWrap() { | |
| if (!wrapSet) { | |
| wrap = windowWidth(); | |
| wrapSet = true; | |
| } | |
| return wrap; | |
| } | |
| const deferY18nLookupPrefix = '__yargsString__:'; | |
| self.deferY18nLookup = str => deferY18nLookupPrefix + str; | |
| self.help = function help() { | |
| if (cachedHelpMessage) | |
| return cachedHelpMessage; | |
| normalizeAliases(); | |
| const base$0 = yargs.customScriptName | |
| ? yargs.$0 | |
| : shim.path.basename(yargs.$0); | |
| const demandedOptions = yargs.getDemandedOptions(); | |
| const demandedCommands = yargs.getDemandedCommands(); | |
| const deprecatedOptions = yargs.getDeprecatedOptions(); | |
| const groups = yargs.getGroups(); | |
| const options = yargs.getOptions(); | |
| let keys = []; | |
| keys = keys.concat(Object.keys(descriptions)); | |
| keys = keys.concat(Object.keys(demandedOptions)); | |
| keys = keys.concat(Object.keys(demandedCommands)); | |
| keys = keys.concat(Object.keys(options.default)); | |
| keys = keys.filter(filterHiddenOptions); | |
| keys = Object.keys(keys.reduce((acc, key) => { | |
| if (key !== '_') | |
| acc[key] = true; | |
| return acc; | |
| }, {})); | |
| const theWrap = getWrap(); | |
| const ui = shim.cliui({ | |
| width: theWrap, | |
| wrap: !!theWrap, | |
| }); | |
| if (!usageDisabled) { | |
| if (usages.length) { | |
| usages.forEach(usage => { | |
| ui.div({ text: `${usage[0].replace(/\$0/g, base$0)}` }); | |
| if (usage[1]) { | |
| ui.div({ text: `${usage[1]}`, padding: [1, 0, 0, 0] }); | |
| } | |
| }); | |
| ui.div(); | |
| } | |
| else if (commands.length) { | |
| let u = null; | |
| if (demandedCommands._) { | |
| u = `${base$0} <${__('command')}>\n`; | |
| } | |
| else { | |
| u = `${base$0} [${__('command')}]\n`; | |
| } | |
| ui.div(`${u}`); | |
| } | |
| } | |
| if (commands.length > 1 || (commands.length === 1 && !commands[0][2])) { | |
| ui.div(__('Commands:')); | |
| const context = yargs.getInternalMethods().getContext(); | |
| const parentCommands = context.commands.length | |
| ? `${context.commands.join(' ')} ` | |
| : ''; | |
| if (yargs.getInternalMethods().getParserConfiguration()['sort-commands'] === | |
| true) { | |
| commands = commands.sort((a, b) => a[0].localeCompare(b[0])); | |
| } | |
| commands.forEach(command => { | |
| const commandString = `${base$0} ${parentCommands}${command[0].replace(/^\$0 ?/, '')}`; | |
| ui.span({ | |
| text: commandString, | |
| padding: [0, 2, 0, 2], | |
| width: maxWidth(commands, theWrap, `${base$0}${parentCommands}`) + 4, | |
| }, { text: command[1] }); | |
| const hints = []; | |
| if (command[2]) | |
| hints.push(`[${__('default')}]`); | |
| if (command[3] && command[3].length) { | |
| hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`); | |
| } | |
| if (command[4]) { | |
| if (typeof command[4] === 'string') { | |
| hints.push(`[${__('deprecated: %s', command[4])}]`); | |
| } | |
| else { | |
| hints.push(`[${__('deprecated')}]`); | |
| } | |
| } | |
| if (hints.length) { | |
| ui.div({ | |
| text: hints.join(' '), | |
| padding: [0, 0, 0, 2], | |
| align: 'right', | |
| }); | |
| } | |
| else { | |
| ui.div(); | |
| } | |
| }); | |
| ui.div(); | |
| } | |
| const aliasKeys = (Object.keys(options.alias) || []).concat(Object.keys(yargs.parsed.newAliases) || []); | |
| keys = keys.filter(key => !yargs.parsed.newAliases[key] && | |
| aliasKeys.every(alias => (options.alias[alias] || []).indexOf(key) === -1)); | |
| const defaultGroup = __('Options:'); | |
| if (!groups[defaultGroup]) | |
| groups[defaultGroup] = []; | |
| addUngroupedKeys(keys, options.alias, groups, defaultGroup); | |
| const isLongSwitch = (sw) => /^--/.test(getText(sw)); | |
| const displayedGroups = Object.keys(groups) | |
| .filter(groupName => groups[groupName].length > 0) | |
| .map(groupName => { | |
| const normalizedKeys = groups[groupName] | |
| .filter(filterHiddenOptions) | |
| .map(key => { | |
| if (aliasKeys.includes(key)) | |
| return key; | |
| for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) { | |
| if ((options.alias[aliasKey] || []).includes(key)) | |
| return aliasKey; | |
| } | |
| return key; | |
| }); | |
| return { groupName, normalizedKeys }; | |
| }) | |
| .filter(({ normalizedKeys }) => normalizedKeys.length > 0) | |
| .map(({ groupName, normalizedKeys }) => { | |
| const switches = normalizedKeys.reduce((acc, key) => { | |
| acc[key] = [key] | |
| .concat(options.alias[key] || []) | |
| .map(sw => { | |
| if (groupName === self.getPositionalGroupName()) | |
| return sw; | |
| else { | |
| return ((/^[0-9]$/.test(sw) | |
| ? options.boolean.includes(key) | |
| ? '-' | |
| : '--' | |
| : sw.length > 1 | |
| ? '--' | |
| : '-') + sw); | |
| } | |
| }) | |
| .sort((sw1, sw2) => isLongSwitch(sw1) === isLongSwitch(sw2) | |
| ? 0 | |
| : isLongSwitch(sw1) | |
| ? 1 | |
| : -1) | |
| .join(', '); | |
| return acc; | |
| }, {}); | |
| return { groupName, normalizedKeys, switches }; | |
| }); | |
| const shortSwitchesUsed = displayedGroups | |
| .filter(({ groupName }) => groupName !== self.getPositionalGroupName()) | |
| .some(({ normalizedKeys, switches }) => !normalizedKeys.every(key => isLongSwitch(switches[key]))); | |
| if (shortSwitchesUsed) { | |
| displayedGroups | |
| .filter(({ groupName }) => groupName !== self.getPositionalGroupName()) | |
| .forEach(({ normalizedKeys, switches }) => { | |
| normalizedKeys.forEach(key => { | |
| if (isLongSwitch(switches[key])) { | |
| switches[key] = addIndentation(switches[key], '-x, '.length); | |
| } | |
| }); | |
| }); | |
| } | |
| displayedGroups.forEach(({ groupName, normalizedKeys, switches }) => { | |
| ui.div(groupName); | |
| normalizedKeys.forEach(key => { | |
| const kswitch = switches[key]; | |
| let desc = descriptions[key] || ''; | |
| let type = null; | |
| if (desc.includes(deferY18nLookupPrefix)) | |
| desc = __(desc.substring(deferY18nLookupPrefix.length)); | |
| if (options.boolean.includes(key)) | |
| type = `[${__('boolean')}]`; | |
| if (options.count.includes(key)) | |
| type = `[${__('count')}]`; | |
| if (options.string.includes(key)) | |
| type = `[${__('string')}]`; | |
| if (options.normalize.includes(key)) | |
| type = `[${__('string')}]`; | |
| if (options.array.includes(key)) | |
| type = `[${__('array')}]`; | |
| if (options.number.includes(key)) | |
| type = `[${__('number')}]`; | |
| const deprecatedExtra = (deprecated) => typeof deprecated === 'string' | |
| ? `[${__('deprecated: %s', deprecated)}]` | |
| : `[${__('deprecated')}]`; | |
| const extra = [ | |
| key in deprecatedOptions | |
| ? deprecatedExtra(deprecatedOptions[key]) | |
| : null, | |
| type, | |
| key in demandedOptions ? `[${__('required')}]` : null, | |
| options.choices && options.choices[key] | |
| ? `[${__('choices:')} ${self.stringifiedValues(options.choices[key])}]` | |
| : null, | |
| defaultString(options.default[key], options.defaultDescription[key]), | |
| ] | |
| .filter(Boolean) | |
| .join(' '); | |
| ui.span({ | |
| text: getText(kswitch), | |
| padding: [0, 2, 0, 2 + getIndentation(kswitch)], | |
| width: maxWidth(switches, theWrap) + 4, | |
| }, desc); | |
| if (extra) | |
| ui.div({ text: extra, padding: [0, 0, 0, 2], align: 'right' }); | |
| else | |
| ui.div(); | |
| }); | |
| ui.div(); | |
| }); | |
| if (examples.length) { | |
| ui.div(__('Examples:')); | |
| examples.forEach(example => { | |
| example[0] = example[0].replace(/\$0/g, base$0); | |
| }); | |
| examples.forEach(example => { | |
| if (example[1] === '') { | |
| ui.div({ | |
| text: example[0], | |
| padding: [0, 2, 0, 2], | |
| }); | |
| } | |
| else { | |
| ui.div({ | |
| text: example[0], | |
| padding: [0, 2, 0, 2], | |
| width: maxWidth(examples, theWrap) + 4, | |
| }, { | |
| text: example[1], | |
| }); | |
| } | |
| }); | |
| ui.div(); | |
| } | |
| if (epilogs.length > 0) { | |
| const e = epilogs | |
| .map(epilog => epilog.replace(/\$0/g, base$0)) | |
| .join('\n'); | |
| ui.div(`${e}\n`); | |
| } | |
| return ui.toString().replace(/\s*$/, ''); | |
| }; | |
| function maxWidth(table, theWrap, modifier) { | |
| let width = 0; | |
| if (!Array.isArray(table)) { | |
| table = Object.values(table).map(v => [v]); | |
| } | |
| table.forEach(v => { | |
| width = Math.max(shim.stringWidth(modifier ? `${modifier} ${getText(v[0])}` : getText(v[0])) + getIndentation(v[0]), width); | |
| }); | |
| if (theWrap) | |
| width = Math.min(width, parseInt((theWrap * 0.5).toString(), 10)); | |
| return width; | |
| } | |
| function normalizeAliases() { | |
| const demandedOptions = yargs.getDemandedOptions(); | |
| const options = yargs.getOptions(); | |
| (Object.keys(options.alias) || []).forEach(key => { | |
| options.alias[key].forEach(alias => { | |
| if (descriptions[alias]) | |
| self.describe(key, descriptions[alias]); | |
| if (alias in demandedOptions) | |
| yargs.demandOption(key, demandedOptions[alias]); | |
| if (options.boolean.includes(alias)) | |
| yargs.boolean(key); | |
| if (options.count.includes(alias)) | |
| yargs.count(key); | |
| if (options.string.includes(alias)) | |
| yargs.string(key); | |
| if (options.normalize.includes(alias)) | |
| yargs.normalize(key); | |
| if (options.array.includes(alias)) | |
| yargs.array(key); | |
| if (options.number.includes(alias)) | |
| yargs.number(key); | |
| }); | |
| }); | |
| } | |
| let cachedHelpMessage; | |
| self.cacheHelpMessage = function () { | |
| cachedHelpMessage = this.help(); | |
| }; | |
| self.clearCachedHelpMessage = function () { | |
| cachedHelpMessage = undefined; | |
| }; | |
| self.hasCachedHelpMessage = function () { | |
| return !!cachedHelpMessage; | |
| }; | |
| function addUngroupedKeys(keys, aliases, groups, defaultGroup) { | |
| let groupedKeys = []; | |
| let toCheck = null; | |
| Object.keys(groups).forEach(group => { | |
| groupedKeys = groupedKeys.concat(groups[group]); | |
| }); | |
| keys.forEach(key => { | |
| toCheck = [key].concat(aliases[key]); | |
| if (!toCheck.some(k => groupedKeys.indexOf(k) !== -1)) { | |
| groups[defaultGroup].push(key); | |
| } | |
| }); | |
| return groupedKeys; | |
| } | |
| function filterHiddenOptions(key) { | |
| return (yargs.getOptions().hiddenOptions.indexOf(key) < 0 || | |
| yargs.parsed.argv[yargs.getOptions().showHiddenOpt]); | |
| } | |
| self.showHelp = (level) => { | |
| const logger = yargs.getInternalMethods().getLoggerInstance(); | |
| if (!level) | |
| level = 'error'; | |
| const emit = typeof level === 'function' ? level : logger[level]; | |
| emit(self.help()); | |
| }; | |
| self.functionDescription = fn => { | |
| const description = fn.name | |
| ? shim.Parser.decamelize(fn.name, '-') | |
| : __('generated-value'); | |
| return ['(', description, ')'].join(''); | |
| }; | |
| self.stringifiedValues = function stringifiedValues(values, separator) { | |
| let string = ''; | |
| const sep = separator || ', '; | |
| const array = [].concat(values); | |
| if (!values || !array.length) | |
| return string; | |
| array.forEach(value => { | |
| if (string.length) | |
| string += sep; | |
| string += JSON.stringify(value); | |
| }); | |
| return string; | |
| }; | |
| function defaultString(value, defaultDescription) { | |
| let string = `[${__('default:')} `; | |
| if (value === undefined && !defaultDescription) | |
| return null; | |
| if (defaultDescription) { | |
| string += defaultDescription; | |
| } | |
| else { | |
| switch (typeof value) { | |
| case 'string': | |
| string += `"${value}"`; | |
| break; | |
| case 'object': | |
| string += JSON.stringify(value); | |
| break; | |
| default: | |
| string += value; | |
| } | |
| } | |
| return `${string}]`; | |
| } | |
| function windowWidth() { | |
| const maxWidth = 80; | |
| if (shim.process.stdColumns) { | |
| return Math.min(maxWidth, shim.process.stdColumns); | |
| } | |
| else { | |
| return maxWidth; | |
| } | |
| } | |
| let version = null; | |
| self.version = ver => { | |
| version = ver; | |
| }; | |
| self.showVersion = level => { | |
| const logger = yargs.getInternalMethods().getLoggerInstance(); | |
| if (!level) | |
| level = 'error'; | |
| const emit = typeof level === 'function' ? level : logger[level]; | |
| emit(version); | |
| }; | |
| self.reset = function reset(localLookup) { | |
| failMessage = null; | |
| failureOutput = false; | |
| usages = []; | |
| usageDisabled = false; | |
| epilogs = []; | |
| examples = []; | |
| commands = []; | |
| descriptions = objFilter(descriptions, k => !localLookup[k]); | |
| return self; | |
| }; | |
| const frozens = []; | |
| self.freeze = function freeze() { | |
| frozens.push({ | |
| failMessage, | |
| failureOutput, | |
| usages, | |
| usageDisabled, | |
| epilogs, | |
| examples, | |
| commands, | |
| descriptions, | |
| }); | |
| }; | |
| self.unfreeze = function unfreeze() { | |
| const frozen = frozens.pop(); | |
| if (!frozen) | |
| return; | |
| ({ | |
| failMessage, | |
| failureOutput, | |
| usages, | |
| usageDisabled, | |
| epilogs, | |
| examples, | |
| commands, | |
| descriptions, | |
| } = frozen); | |
| }; | |
| return self; | |
| } | |
| function isIndentedText(text) { | |
| return typeof text === 'object'; | |
| } | |
| function addIndentation(text, indent) { | |
| return isIndentedText(text) | |
| ? { text: text.text, indentation: text.indentation + indent } | |
| : { text, indentation: indent }; | |
| } | |
| function getIndentation(text) { | |
| return isIndentedText(text) ? text.indentation : 0; | |
| } | |
| function getText(text) { | |
| return isIndentedText(text) ? text.text : text; | |
| } | |