| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 | 'use strict'const argsert = require('./lib/argsert')const fs = require('fs')const Command = require('./lib/command')const Completion = require('./lib/completion')const Parser = require('yargs-parser')const path = require('path')const Usage = require('./lib/usage')const Validation = require('./lib/validation')const Y18n = require('y18n')const objFilter = require('./lib/obj-filter')const setBlocking = require('set-blocking')const applyExtends = require('./lib/apply-extends')const { globalMiddlewareFactory } = require('./lib/middleware')const YError = require('./lib/yerror')exports = module.exports = Yargsfunction Yargs (processArgs, cwd, parentRequire) {  processArgs = processArgs || [] // handle calling yargs().  const self = {}  let command = null  let completion = null  let groups = {}  let globalMiddleware = []  let output = ''  let preservedGroups = {}  let usage = null  let validation = null  const y18n = Y18n({    directory: path.resolve(__dirname, './locales'),    updateFiles: false  })  self.middleware = globalMiddlewareFactory(globalMiddleware, self)  if (!cwd) cwd = process.cwd()  self.scriptName = function scriptName (scriptName) {    self.$0 = scriptName    return self  }  // ignore the node bin, specify this in your  // bin file with #!/usr/bin/env node  if (/\b(node|iojs|electron)(\.exe)?$/.test(process.argv[0])) {    self.$0 = process.argv.slice(1, 2)  } else {    self.$0 = process.argv.slice(0, 1)  }  self.$0 = self.$0    .map((x, i) => {      const b = rebase(cwd, x)      return x.match(/^(\/|([a-zA-Z]:)?\\)/) && b.length < x.length ? b : x    })    .join(' ').trim()  if (process.env._ !== undefined && process.argv[1] === process.env._) {    self.$0 = process.env._.replace(      `${path.dirname(process.execPath)}/`, ''    )  }  // use context object to keep track of resets, subcommand execution, etc  // submodules should modify and check the state of context as necessary  const context = { resets: -1, commands: [], fullCommands: [], files: [] }  self.getContext = () => context  // puts yargs back into an initial state. any keys  // that have been set to "global" will not be reset  // by this action.  let options  self.resetOptions = self.reset = function resetOptions (aliases) {    context.resets++    aliases = aliases || {}    options = options || {}    // put yargs back into an initial state, this    // logic is used to build a nested command    // hierarchy.    const tmpOptions = {}    tmpOptions.local = options.local ? options.local : []    tmpOptions.configObjects = options.configObjects ? options.configObjects : []    // if a key has been explicitly set as local,    // we should reset it before passing options to command.    const localLookup = {}    tmpOptions.local.forEach((l) => {      localLookup[l] = true      ;(aliases[l] || []).forEach((a) => {        localLookup[a] = true      })    })    // preserve all groups not set to local.    preservedGroups = Object.keys(groups).reduce((acc, groupName) => {      const keys = groups[groupName].filter(key => !(key in localLookup))      if (keys.length > 0) {        acc[groupName] = keys      }      return acc    }, {})    // groups can now be reset    groups = {}    const arrayOptions = [      'array', 'boolean', 'string', 'skipValidation',      'count', 'normalize', 'number',      'hiddenOptions'    ]    const objectOptions = [      'narg', 'key', 'alias', 'default', 'defaultDescription',      'config', 'choices', 'demandedOptions', 'demandedCommands', 'coerce'    ]    arrayOptions.forEach((k) => {      tmpOptions[k] = (options[k] || []).filter(k => !localLookup[k])    })    objectOptions.forEach((k) => {      tmpOptions[k] = objFilter(options[k], (k, v) => !localLookup[k])    })    tmpOptions.envPrefix = options.envPrefix    options = tmpOptions    // if this is the first time being executed, create    // instances of all our helpers -- otherwise just reset.    usage = usage ? usage.reset(localLookup) : Usage(self, y18n)    validation = validation ? validation.reset(localLookup) : Validation(self, usage, y18n)    command = command ? command.reset() : Command(self, usage, validation, globalMiddleware)    if (!completion) completion = Completion(self, usage, command)    completionCommand = null    output = ''    exitError = null    hasOutput = false    self.parsed = false    return self  }  self.resetOptions()  // temporary hack: allow "freezing" of reset-able state for parse(msg, cb)  let frozen  function freeze () {    frozen = {}    frozen.options = options    frozen.configObjects = options.configObjects.slice(0)    frozen.exitProcess = exitProcess    frozen.groups = groups    usage.freeze()    validation.freeze()    command.freeze()    frozen.strict = strict    frozen.completionCommand = completionCommand    frozen.output = output    frozen.exitError = exitError    frozen.hasOutput = hasOutput    frozen.parsed = self.parsed  }  function unfreeze () {    options = frozen.options    options.configObjects = frozen.configObjects    exitProcess = frozen.exitProcess    groups = frozen.groups    output = frozen.output    exitError = frozen.exitError    hasOutput = frozen.hasOutput    self.parsed = frozen.parsed    usage.unfreeze()    validation.unfreeze()    command.unfreeze()    strict = frozen.strict    completionCommand = frozen.completionCommand    parseFn = null    parseContext = null    frozen = undefined  }  self.boolean = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('boolean', keys)    return self  }  self.array = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('array', keys)    return self  }  self.number = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('number', keys)    return self  }  self.normalize = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('normalize', keys)    return self  }  self.count = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('count', keys)    return self  }  self.string = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('string', keys)    return self  }  self.requiresArg = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintObject(self.nargs, false, 'narg', keys, 1)    return self  }  self.skipValidation = function (keys) {    argsert('<array|string>', [keys], arguments.length)    populateParserHintArray('skipValidation', keys)    return self  }  function populateParserHintArray (type, keys, value) {    keys = [].concat(keys)    keys.forEach((key) => {      key = sanitizeKey(key)      options[type].push(key)    })  }  self.nargs = function (key, value) {    argsert('<string|object|array> [number]', [key, value], arguments.length)    populateParserHintObject(self.nargs, false, 'narg', key, value)    return self  }  self.choices = function (key, value) {    argsert('<object|string|array> [string|array]', [key, value], arguments.length)    populateParserHintObject(self.choices, true, 'choices', key, value)    return self  }  self.alias = function (key, value) {    argsert('<object|string|array> [string|array]', [key, value], arguments.length)    populateParserHintObject(self.alias, true, 'alias', key, value)    return self  }  // TODO: actually deprecate self.defaults.  self.default = self.defaults = function (key, value, defaultDescription) {    argsert('<object|string|array> [*] [string]', [key, value, defaultDescription], arguments.length)    if (defaultDescription) options.defaultDescription[key] = defaultDescription    if (typeof value === 'function') {      if (!options.defaultDescription[key]) options.defaultDescription[key] = usage.functionDescription(value)      value = value.call()    }    populateParserHintObject(self.default, false, 'default', key, value)    return self  }  self.describe = function (key, desc) {    argsert('<object|string|array> [string]', [key, desc], arguments.length)    populateParserHintObject(self.describe, false, 'key', key, true)    usage.describe(key, desc)    return self  }  self.demandOption = function (keys, msg) {    argsert('<object|string|array> [string]', [keys, msg], arguments.length)    populateParserHintObject(self.demandOption, false, 'demandedOptions', keys, msg)    return self  }  self.coerce = function (keys, value) {    argsert('<object|string|array> [function]', [keys, value], arguments.length)    populateParserHintObject(self.coerce, false, 'coerce', keys, value)    return self  }  function populateParserHintObject (builder, isArray, type, key, value) {    if (Array.isArray(key)) {      const temp = Object.create(null)      // an array of keys with one value ['x', 'y', 'z'], function parse () {}      key.forEach((k) => {        temp[k] = value      })      builder(temp)    } else if (typeof key === 'object') {      // an object of key value pairs: {'x': parse () {}, 'y': parse() {}}      Object.keys(key).forEach((k) => {        builder(k, key[k])      })    } else {      key = sanitizeKey(key)      // a single key value pair 'x', parse() {}      if (isArray) {        options[type][key] = (options[type][key] || []).concat(value)      } else {        options[type][key] = value      }    }  }  // TODO(bcoe): in future major versions move more objects towards  // Object.create(null):  function sanitizeKey (key) {    if (key === '__proto__') return '___proto___'    return key  }  function deleteFromParserHintObject (optionKey) {    // delete from all parsing hints:    // boolean, array, key, alias, etc.    Object.keys(options).forEach((hintKey) => {      const hint = options[hintKey]      if (Array.isArray(hint)) {        if (~hint.indexOf(optionKey)) hint.splice(hint.indexOf(optionKey), 1)      } else if (typeof hint === 'object') {        delete hint[optionKey]      }    })    // now delete the description from usage.js.    delete usage.getDescriptions()[optionKey]  }  self.config = function config (key, msg, parseFn) {    argsert('[object|string] [string|function] [function]', [key, msg, parseFn], arguments.length)    // allow a config object to be provided directly.    if (typeof key === 'object') {      key = applyExtends(key, cwd)      options.configObjects = (options.configObjects || []).concat(key)      return self    }    // allow for a custom parsing function.    if (typeof msg === 'function') {      parseFn = msg      msg = null    }    key = key || 'config'    self.describe(key, msg || usage.deferY18nLookup('Path to JSON config file'))    ;(Array.isArray(key) ? key : [key]).forEach((k) => {      options.config[k] = parseFn || true    })    return self  }  self.example = function (cmd, description) {    argsert('<string> [string]', [cmd, description], arguments.length)    usage.example(cmd, description)    return self  }  self.command = function (cmd, description, builder, handler, middlewares) {    argsert('<string|array|object> [string|boolean] [function|object] [function] [array]', [cmd, description, builder, handler, middlewares], arguments.length)    command.addHandler(cmd, description, builder, handler, middlewares)    return self  }  self.commandDir = function (dir, opts) {    argsert('<string> [object]', [dir, opts], arguments.length)    const req = parentRequire || require    command.addDirectory(dir, self.getContext(), req, require('get-caller-file')(), opts)    return self  }  // TODO: deprecate self.demand in favor of  // .demandCommand() .demandOption().  self.demand = self.required = self.require = function demand (keys, max, msg) {    // you can optionally provide a 'max' key,    // which will raise an exception if too many '_'    // options are provided.    if (Array.isArray(max)) {      max.forEach((key) => {        self.demandOption(key, msg)      })      max = Infinity    } else if (typeof max !== 'number') {      msg = max      max = Infinity    }    if (typeof keys === 'number') {      self.demandCommand(keys, max, msg, msg)    } else if (Array.isArray(keys)) {      keys.forEach((key) => {        self.demandOption(key, msg)      })    } else {      if (typeof msg === 'string') {        self.demandOption(keys, msg)      } else if (msg === true || typeof msg === 'undefined') {        self.demandOption(keys)      }    }    return self  }  self.demandCommand = function demandCommand (min, max, minMsg, maxMsg) {    argsert('[number] [number|string] [string|null|undefined] [string|null|undefined]', [min, max, minMsg, maxMsg], arguments.length)    if (typeof min === 'undefined') min = 1    if (typeof max !== 'number') {      minMsg = max      max = Infinity    }    self.global('_', false)    options.demandedCommands._ = {      min,      max,      minMsg,      maxMsg    }    return self  }  self.getDemandedOptions = () => {    argsert([], 0)    return options.demandedOptions  }  self.getDemandedCommands = () => {    argsert([], 0)    return options.demandedCommands  }  self.implies = function (key, value) {    argsert('<string|object> [number|string|array]', [key, value], arguments.length)    validation.implies(key, value)    return self  }  self.conflicts = function (key1, key2) {    argsert('<string|object> [string|array]', [key1, key2], arguments.length)    validation.conflicts(key1, key2)    return self  }  self.usage = function (msg, description, builder, handler) {    argsert('<string|null|undefined> [string|boolean] [function|object] [function]', [msg, description, builder, handler], arguments.length)    if (description !== undefined) {      // .usage() can be used as an alias for defining      // a default command.      if ((msg || '').match(/^\$0( |$)/)) {        return self.command(msg, description, builder, handler)      } else {        throw new YError('.usage() description must start with $0 if being used as alias for .command()')      }    } else {      usage.usage(msg)      return self    }  }  self.epilogue = self.epilog = function (msg) {    argsert('<string>', [msg], arguments.length)    usage.epilog(msg)    return self  }  self.fail = function (f) {    argsert('<function>', [f], arguments.length)    usage.failFn(f)    return self  }  self.check = function (f, _global) {    argsert('<function> [boolean]', [f, _global], arguments.length)    validation.check(f, _global !== false)    return self  }  self.global = function global (globals, global) {    argsert('<string|array> [boolean]', [globals, global], arguments.length)    globals = [].concat(globals)    if (global !== false) {      options.local = options.local.filter(l => globals.indexOf(l) === -1)    } else {      globals.forEach((g) => {        if (options.local.indexOf(g) === -1) options.local.push(g)      })    }    return self  }  self.pkgConf = function pkgConf (key, rootPath) {    argsert('<string> [string]', [key, rootPath], arguments.length)    let conf = null    // prefer cwd to require-main-filename in this method    // since we're looking for e.g. "nyc" config in nyc consumer    // rather than "yargs" config in nyc (where nyc is the main filename)    const obj = pkgUp(rootPath || cwd)    // If an object exists in the key, add it to options.configObjects    if (obj[key] && typeof obj[key] === 'object') {      conf = applyExtends(obj[key], rootPath || cwd)      options.configObjects = (options.configObjects || []).concat(conf)    }    return self  }  const pkgs = {}  function pkgUp (rootPath) {    const npath = rootPath || '*'    if (pkgs[npath]) return pkgs[npath]    const findUp = require('find-up')    let obj = {}    try {      let startDir = rootPath || require('require-main-filename')(parentRequire || require)      // When called in an environment that lacks require.main.filename, such as a jest test runner,      // startDir is already process.cwd(), and should not be shortened.      // Whether or not it is _actually_ a directory (e.g., extensionless bin) is irrelevant, find-up handles it.      if (!rootPath && path.extname(startDir)) {        startDir = path.dirname(startDir)      }      const pkgJsonPath = findUp.sync('package.json', {        cwd: startDir      })      obj = JSON.parse(fs.readFileSync(pkgJsonPath))    } catch (noop) {}    pkgs[npath] = obj || {}    return pkgs[npath]  }  let parseFn = null  let parseContext = null  self.parse = function parse (args, shortCircuit, _parseFn) {    argsert('[string|array] [function|boolean|object] [function]', [args, shortCircuit, _parseFn], arguments.length)    if (typeof args === 'undefined') {      return self._parseArgs(processArgs)    }    // a context object can optionally be provided, this allows    // additional information to be passed to a command handler.    if (typeof shortCircuit === 'object') {      parseContext = shortCircuit      shortCircuit = _parseFn    }    // by providing a function as a second argument to    // parse you can capture output that would otherwise    // default to printing to stdout/stderr.    if (typeof shortCircuit === 'function') {      parseFn = shortCircuit      shortCircuit = null    }    // completion short-circuits the parsing process,    // skipping validation, etc.    if (!shortCircuit) processArgs = args    freeze()    if (parseFn) exitProcess = false    const parsed = self._parseArgs(args, shortCircuit)    if (parseFn) parseFn(exitError, parsed, output)    unfreeze()    return parsed  }  self._getParseContext = () => parseContext || {}  self._hasParseCallback = () => !!parseFn  self.option = self.options = function option (key, opt) {    argsert('<string|object> [object]', [key, opt], arguments.length)    if (typeof key === 'object') {      Object.keys(key).forEach((k) => {        self.options(k, key[k])      })    } else {      if (typeof opt !== 'object') {        opt = {}      }      options.key[key] = true // track manually set keys.      if (opt.alias) self.alias(key, opt.alias)      const demand = opt.demand || opt.required || opt.require      // deprecated, use 'demandOption' instead      if (demand) {        self.demand(key, demand)      }      if (opt.demandOption) {        self.demandOption(key, typeof opt.demandOption === 'string' ? opt.demandOption : undefined)      }      if ('conflicts' in opt) {        self.conflicts(key, opt.conflicts)      }      if ('default' in opt) {        self.default(key, opt.default)      }      if ('implies' in opt) {        self.implies(key, opt.implies)      }      if ('nargs' in opt) {        self.nargs(key, opt.nargs)      }      if (opt.config) {        self.config(key, opt.configParser)      }      if (opt.normalize) {        self.normalize(key)      }      if ('choices' in opt) {        self.choices(key, opt.choices)      }      if ('coerce' in opt) {        self.coerce(key, opt.coerce)      }      if ('group' in opt) {        self.group(key, opt.group)      }      if (opt.boolean || opt.type === 'boolean') {        self.boolean(key)        if (opt.alias) self.boolean(opt.alias)      }      if (opt.array || opt.type === 'array') {        self.array(key)        if (opt.alias) self.array(opt.alias)      }      if (opt.number || opt.type === 'number') {        self.number(key)        if (opt.alias) self.number(opt.alias)      }      if (opt.string || opt.type === 'string') {        self.string(key)        if (opt.alias) self.string(opt.alias)      }      if (opt.count || opt.type === 'count') {        self.count(key)      }      if (typeof opt.global === 'boolean') {        self.global(key, opt.global)      }      if (opt.defaultDescription) {        options.defaultDescription[key] = opt.defaultDescription      }      if (opt.skipValidation) {        self.skipValidation(key)      }      const desc = opt.describe || opt.description || opt.desc      self.describe(key, desc)      if (opt.hidden) {        self.hide(key)      }      if (opt.requiresArg) {        self.requiresArg(key)      }    }    return self  }  self.getOptions = () => options  self.positional = function (key, opts) {    argsert('<string> <object>', [key, opts], arguments.length)    if (context.resets === 0) {      throw new YError(".positional() can only be called in a command's builder function")    }    // .positional() only supports a subset of the configuration    // options available to .option().    const supportedOpts = ['default', 'defaultDescription', 'implies', 'normalize',      'choices', 'conflicts', 'coerce', 'type', 'describe',      'desc', 'description', 'alias']    opts = objFilter(opts, (k, v) => {      let accept = supportedOpts.indexOf(k) !== -1      // type can be one of string|number|boolean.      if (k === 'type' && ['string', 'number', 'boolean'].indexOf(v) === -1) accept = false      return accept    })    // copy over any settings that can be inferred from the command string.    const fullCommand = context.fullCommands[context.fullCommands.length - 1]    const parseOptions = fullCommand ? command.cmdToParseOptions(fullCommand) : {      array: [],      alias: {},      default: {},      demand: {}    }    Object.keys(parseOptions).forEach((pk) => {      if (Array.isArray(parseOptions[pk])) {        if (parseOptions[pk].indexOf(key) !== -1) opts[pk] = true      } else {        if (parseOptions[pk][key] && !(pk in opts)) opts[pk] = parseOptions[pk][key]      }    })    self.group(key, usage.getPositionalGroupName())    return self.option(key, opts)  }  self.group = function group (opts, groupName) {    argsert('<string|array> <string>', [opts, groupName], arguments.length)    const existing = preservedGroups[groupName] || groups[groupName]    if (preservedGroups[groupName]) {      // we now only need to track this group name in groups.      delete preservedGroups[groupName]    }    const seen = {}    groups[groupName] = (existing || []).concat(opts).filter((key) => {      if (seen[key]) return false      return (seen[key] = true)    })    return self  }  // combine explicit and preserved groups. explicit groups should be first  self.getGroups = () => Object.assign({}, groups, preservedGroups)  // as long as options.envPrefix is not undefined,  // parser will apply env vars matching prefix to argv  self.env = function (prefix) {    argsert('[string|boolean]', [prefix], arguments.length)    if (prefix === false) options.envPrefix = undefined    else options.envPrefix = prefix || ''    return self  }  self.wrap = function (cols) {    argsert('<number|null|undefined>', [cols], arguments.length)    usage.wrap(cols)    return self  }  let strict = false  self.strict = function (enabled) {    argsert('[boolean]', [enabled], arguments.length)    strict = enabled !== false    return self  }  self.getStrict = () => strict  let parserConfig = {}  self.parserConfiguration = function parserConfiguration (config) {    argsert('<object>', [config], arguments.length)    parserConfig = config    return self  }  self.getParserConfiguration = () => parserConfig  self.showHelp = function (level) {    argsert('[string|function]', [level], arguments.length)    if (!self.parsed) self._parseArgs(processArgs) // run parser, if it has not already been executed.    if (command.hasDefaultCommand()) {      context.resets++ // override the restriction on top-level positoinals.      command.runDefaultBuilderOn(self, true)    }    usage.showHelp(level)    return self  }  let versionOpt = null  self.version = function version (opt, msg, ver) {    const defaultVersionOpt = 'version'    argsert('[boolean|string] [string] [string]', [opt, msg, ver], arguments.length)    // nuke the key previously configured    // to return version #.    if (versionOpt) {      deleteFromParserHintObject(versionOpt)      usage.version(undefined)      versionOpt = null    }    if (arguments.length === 0) {      ver = guessVersion()      opt = defaultVersionOpt    } else if (arguments.length === 1) {      if (opt === false) { // disable default 'version' key.        return self      }      ver = opt      opt = defaultVersionOpt    } else if (arguments.length === 2) {      ver = msg      msg = null    }    versionOpt = typeof opt === 'string' ? opt : defaultVersionOpt    msg = msg || usage.deferY18nLookup('Show version number')    usage.version(ver || undefined)    self.boolean(versionOpt)    self.describe(versionOpt, msg)    return self  }  function guessVersion () {    const obj = pkgUp()    return obj.version || 'unknown'  }  let helpOpt = null  self.addHelpOpt = self.help = function addHelpOpt (opt, msg) {    const defaultHelpOpt = 'help'    argsert('[string|boolean] [string]', [opt, msg], arguments.length)    // nuke the key previously configured    // to return help.    if (helpOpt) {      deleteFromParserHintObject(helpOpt)      helpOpt = null    }    if (arguments.length === 1) {      if (opt === false) return self    }    // use arguments, fallback to defaults for opt and msg    helpOpt = typeof opt === 'string' ? opt : defaultHelpOpt    self.boolean(helpOpt)    self.describe(helpOpt, msg || usage.deferY18nLookup('Show help'))    return self  }  const defaultShowHiddenOpt = 'show-hidden'  options.showHiddenOpt = defaultShowHiddenOpt  self.addShowHiddenOpt = self.showHidden = function addShowHiddenOpt (opt, msg) {    argsert('[string|boolean] [string]', [opt, msg], arguments.length)    if (arguments.length === 1) {      if (opt === false) return self    }    const showHiddenOpt = typeof opt === 'string' ? opt : defaultShowHiddenOpt    self.boolean(showHiddenOpt)    self.describe(showHiddenOpt, msg || usage.deferY18nLookup('Show hidden options'))    options.showHiddenOpt = showHiddenOpt    return self  }  self.hide = function hide (key) {    argsert('<string|object>', [key], arguments.length)    options.hiddenOptions.push(key)    return self  }  self.showHelpOnFail = function showHelpOnFail (enabled, message) {    argsert('[boolean|string] [string]', [enabled, message], arguments.length)    usage.showHelpOnFail(enabled, message)    return self  }  var exitProcess = true  self.exitProcess = function (enabled) {    argsert('[boolean]', [enabled], arguments.length)    if (typeof enabled !== 'boolean') {      enabled = true    }    exitProcess = enabled    return self  }  self.getExitProcess = () => exitProcess  var completionCommand = null  self.completion = function (cmd, desc, fn) {    argsert('[string] [string|boolean|function] [function]', [cmd, desc, fn], arguments.length)    // a function to execute when generating    // completions can be provided as the second    // or third argument to completion.    if (typeof desc === 'function') {      fn = desc      desc = null    }    // register the completion command.    completionCommand = cmd || 'completion'    if (!desc && desc !== false) {      desc = 'generate completion script'    }    self.command(completionCommand, desc)    // a function can be provided    if (fn) completion.registerFunction(fn)    return self  }  self.showCompletionScript = function ($0) {    argsert('[string]', [$0], arguments.length)    $0 = $0 || self.$0    _logger.log(completion.generateCompletionScript($0, completionCommand))    return self  }  self.getCompletion = function (args, done) {    argsert('<array> <function>', [args, done], arguments.length)    completion.getCompletion(args, done)  }  self.locale = function (locale) {    argsert('[string]', [locale], arguments.length)    if (arguments.length === 0) {      guessLocale()      return y18n.getLocale()    }    detectLocale = false    y18n.setLocale(locale)    return self  }  self.updateStrings = self.updateLocale = function (obj) {    argsert('<object>', [obj], arguments.length)    detectLocale = false    y18n.updateLocale(obj)    return self  }  let detectLocale = true  self.detectLocale = function (detect) {    argsert('<boolean>', [detect], arguments.length)    detectLocale = detect    return self  }  self.getDetectLocale = () => detectLocale  var hasOutput = false  var exitError = null  // maybe exit, always capture  // context about why we wanted to exit.  self.exit = (code, err) => {    hasOutput = true    exitError = err    if (exitProcess) process.exit(code)  }  // we use a custom logger that buffers output,  // so that we can print to non-CLIs, e.g., chat-bots.  const _logger = {    log () {      const args = []      for (let i = 0; i < arguments.length; i++) args.push(arguments[i])      if (!self._hasParseCallback()) console.log.apply(console, args)      hasOutput = true      if (output.length) output += '\n'      output += args.join(' ')    },    error () {      const args = []      for (let i = 0; i < arguments.length; i++) args.push(arguments[i])      if (!self._hasParseCallback()) console.error.apply(console, args)      hasOutput = true      if (output.length) output += '\n'      output += args.join(' ')    }  }  self._getLoggerInstance = () => _logger  // has yargs output an error our help  // message in the current execution context.  self._hasOutput = () => hasOutput  self._setHasOutput = () => {    hasOutput = true  }  let recommendCommands  self.recommendCommands = function (recommend) {    argsert('[boolean]', [recommend], arguments.length)    recommendCommands = typeof recommend === 'boolean' ? recommend : true    return self  }  self.getUsageInstance = () => usage  self.getValidationInstance = () => validation  self.getCommandInstance = () => command  self.terminalWidth = () => {    argsert([], 0)    return typeof process.stdout.columns !== 'undefined' ? process.stdout.columns : null  }  Object.defineProperty(self, 'argv', {    get: () => self._parseArgs(processArgs),    enumerable: true  })  self._parseArgs = function parseArgs (args, shortCircuit, _skipValidation, commandIndex) {    let skipValidation = !!_skipValidation    args = args || processArgs    options.__ = y18n.__    options.configuration = self.getParserConfiguration()    // Deprecated    let pkgConfig = pkgUp()['yargs']    if (pkgConfig) {      console.warn('Configuring yargs through package.json is deprecated and will be removed in the next major release, please use the JS API instead.')      options.configuration = Object.assign({}, pkgConfig, options.configuration)    }    const parsed = Parser.detailed(args, options)    let argv = parsed.argv    if (parseContext) argv = Object.assign({}, argv, parseContext)    const aliases = parsed.aliases    argv.$0 = self.$0    self.parsed = parsed    try {      guessLocale() // guess locale lazily, so that it can be turned off in chain.      // while building up the argv object, there      // are two passes through the parser. If completion      // is being performed short-circuit on the first pass.      if (shortCircuit) {        return argv      }      // if there's a handler associated with a      // command defer processing to it.      if (helpOpt) {        // consider any multi-char helpOpt alias as a valid help command        // unless all helpOpt aliases are single-char        // note that parsed.aliases is a normalized bidirectional map :)        const helpCmds = [helpOpt]          .concat(aliases[helpOpt] || [])          .filter(k => k.length > 1)        // check if help should trigger and strip it from _.        if (~helpCmds.indexOf(argv._[argv._.length - 1])) {          argv._.pop()          argv[helpOpt] = true        }      }      const handlerKeys = command.getCommands()      const requestCompletions = completion.completionKey in argv      const skipRecommendation = argv[helpOpt] || requestCompletions      const skipDefaultCommand = skipRecommendation && (handlerKeys.length > 1 || handlerKeys[0] !== '$0')      if (argv._.length) {        if (handlerKeys.length) {          let firstUnknownCommand          for (let i = (commandIndex || 0), cmd; argv._[i] !== undefined; i++) {            cmd = String(argv._[i])            if (~handlerKeys.indexOf(cmd) && cmd !== completionCommand) {              // commands are executed using a recursive algorithm that executes              // the deepest command first; we keep track of the position in the              // argv._ array that is currently being executed.              return command.runCommand(cmd, self, parsed, i + 1)            } else if (!firstUnknownCommand && cmd !== completionCommand) {              firstUnknownCommand = cmd              break            }          }          // run the default command, if defined          if (command.hasDefaultCommand() && !skipDefaultCommand) {            return command.runCommand(null, self, parsed)          }          // recommend a command if recommendCommands() has          // been enabled, and no commands were found to execute          if (recommendCommands && firstUnknownCommand && !skipRecommendation) {            validation.recommendCommands(firstUnknownCommand, handlerKeys)          }        }        // generate a completion script for adding to ~/.bashrc.        if (completionCommand && ~argv._.indexOf(completionCommand) && !requestCompletions) {          if (exitProcess) setBlocking(true)          self.showCompletionScript()          self.exit(0)        }      } else if (command.hasDefaultCommand() && !skipDefaultCommand) {        return command.runCommand(null, self, parsed)      }      // we must run completions first, a user might      // want to complete the --help or --version option.      if (requestCompletions) {        if (exitProcess) setBlocking(true)        // we allow for asynchronous completions,        // e.g., loading in a list of commands from an API.        const completionArgs = args.slice(args.indexOf(`--${completion.completionKey}`) + 1)        completion.getCompletion(completionArgs, (completions) => {          ;(completions || []).forEach((completion) => {            _logger.log(completion)          })          self.exit(0)        })        return argv      }      // Handle 'help' and 'version' options      // if we haven't already output help!      if (!hasOutput) {        Object.keys(argv).forEach((key) => {          if (key === helpOpt && argv[key]) {            if (exitProcess) setBlocking(true)            skipValidation = true            self.showHelp('log')            self.exit(0)          } else if (key === versionOpt && argv[key]) {            if (exitProcess) setBlocking(true)            skipValidation = true            usage.showVersion()            self.exit(0)          }        })      }      // Check if any of the options to skip validation were provided      if (!skipValidation && options.skipValidation.length > 0) {        skipValidation = Object.keys(argv).some(key => options.skipValidation.indexOf(key) >= 0 && argv[key] === true)      }      // If the help or version options where used and exitProcess is false,      // or if explicitly skipped, we won't run validations.      if (!skipValidation) {        if (parsed.error) throw new YError(parsed.error.message)        // if we're executed via bash completion, don't        // bother with validation.        if (!requestCompletions) {          self._runValidation(argv, aliases, {}, parsed.error)        }      }    } catch (err) {      if (err instanceof YError) usage.fail(err.message, err)      else throw err    }    return argv  }  self._runValidation = function runValidation (argv, aliases, positionalMap, parseErrors) {    if (parseErrors) throw new YError(parseErrors.message || parseErrors)    validation.nonOptionCount(argv)    validation.requiredArguments(argv)    if (strict) validation.unknownArguments(argv, aliases, positionalMap)    validation.customChecks(argv, aliases)    validation.limitedChoices(argv)    validation.implications(argv)    validation.conflicting(argv)  }  function guessLocale () {    if (!detectLocale) return    try {      const { env } = process      const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG || env.LANGUAGE || 'en_US'      self.locale(locale.replace(/[.:].*/, ''))    } catch (err) {      // if we explode looking up locale just noop      // we'll keep using the default language 'en'.    }  }  // an app should almost always have --version and --help,  // if you *really* want to disable this use .help(false)/.version(false).  self.help()  self.version()  return self}// rebase an absolute path to a relative one with respect to a base directory// exported for testsexports.rebase = rebasefunction rebase (base, dir) {  return path.relative(base, dir)}
 |