source: trip-planner-front/node_modules/yargs/lib/usage.js@ 1ad8e64

Last change on this file since 1ad8e64 was e29cc2e, checked in by Ema <ema_spirova@…>, 3 years ago

primeNG components

  • Property mode set to 100644
File size: 15.1 KB
RevLine 
[6a3a178]1'use strict'
2// this file handles outputting usage instructions,
3// failures, etc. keeps logging in one place.
4const decamelize = require('./decamelize')
5const stringWidth = require('string-width')
6const objFilter = require('./obj-filter')
7const path = require('path')
8const setBlocking = require('set-blocking')
9const YError = require('./yerror')
10
11module.exports = function usage (yargs, y18n) {
12 const __ = y18n.__
13 const self = {}
14
15 // methods for ouputting/building failure message.
16 const fails = []
17 self.failFn = function failFn (f) {
18 fails.push(f)
19 }
20
21 let failMessage = null
22 let showHelpOnFail = true
23 self.showHelpOnFail = function showHelpOnFailFn (enabled, message) {
24 if (typeof enabled === 'string') {
25 message = enabled
26 enabled = true
27 } else if (typeof enabled === 'undefined') {
28 enabled = true
29 }
30 failMessage = message
31 showHelpOnFail = enabled
32 return self
33 }
34
35 let failureOutput = false
36 self.fail = function fail (msg, err) {
37 const logger = yargs._getLoggerInstance()
38
39 if (fails.length) {
40 for (let i = fails.length - 1; i >= 0; --i) {
41 fails[i](msg, err, self)
42 }
43 } else {
44 if (yargs.getExitProcess()) setBlocking(true)
45
46 // don't output failure message more than once
47 if (!failureOutput) {
48 failureOutput = true
49 if (showHelpOnFail) {
50 yargs.showHelp('error')
51 logger.error()
52 }
53 if (msg || err) logger.error(msg || err)
54 if (failMessage) {
55 if (msg || err) logger.error('')
56 logger.error(failMessage)
57 }
58 }
59
60 err = err || new YError(msg)
61 if (yargs.getExitProcess()) {
62 return yargs.exit(1)
63 } else if (yargs._hasParseCallback()) {
64 return yargs.exit(1, err)
65 } else {
66 throw err
67 }
68 }
69 }
70
71 // methods for ouputting/building help (usage) message.
72 let usages = []
73 let usageDisabled = false
74 self.usage = (msg, description) => {
75 if (msg === null) {
76 usageDisabled = true
77 usages = []
78 return
79 }
80 usageDisabled = false
81 usages.push([msg, description || ''])
82 return self
83 }
84 self.getUsage = () => {
85 return usages
86 }
87 self.getUsageDisabled = () => {
88 return usageDisabled
89 }
90
91 self.getPositionalGroupName = () => {
92 return __('Positionals:')
93 }
94
95 let examples = []
96 self.example = (cmd, description) => {
97 examples.push([cmd, description || ''])
98 }
99
100 let commands = []
101 self.command = function command (cmd, description, isDefault, aliases) {
102 // the last default wins, so cancel out any previously set default
103 if (isDefault) {
104 commands = commands.map((cmdArray) => {
105 cmdArray[2] = false
106 return cmdArray
107 })
108 }
109 commands.push([cmd, description || '', isDefault, aliases])
110 }
111 self.getCommands = () => commands
112
113 let descriptions = {}
114 self.describe = function describe (key, desc) {
115 if (typeof key === 'object') {
116 Object.keys(key).forEach((k) => {
117 self.describe(k, key[k])
118 })
119 } else {
120 descriptions[key] = desc
121 }
122 }
123 self.getDescriptions = () => descriptions
124
125 let epilog
126 self.epilog = (msg) => {
127 epilog = msg
128 }
129
130 let wrapSet = false
131 let wrap
132 self.wrap = (cols) => {
133 wrapSet = true
134 wrap = cols
135 }
136
137 function getWrap () {
138 if (!wrapSet) {
139 wrap = windowWidth()
140 wrapSet = true
141 }
142
143 return wrap
144 }
145
146 const deferY18nLookupPrefix = '__yargsString__:'
147 self.deferY18nLookup = str => deferY18nLookupPrefix + str
148
149 const defaultGroup = 'Options:'
150 self.help = function help () {
151 normalizeAliases()
152
153 // handle old demanded API
154 const base$0 = path.basename(yargs.$0)
155 const demandedOptions = yargs.getDemandedOptions()
156 const demandedCommands = yargs.getDemandedCommands()
157 const groups = yargs.getGroups()
158 const options = yargs.getOptions()
159
160 let keys = []
161 keys = keys.concat(Object.keys(descriptions))
162 keys = keys.concat(Object.keys(demandedOptions))
163 keys = keys.concat(Object.keys(demandedCommands))
164 keys = keys.concat(Object.keys(options.default))
165 keys = keys.filter(filterHiddenOptions)
166 keys = Object.keys(keys.reduce((acc, key) => {
167 if (key !== '_') acc[key] = true
168 return acc
169 }, {}))
170
171 const theWrap = getWrap()
172 const ui = require('cliui')({
173 width: theWrap,
174 wrap: !!theWrap
175 })
176
177 // the usage string.
178 if (!usageDisabled) {
179 if (usages.length) {
180 // user-defined usage.
181 usages.forEach((usage) => {
182 ui.div(`${usage[0].replace(/\$0/g, base$0)}`)
183 if (usage[1]) {
184 ui.div({ text: `${usage[1]}`, padding: [1, 0, 0, 0] })
185 }
186 })
187 ui.div()
188 } else if (commands.length) {
189 let u = null
190 // demonstrate how commands are used.
191 if (demandedCommands._) {
192 u = `${base$0} <${__('command')}>\n`
193 } else {
194 u = `${base$0} [${__('command')}]\n`
195 }
196 ui.div(`${u}`)
197 }
198 }
199
200 // your application's commands, i.e., non-option
201 // arguments populated in '_'.
202 if (commands.length) {
203 ui.div(__('Commands:'))
204
205 const context = yargs.getContext()
206 const parentCommands = context.commands.length ? `${context.commands.join(' ')} ` : ''
207
208 if (yargs.getParserConfiguration()['sort-commands'] === true) {
209 commands = commands.sort((a, b) => a[0].localeCompare(b[0]))
210 }
211
212 commands.forEach((command) => {
213 const commandString = `${base$0} ${parentCommands}${command[0].replace(/^\$0 ?/, '')}` // drop $0 from default commands.
214 ui.span(
215 {
216 text: commandString,
217 padding: [0, 2, 0, 2],
218 width: maxWidth(commands, theWrap, `${base$0}${parentCommands}`) + 4
219 },
220 { text: command[1] }
221 )
222 const hints = []
223 if (command[2]) hints.push(`[${__('default:').slice(0, -1)}]`) // TODO hacking around i18n here
224 if (command[3] && command[3].length) {
225 hints.push(`[${__('aliases:')} ${command[3].join(', ')}]`)
226 }
227 if (hints.length) {
228 ui.div({ text: hints.join(' '), padding: [0, 0, 0, 2], align: 'right' })
229 } else {
230 ui.div()
231 }
232 })
233
234 ui.div()
235 }
236
237 // perform some cleanup on the keys array, making it
238 // only include top-level keys not their aliases.
239 const aliasKeys = (Object.keys(options.alias) || [])
240 .concat(Object.keys(yargs.parsed.newAliases) || [])
241
242 keys = keys.filter(key => !yargs.parsed.newAliases[key] && aliasKeys.every(alias => (options.alias[alias] || []).indexOf(key) === -1))
243
244 // populate 'Options:' group with any keys that have not
245 // explicitly had a group set.
246 if (!groups[defaultGroup]) groups[defaultGroup] = []
247 addUngroupedKeys(keys, options.alias, groups)
248
249 // display 'Options:' table along with any custom tables:
250 Object.keys(groups).forEach((groupName) => {
251 if (!groups[groupName].length) return
252
253 // if we've grouped the key 'f', but 'f' aliases 'foobar',
254 // normalizedKeys should contain only 'foobar'.
255 const normalizedKeys = groups[groupName].filter(filterHiddenOptions).map((key) => {
256 if (~aliasKeys.indexOf(key)) return key
257 for (let i = 0, aliasKey; (aliasKey = aliasKeys[i]) !== undefined; i++) {
258 if (~(options.alias[aliasKey] || []).indexOf(key)) return aliasKey
259 }
260 return key
261 })
262
263 if (normalizedKeys.length < 1) return
264
265 ui.div(__(groupName))
266
267 // actually generate the switches string --foo, -f, --bar.
268 const switches = normalizedKeys.reduce((acc, key) => {
269 acc[key] = [ key ].concat(options.alias[key] || [])
270 .map(sw => {
271 // for the special positional group don't
272 // add '--' or '-' prefix.
273 if (groupName === self.getPositionalGroupName()) return sw
274 else return (sw.length > 1 ? '--' : '-') + sw
275 })
276 .join(', ')
277
278 return acc
279 }, {})
280
281 normalizedKeys.forEach((key) => {
282 const kswitch = switches[key]
283 let desc = descriptions[key] || ''
284 let type = null
285
286 if (~desc.lastIndexOf(deferY18nLookupPrefix)) desc = __(desc.substring(deferY18nLookupPrefix.length))
287
288 if (~options.boolean.indexOf(key)) type = `[${__('boolean')}]`
289 if (~options.count.indexOf(key)) type = `[${__('count')}]`
290 if (~options.string.indexOf(key)) type = `[${__('string')}]`
291 if (~options.normalize.indexOf(key)) type = `[${__('string')}]`
292 if (~options.array.indexOf(key)) type = `[${__('array')}]`
293 if (~options.number.indexOf(key)) type = `[${__('number')}]`
294
295 const extra = [
296 type,
297 (key in demandedOptions) ? `[${__('required')}]` : null,
298 options.choices && options.choices[key] ? `[${__('choices:')} ${
299 self.stringifiedValues(options.choices[key])}]` : null,
300 defaultString(options.default[key], options.defaultDescription[key])
301 ].filter(Boolean).join(' ')
302
303 ui.span(
304 { text: kswitch, padding: [0, 2, 0, 2], width: maxWidth(switches, theWrap) + 4 },
305 desc
306 )
307
308 if (extra) ui.div({ text: extra, padding: [0, 0, 0, 2], align: 'right' })
309 else ui.div()
310 })
311
312 ui.div()
313 })
314
315 // describe some common use-cases for your application.
316 if (examples.length) {
317 ui.div(__('Examples:'))
318
319 examples.forEach((example) => {
320 example[0] = example[0].replace(/\$0/g, base$0)
321 })
322
323 examples.forEach((example) => {
324 if (example[1] === '') {
325 ui.div(
326 {
327 text: example[0],
328 padding: [0, 2, 0, 2]
329 }
330 )
331 } else {
332 ui.div(
333 {
334 text: example[0],
335 padding: [0, 2, 0, 2],
336 width: maxWidth(examples, theWrap) + 4
337 }, {
338 text: example[1]
339 }
340 )
341 }
342 })
343
344 ui.div()
345 }
346
347 // the usage string.
348 if (epilog) {
349 const e = epilog.replace(/\$0/g, base$0)
350 ui.div(`${e}\n`)
351 }
352
353 // Remove the trailing white spaces
354 return ui.toString().replace(/\s*$/, '')
355 }
356
357 // return the maximum width of a string
358 // in the left-hand column of a table.
359 function maxWidth (table, theWrap, modifier) {
360 let width = 0
361
362 // table might be of the form [leftColumn],
363 // or {key: leftColumn}
364 if (!Array.isArray(table)) {
365 table = Object.keys(table).map(key => [table[key]])
366 }
367
368 table.forEach((v) => {
369 width = Math.max(
370 stringWidth(modifier ? `${modifier} ${v[0]}` : v[0]),
371 width
372 )
373 })
374
375 // if we've enabled 'wrap' we should limit
376 // the max-width of the left-column.
377 if (theWrap) width = Math.min(width, parseInt(theWrap * 0.5, 10))
378
379 return width
380 }
381
382 // make sure any options set for aliases,
383 // are copied to the keys being aliased.
384 function normalizeAliases () {
385 // handle old demanded API
386 const demandedOptions = yargs.getDemandedOptions()
387 const options = yargs.getOptions()
388
389 ;(Object.keys(options.alias) || []).forEach((key) => {
390 options.alias[key].forEach((alias) => {
391 // copy descriptions.
392 if (descriptions[alias]) self.describe(key, descriptions[alias])
393 // copy demanded.
394 if (alias in demandedOptions) yargs.demandOption(key, demandedOptions[alias])
395 // type messages.
396 if (~options.boolean.indexOf(alias)) yargs.boolean(key)
397 if (~options.count.indexOf(alias)) yargs.count(key)
398 if (~options.string.indexOf(alias)) yargs.string(key)
399 if (~options.normalize.indexOf(alias)) yargs.normalize(key)
400 if (~options.array.indexOf(alias)) yargs.array(key)
401 if (~options.number.indexOf(alias)) yargs.number(key)
402 })
403 })
404 }
405
406 // given a set of keys, place any keys that are
407 // ungrouped under the 'Options:' grouping.
408 function addUngroupedKeys (keys, aliases, groups) {
409 let groupedKeys = []
410 let toCheck = null
411 Object.keys(groups).forEach((group) => {
412 groupedKeys = groupedKeys.concat(groups[group])
413 })
414
415 keys.forEach((key) => {
416 toCheck = [key].concat(aliases[key])
417 if (!toCheck.some(k => groupedKeys.indexOf(k) !== -1)) {
418 groups[defaultGroup].push(key)
419 }
420 })
421 return groupedKeys
422 }
423
424 function filterHiddenOptions (key) {
425 return yargs.getOptions().hiddenOptions.indexOf(key) < 0 || yargs.parsed.argv[yargs.getOptions().showHiddenOpt]
426 }
427
428 self.showHelp = (level) => {
429 const logger = yargs._getLoggerInstance()
430 if (!level) level = 'error'
431 const emit = typeof level === 'function' ? level : logger[level]
432 emit(self.help())
433 }
434
435 self.functionDescription = (fn) => {
436 const description = fn.name ? decamelize(fn.name, '-') : __('generated-value')
437 return ['(', description, ')'].join('')
438 }
439
440 self.stringifiedValues = function stringifiedValues (values, separator) {
441 let string = ''
442 const sep = separator || ', '
443 const array = [].concat(values)
444
445 if (!values || !array.length) return string
446
447 array.forEach((value) => {
448 if (string.length) string += sep
449 string += JSON.stringify(value)
450 })
451
452 return string
453 }
454
455 // format the default-value-string displayed in
456 // the right-hand column.
457 function defaultString (value, defaultDescription) {
458 let string = `[${__('default:')} `
459
460 if (value === undefined && !defaultDescription) return null
461
462 if (defaultDescription) {
463 string += defaultDescription
464 } else {
465 switch (typeof value) {
466 case 'string':
467 string += `"${value}"`
468 break
469 case 'object':
470 string += JSON.stringify(value)
471 break
472 default:
473 string += value
474 }
475 }
476
477 return `${string}]`
478 }
479
480 // guess the width of the console window, max-width 80.
481 function windowWidth () {
482 const maxWidth = 80
483 if (typeof process === 'object' && process.stdout && process.stdout.columns) {
484 return Math.min(maxWidth, process.stdout.columns)
485 } else {
486 return maxWidth
487 }
488 }
489
490 // logic for displaying application version.
491 let version = null
492 self.version = (ver) => {
493 version = ver
494 }
495
496 self.showVersion = () => {
497 const logger = yargs._getLoggerInstance()
498 logger.log(version)
499 }
500
501 self.reset = function reset (localLookup) {
502 // do not reset wrap here
503 // do not reset fails here
504 failMessage = null
505 failureOutput = false
506 usages = []
507 usageDisabled = false
508 epilog = undefined
509 examples = []
510 commands = []
511 descriptions = objFilter(descriptions, (k, v) => !localLookup[k])
512 return self
513 }
514
515 let frozen
516 self.freeze = function freeze () {
517 frozen = {}
518 frozen.failMessage = failMessage
519 frozen.failureOutput = failureOutput
520 frozen.usages = usages
521 frozen.usageDisabled = usageDisabled
522 frozen.epilog = epilog
523 frozen.examples = examples
524 frozen.commands = commands
525 frozen.descriptions = descriptions
526 }
527 self.unfreeze = function unfreeze () {
528 failMessage = frozen.failMessage
529 failureOutput = frozen.failureOutput
530 usages = frozen.usages
531 usageDisabled = frozen.usageDisabled
532 epilog = frozen.epilog
533 examples = frozen.examples
534 commands = frozen.commands
535 descriptions = frozen.descriptions
536 frozen = undefined
537 }
538
539 return self
540}
Note: See TracBrowser for help on using the repository browser.