| 'use strict' | 
| // this file handles outputting usage instructions, | 
| // failures, etc. keeps logging in one place. | 
| const decamelize = require('./decamelize') | 
| const stringWidth = require('string-width') | 
| const objFilter = require('./obj-filter') | 
| const path = require('path') | 
| const setBlocking = require('set-blocking') | 
| const YError = require('./yerror') | 
|   | 
| module.exports = function usage (yargs, y18n) { | 
|   const __ = y18n.__ | 
|   const self = {} | 
|   | 
|   // methods for ouputting/building failure message. | 
|   const fails = [] | 
|   self.failFn = function failFn (f) { | 
|     fails.push(f) | 
|   } | 
|   | 
|   let failMessage = null | 
|   let showHelpOnFail = true | 
|   self.showHelpOnFail = function showHelpOnFailFn (enabled, message) { | 
|     if (typeof enabled === 'string') { | 
|       message = enabled | 
|       enabled = true | 
|     } else if (typeof enabled === 'undefined') { | 
|       enabled = true | 
|     } | 
|     failMessage = message | 
|     showHelpOnFail = enabled | 
|     return self | 
|   } | 
|   | 
|   let failureOutput = false | 
|   self.fail = function fail (msg, err) { | 
|     const logger = yargs._getLoggerInstance() | 
|   | 
|     if (fails.length) { | 
|       for (let i = fails.length - 1; i >= 0; --i) { | 
|         fails[i](msg, err, self) | 
|       } | 
|     } else { | 
|       if (yargs.getExitProcess()) setBlocking(true) | 
|   | 
|       // don't output failure message more than once | 
|       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._hasParseCallback()) { | 
|         return yargs.exit(1, err) | 
|       } else { | 
|         throw err | 
|       } | 
|     } | 
|   } | 
|   | 
|   // methods for ouputting/building help (usage) message. | 
|   let usages = [] | 
|   let usageDisabled = false | 
|   self.usage = (msg, description) => { | 
|     if (msg === null) { | 
|       usageDisabled = true | 
|       usages = [] | 
|       return | 
|     } | 
|     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) { | 
|     // the last default wins, so cancel out any previously set default | 
|     if (isDefault) { | 
|       commands = commands.map((cmdArray) => { | 
|         cmdArray[2] = false | 
|         return cmdArray | 
|       }) | 
|     } | 
|     commands.push([cmd, description || '', isDefault, aliases]) | 
|   } | 
|   self.getCommands = () => commands | 
|   | 
|   let descriptions = {} | 
|   self.describe = function describe (key, desc) { | 
|     if (typeof key === 'object') { | 
|       Object.keys(key).forEach((k) => { | 
|         self.describe(k, key[k]) | 
|       }) | 
|     } else { | 
|       descriptions[key] = desc | 
|     } | 
|   } | 
|   self.getDescriptions = () => descriptions | 
|   | 
|   let epilog | 
|   self.epilog = (msg) => { | 
|     epilog = 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 | 
|   | 
|   const defaultGroup = 'Options:' | 
|   self.help = function help () { | 
|     normalizeAliases() | 
|   | 
|     // handle old demanded API | 
|     const base$0 = path.basename(yargs.$0) | 
|     const demandedOptions = yargs.getDemandedOptions() | 
|     const demandedCommands = yargs.getDemandedCommands() | 
|     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 = require('cliui')({ | 
|       width: theWrap, | 
|       wrap: !!theWrap | 
|     }) | 
|   | 
|     // the usage string. | 
|     if (!usageDisabled) { | 
|       if (usages.length) { | 
|         // user-defined usage. | 
|         usages.forEach((usage) => { | 
|           ui.div(`${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 | 
|         // demonstrate how commands are used. | 
|         if (demandedCommands._) { | 
|           u = `${base$0} <${__('command')}>\n` | 
|         } else { | 
|           u = `${base$0} [${__('command')}]\n` | 
|         } | 
|         ui.div(`${u}`) | 
|       } | 
|     } | 
|   | 
|     // your application's commands, i.e., non-option | 
|     // arguments populated in '_'. | 
|     if (commands.length) { | 
|       ui.div(__('Commands:')) | 
|   | 
|       const context = yargs.getContext() | 
|       const parentCommands = context.commands.length ? `${context.commands.join(' ')} ` : '' | 
|   | 
|       if (yargs.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 ?/, '')}` // drop $0 from default commands. | 
|         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:').slice(0, -1)}]`) // TODO hacking around i18n here | 
|         if (command[3] && command[3].length) { | 
|           hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`) | 
|         } | 
|         if (hints.length) { | 
|           ui.div({ text: hints.join(' '), padding: [0, 0, 0, 2], align: 'right' }) | 
|         } else { | 
|           ui.div() | 
|         } | 
|       }) | 
|   | 
|       ui.div() | 
|     } | 
|   | 
|     // perform some cleanup on the keys array, making it | 
|     // only include top-level keys not their aliases. | 
|     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)) | 
|   | 
|     // populate 'Options:' group with any keys that have not | 
|     // explicitly had a group set. | 
|     if (!groups[defaultGroup]) groups[defaultGroup] = [] | 
|     addUngroupedKeys(keys, options.alias, groups) | 
|   | 
|     // display 'Options:' table along with any custom tables: | 
|     Object.keys(groups).forEach((groupName) => { | 
|       if (!groups[groupName].length) return | 
|   | 
|       // if we've grouped the key 'f', but 'f' aliases 'foobar', | 
|       // normalizedKeys should contain only 'foobar'. | 
|       const normalizedKeys = groups[groupName].filter(filterHiddenOptions).map((key) => { | 
|         if (~aliasKeys.indexOf(key)) return key | 
|         for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) { | 
|           if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey | 
|         } | 
|         return key | 
|       }) | 
|   | 
|       if (normalizedKeys.length < 1) return | 
|   | 
|       ui.div(__(groupName)) | 
|   | 
|       // actually generate the switches string --foo, -f, --bar. | 
|       const switches = normalizedKeys.reduce((acc, key) => { | 
|         acc[key] = [ key ].concat(options.alias[key] || []) | 
|           .map(sw => { | 
|             // for the special positional group don't | 
|             // add '--' or '-' prefix. | 
|             if (groupName === self.getPositionalGroupName()) return sw | 
|             else return (sw.length > 1 ? '--' : '-') + sw | 
|           }) | 
|           .join(', ') | 
|   | 
|         return acc | 
|       }, {}) | 
|   | 
|       normalizedKeys.forEach((key) => { | 
|         const kswitch = switches[key] | 
|         let desc = descriptions[key] || '' | 
|         let type = null | 
|   | 
|         if (~desc.lastIndexOf(deferY18nLookupPrefix)) desc = __(desc.substring(deferY18nLookupPrefix.length)) | 
|   | 
|         if (~options.boolean.indexOf(key)) type = `[${__('boolean')}]` | 
|         if (~options.count.indexOf(key)) type = `[${__('count')}]` | 
|         if (~options.string.indexOf(key)) type = `[${__('string')}]` | 
|         if (~options.normalize.indexOf(key)) type = `[${__('string')}]` | 
|         if (~options.array.indexOf(key)) type = `[${__('array')}]` | 
|         if (~options.number.indexOf(key)) type = `[${__('number')}]` | 
|   | 
|         const extra = [ | 
|           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: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches, theWrap) + 4 }, | 
|           desc | 
|         ) | 
|   | 
|         if (extra) ui.div({ text: extra, padding: [0, 0, 0, 2], align: 'right' }) | 
|         else ui.div() | 
|       }) | 
|   | 
|       ui.div() | 
|     }) | 
|   | 
|     // describe some common use-cases for your application. | 
|     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() | 
|     } | 
|   | 
|     // the usage string. | 
|     if (epilog) { | 
|       const e = epilog.replace(/\$0/g, base$0) | 
|       ui.div(`${e}\n`) | 
|     } | 
|   | 
|     // Remove the trailing white spaces | 
|     return ui.toString().replace(/\s*$/, '') | 
|   } | 
|   | 
|   // return the maximum width of a string | 
|   // in the left-hand column of a table. | 
|   function maxWidth (table, theWrap, modifier) { | 
|     let width = 0 | 
|   | 
|     // table might be of the form [leftColumn], | 
|     // or {key: leftColumn} | 
|     if (!Array.isArray(table)) { | 
|       table = Object.keys(table).map(key => [table[key]]) | 
|     } | 
|   | 
|     table.forEach((v) => { | 
|       width = Math.max( | 
|         stringWidth(modifier ? `${modifier} ${v[0]}` : v[0]), | 
|         width | 
|       ) | 
|     }) | 
|   | 
|     // if we've enabled 'wrap' we should limit | 
|     // the max-width of the left-column. | 
|     if (theWrap) width = Math.min(width, parseInt(theWrap * 0.5, 10)) | 
|   | 
|     return width | 
|   } | 
|   | 
|   // make sure any options set for aliases, | 
|   // are copied to the keys being aliased. | 
|   function normalizeAliases () { | 
|     // handle old demanded API | 
|     const demandedOptions = yargs.getDemandedOptions() | 
|     const options = yargs.getOptions() | 
|   | 
|     ;(Object.keys(options.alias) || []).forEach((key) => { | 
|       options.alias[key].forEach((alias) => { | 
|         // copy descriptions. | 
|         if (descriptions[alias]) self.describe(key, descriptions[alias]) | 
|         // copy demanded. | 
|         if (alias in demandedOptions) yargs.demandOption(key, demandedOptions[alias]) | 
|         // type messages. | 
|         if (~options.boolean.indexOf(alias)) yargs.boolean(key) | 
|         if (~options.count.indexOf(alias)) yargs.count(key) | 
|         if (~options.string.indexOf(alias)) yargs.string(key) | 
|         if (~options.normalize.indexOf(alias)) yargs.normalize(key) | 
|         if (~options.array.indexOf(alias)) yargs.array(key) | 
|         if (~options.number.indexOf(alias)) yargs.number(key) | 
|       }) | 
|     }) | 
|   } | 
|   | 
|   // given a set of keys, place any keys that are | 
|   // ungrouped under the 'Options:' grouping. | 
|   function addUngroupedKeys (keys, aliases, groups) { | 
|     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._getLoggerInstance() | 
|     if (!level) level = 'error' | 
|     const emit = typeof level === 'function' ? level : logger[level] | 
|     emit(self.help()) | 
|   } | 
|   | 
|   self.functionDescription = (fn) => { | 
|     const description = fn.name ? 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 | 
|   } | 
|   | 
|   // format the default-value-string displayed in | 
|   // the right-hand column. | 
|   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}]` | 
|   } | 
|   | 
|   // guess the width of the console window, max-width 80. | 
|   function windowWidth () { | 
|     const maxWidth = 80 | 
|     if (typeof process === 'object' && process.stdout && process.stdout.columns) { | 
|       return Math.min(maxWidth, process.stdout.columns) | 
|     } else { | 
|       return maxWidth | 
|     } | 
|   } | 
|   | 
|   // logic for displaying application version. | 
|   let version = null | 
|   self.version = (ver) => { | 
|     version = ver | 
|   } | 
|   | 
|   self.showVersion = () => { | 
|     const logger = yargs._getLoggerInstance() | 
|     logger.log(version) | 
|   } | 
|   | 
|   self.reset = function reset (localLookup) { | 
|     // do not reset wrap here | 
|     // do not reset fails here | 
|     failMessage = null | 
|     failureOutput = false | 
|     usages = [] | 
|     usageDisabled = false | 
|     epilog = undefined | 
|     examples = [] | 
|     commands = [] | 
|     descriptions = objFilter(descriptions, (k, v) => !localLookup[k]) | 
|     return self | 
|   } | 
|   | 
|   let frozen | 
|   self.freeze = function freeze () { | 
|     frozen = {} | 
|     frozen.failMessage = failMessage | 
|     frozen.failureOutput = failureOutput | 
|     frozen.usages = usages | 
|     frozen.usageDisabled = usageDisabled | 
|     frozen.epilog = epilog | 
|     frozen.examples = examples | 
|     frozen.commands = commands | 
|     frozen.descriptions = descriptions | 
|   } | 
|   self.unfreeze = function unfreeze () { | 
|     failMessage = frozen.failMessage | 
|     failureOutput = frozen.failureOutput | 
|     usages = frozen.usages | 
|     usageDisabled = frozen.usageDisabled | 
|     epilog = frozen.epilog | 
|     examples = frozen.examples | 
|     commands = frozen.commands | 
|     descriptions = frozen.descriptions | 
|     frozen = undefined | 
|   } | 
|   | 
|   return self | 
| } |