1 // this file handles outputting usage instructions,
2 // failures, etc. keeps logging in one place.
3 var cliui = require('cliui'),
4 decamelize = require('decamelize'),
5 wsize = require('window-size')
7 module.exports = function (yargs) {
10 // methods for ouputting/building failure message.
12 self.failFn = function (f) {
16 var failMessage = null
17 var showHelpOnFail = true
18 self.showHelpOnFail = function (enabled, message) {
19 if (typeof enabled === 'string') {
22 } else if (typeof enabled === 'undefined') {
26 showHelpOnFail = enabled
30 self.fail = function (msg) {
32 fails.forEach(function (f) {
36 if (showHelpOnFail) yargs.showHelp('error')
37 if (msg) console.error(msg)
39 if (msg) console.error('')
40 console.error(failMessage)
42 if (yargs.getExitProcess()) {
50 // methods for ouputting/building help (usage) message.
52 self.usage = function (msg) {
57 self.example = function (cmd, description) {
58 examples.push([cmd, description || ''])
62 self.command = function (cmd, description) {
63 commands.push([cmd, description || ''])
65 self.getCommands = function () {
70 self.describe = function (key, desc) {
71 if (typeof key === 'object') {
72 Object.keys(key).forEach(function (k) {
73 self.describe(k, key[k])
76 descriptions[key] = desc
79 self.getDescriptions = function () {
84 self.epilog = function (msg) {
88 var wrap = windowWidth()
89 self.wrap = function (cols) {
93 self.help = function () {
96 var demanded = yargs.getDemanded(),
97 options = yargs.getOptions(),
99 Object.keys(descriptions)
100 .concat(Object.keys(demanded))
101 .concat(Object.keys(options.default))
102 .reduce(function (acc, key) {
103 if (key !== '_') acc[key] = true
114 var u = usage.replace(/\$0/g, yargs.$0)
118 // your application's commands, i.e., non-option
119 // arguments populated in '_'.
120 if (commands.length) {
123 commands.forEach(function (command) {
125 {text: command[0], padding: [0, 2, 0, 2], width: maxWidth(commands) + 4},
133 // the options table.
134 var aliasKeys = (Object.keys(options.alias) || [])
135 .concat(Object.keys(yargs.parsed.newAliases) || [])
137 keys = keys.filter(function (key) {
138 return !yargs.parsed.newAliases[key] && aliasKeys.every(function (alias) {
139 return (options.alias[alias] || []).indexOf(key) === -1
143 var switches = keys.reduce(function (acc, key) {
144 acc[key] = [ key ].concat(options.alias[key] || [])
146 return (sw.length > 1 ? '--' : '-') + sw
156 keys.forEach(function (key) {
157 var kswitch = switches[key]
158 var desc = descriptions[key] || ''
161 if (~options.boolean.indexOf(key)) type = '[boolean]'
162 if (~options.count.indexOf(key)) type = '[count]'
163 if (~options.string.indexOf(key)) type = '[string]'
164 if (~options.normalize.indexOf(key)) type = '[string]'
165 if (~options.array.indexOf(key)) type = '[array]'
169 demanded[key] ? '[required]' : null,
170 defaultString(options.default[key], options.defaultDescription[key])
171 ].filter(Boolean).join(' ')
174 {text: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches) + 4},
178 if (extra) ui.div({text: extra, padding: [0, 0, 0, 2], align: 'right'})
185 // describe some common use-cases for your application.
186 if (examples.length) {
189 examples.forEach(function (example) {
190 example[0] = example[0].replace(/\$0/g, yargs.$0)
193 examples.forEach(function (example) {
195 {text: example[0], padding: [0, 2, 0, 2], width: maxWidth(examples) + 4},
205 var e = epilog.replace(/\$0/g, yargs.$0)
212 // return the maximum width of a string
213 // in the left-hand column of a table.
214 function maxWidth (table) {
217 // table might be of the form [leftColumn],
218 // or {key: leftColumn}}
219 if (!Array.isArray(table)) {
220 table = Object.keys(table).map(function (key) {
225 table.forEach(function (v) {
226 width = Math.max(v[0].length, width)
229 // if we've enabled 'wrap' we should limit
230 // the max-width of the left-column.
231 if (wrap) width = Math.min(width, parseInt(wrap * 0.5, 10))
236 // make sure any options set for aliases,
237 // are copied to the keys being aliased.
238 function normalizeAliases () {
239 var options = yargs.getOptions(),
240 demanded = yargs.getDemanded()
242 ;(Object.keys(options.alias) || []).forEach(function (key) {
243 options.alias[key].forEach(function (alias) {
244 // copy descriptions.
245 if (descriptions[alias]) self.describe(key, descriptions[alias])
247 if (demanded[alias]) yargs.demand(key, demanded[alias].msg)
250 if (~options.boolean.indexOf(alias)) yargs.boolean(key)
251 if (~options.count.indexOf(alias)) yargs.count(key)
252 if (~options.string.indexOf(alias)) yargs.string(key)
253 if (~options.normalize.indexOf(alias)) yargs.normalize(key)
254 if (~options.array.indexOf(alias)) yargs.array(key)
259 self.showHelp = function (level) {
260 level = level || 'error'
261 console[level](self.help())
264 self.functionDescription = function (fn, defaultDescription) {
265 if (defaultDescription) {
266 return defaultDescription
268 var description = fn.name ? decamelize(fn.name, '-') : 'generated-value'
269 return ['(', description, ')'].join('')
272 // format the default-value-string displayed in
273 // the right-hand column.
274 function defaultString (value, defaultDescription) {
275 var string = '[default: '
277 if (value === undefined) return null
279 if (defaultDescription) {
280 string += defaultDescription
282 switch (typeof value) {
284 string += JSON.stringify(value)
287 string += JSON.stringify(value)
297 // guess the width of the console window, max-width 80.
298 function windowWidth () {
299 return wsize.width ? Math.min(80, wsize.width) : null
302 // logic for displaying application version.
304 self.version = function (ver, opt, msg) {
308 self.showVersion = function () {
309 if (typeof version === 'function') console.log(version())
310 else console.log(version)