'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 = Yargs
function 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) {
self.customScriptName = true
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
})
})
// add all groups not set to local to preserved groups
Object.assign(
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 frozens = []
function freeze () {
let frozen = {}
frozens.push(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
frozen.parseFn = parseFn
frozen.parseContext = parseContext
}
function unfreeze () {
let frozen = frozens.pop()
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 = frozen.parseFn
parseContext = frozen.parseContext
}
self.boolean = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('boolean', keys)
return self
}
self.array = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('array', keys)
return self
}
self.number = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('number', keys)
return self
}
self.normalize = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('normalize', keys)
return self
}
self.count = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('count', keys)
return self
}
self.string = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintArray('string', keys)
return self
}
self.requiresArg = function (keys) {
argsert('', [keys], arguments.length)
populateParserHintObject(self.nargs, false, 'narg', keys, 1)
return self
}
self.skipValidation = function (keys) {
argsert('', [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(' [number]', [key, value], arguments.length)
populateParserHintObject(self.nargs, false, 'narg', key, value)
return self
}
self.choices = function (key, value) {
argsert('