123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- var assert = require('assert'),
- path = require('path'),
- Completion = require('./lib/completion'),
- Parser = require('./lib/parser'),
- Usage = require('./lib/usage'),
- Validation = require('./lib/validation');
- Argv(process.argv.slice(2));
- var exports = module.exports = Argv;
- function Argv (processArgs, cwd) {
- processArgs = processArgs || []; // handle calling yargs().
- var self = {};
- var completion = null;
- var usage = null;
- var validation = null;
- if (!cwd) cwd = process.cwd();
- self.$0 = process.argv
- .slice(0,2)
- .map(function (x) {
- // ignore the node bin, specify this in your
- // bin file with #!/usr/bin/env node
- if (~x.indexOf('node')) return;
- var b = rebase(cwd, x);
- return x.match(/^\//) && 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) + '/', ''
- );
- }
- var options;
- self.resetOptions = self.reset = function () {
- // put yargs back into its initial
- // state, this is useful for creating a
- // nested CLI.
- options = {
- array: [],
- boolean: [],
- string: [],
- narg: {},
- key: {},
- alias: {},
- default: {},
- defaultDescription: {},
- requiresArg: [],
- count: [],
- normalize: [],
- config: []
- };
- usage = Usage(self); // handle usage output.
- validation = Validation(self, usage); // handle arg validation.
- completion = Completion(self, usage);
- demanded = {};
- exitProcess = true;
- strict = false;
- helpOpt = null;
- versionOpt = null;
- completionOpt = null;
- return self;
- };
- self.resetOptions();
- self.boolean = function (bools) {
- options.boolean.push.apply(options.boolean, [].concat(bools));
- return self;
- };
- self.array = function (arrays) {
- options.array.push.apply(options.array, [].concat(arrays));
- return self;
- }
- self.nargs = function (key, n) {
- if (typeof key === 'object') {
- Object.keys(key).forEach(function(k) {
- self.nargs(k, key[k]);
- });
- } else {
- options.narg[key] = n;
- }
- return self;
- }
- self.normalize = function (strings) {
- options.normalize.push.apply(options.normalize, [].concat(strings));
- return self;
- };
- self.config = function (configs) {
- options.config.push.apply(options.config, [].concat(configs));
- return self;
- };
- self.example = function (cmd, description) {
- usage.example(cmd, description);
- return self;
- };
- self.command = function (cmd, description) {
- usage.command(cmd, description);
- return self;
- };
- self.string = function (strings) {
- options.string.push.apply(options.string, [].concat(strings));
- return self;
- };
- self.default = function (key, value, defaultDescription) {
- if (typeof key === 'object') {
- Object.keys(key).forEach(function (k) {
- self.default(k, key[k]);
- });
- }
- else {
- options.defaultDescription[key] = defaultDescription;
- options.default[key] = value;
- }
- return self;
- };
- self.alias = function (x, y) {
- if (typeof x === 'object') {
- Object.keys(x).forEach(function (key) {
- self.alias(key, x[key]);
- });
- }
- else {
- options.alias[x] = (options.alias[x] || []).concat(y);
- }
- return self;
- };
- self.count = function(counts) {
- options.count.push.apply(options.count, [].concat(counts));
- return self;
- };
- var demanded = {};
- self.demand = self.required = self.require = function (keys, msg) {
- if (typeof keys == 'number') {
- if (!demanded._) demanded._ = { count: 0, msg: null };
- demanded._.count += keys;
- demanded._.msg = msg;
- }
- else if (Array.isArray(keys)) {
- keys.forEach(function (key) {
- self.demand(key, msg);
- });
- }
- else {
- if (typeof msg === 'string') {
- demanded[keys] = { msg: msg };
- }
- else if (msg === true || typeof msg === 'undefined') {
- demanded[keys] = { msg: null };
- }
- }
- return self;
- };
- self.getDemanded = function() {
- return demanded;
- };
- self.requiresArg = function (requiresArgs) {
- options.requiresArg.push.apply(options.requiresArg, [].concat(requiresArgs));
- return self;
- };
- self.implies = function (key, value) {
- validation.implies(key, value);
- return self;
- };
- self.usage = function (msg, opts) {
- if (!opts && typeof msg === 'object') {
- opts = msg;
- msg = null;
- }
- usage.usage(msg);
- if (opts) self.options(opts);
- return self;
- };
- self.epilogue = self.epilog = function (msg) {
- usage.epilog(msg);
- return self;
- };
- self.fail = function (f) {
- usage.failFn(f);
- return self;
- };
- self.check = function (f) {
- validation.check(f);
- return self;
- };
- self.defaults = self.default;
- self.describe = function (key, desc) {
- usage.describe(key, desc);
- return self;
- };
- self.parse = function (args) {
- return parseArgs(args);
- };
- self.option = self.options = function (key, opt) {
- if (typeof key === 'object') {
- Object.keys(key).forEach(function (k) {
- self.options(k, key[k]);
- });
- }
- else {
- assert(typeof opt === 'object', 'second argument to option must be an object');
- options.key[key] = true; // track manually set keys.
- if (opt.alias) self.alias(key, opt.alias);
- var demand = opt.demand || opt.required || opt.require;
- if (demand) {
- self.demand(key, demand);
- }
- if ('default' in opt) {
- self.default(key, opt.default);
- }
- if ('nargs' in opt) {
- self.nargs(key, opt.nargs);
- }
- 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.string || opt.type === 'string') {
- self.string(key);
- if (opt.alias) self.string(opt.alias);
- }
- if (opt.count || opt.type === 'count') {
- self.count(key);
- }
- var desc = opt.describe || opt.description || opt.desc;
- if (desc) {
- self.describe(key, desc);
- }
- if (opt.requiresArg) {
- self.requiresArg(key);
- }
- }
- return self;
- };
- self.getOptions = function() {
- return options;
- };
- self.wrap = function (cols) {
- usage.wrap(cols);
- return self;
- };
- var strict = false;
- self.strict = function () {
- strict = true;
- return self;
- };
- self.getStrict = function () {
- return strict;
- }
- self.showHelp = function (fn) {
- usage.showHelp(fn);
- return self;
- };
- var versionOpt = null;
- self.version = function (ver, opt, msg) {
- versionOpt = opt || 'version';
- usage.version(ver);
- self.describe(versionOpt, msg || 'Show version number');
- return self;
- };
- var helpOpt = null;
- self.addHelpOpt = function (opt, msg) {
- helpOpt = opt;
- self.describe(opt, msg || 'Show help');
- return self;
- };
- self.showHelpOnFail = function (enabled, message) {
- usage.showHelpOnFail(enabled, message);
- return self;
- };
- var exitProcess = true;
- self.exitProcess = function (enabled) {
- if (typeof enabled !== 'boolean') {
- enabled = true;
- }
- exitProcess = enabled;
- return self;
- };
- self.getExitProcess = function () {
- return exitProcess;
- }
- self.help = function () {
- if (arguments.length > 0) return self.addHelpOpt.apply(self, arguments);
- if (!self.parsed) parseArgs(processArgs); // run parser, if it has not already been executed.
- return usage.help();
- };
- var completionOpt = null,
- completionCommand = null;
- self.completion = function(cmd, desc, fn) {
- // 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;
- completionOpt = completion.completionKey;
- self.command(completionCommand, desc || 'generate bash completion script');
- // a function can be provided
- if (fn) completion.registerFunction(fn);
- return self;
- };
- self.showCompletionScript = function($0) {
- $0 = $0 || self.$0;
- console.log(completion.generateCompletionScript($0));
- return self;
- };
- self.getUsageInstance = function () {
- return usage;
- };
- self.getValidationInstance = function () {
- return validation;
- }
- Object.defineProperty(self, 'argv', {
- get : function () {
- var args = null;
- try {
- args = parseArgs(processArgs);
- } catch (err) {
- usage.fail(err.message);
- }
- return args;
- },
- enumerable : true
- });
- function parseArgs (args) {
- var parsed = Parser(args, options),
- argv = parsed.argv,
- aliases = parsed.aliases;
- argv.$0 = self.$0;
- self.parsed = parsed;
- // generate a completion script for adding to ~/.bashrc.
- if (completionCommand && ~argv._.indexOf(completionCommand)) {
- self.showCompletionScript();
- if (exitProcess){
- process.exit(0);
- }
- }
- Object.keys(argv).forEach(function(key) {
- if (key === helpOpt) {
- self.showHelp('log');
- if (exitProcess){
- process.exit(0);
- }
- }
- else if (key === versionOpt) {
- usage.showVersion();
- if (exitProcess){
- process.exit(0);
- }
- }
- else if (key === completionOpt) {
- // we allow for asynchronous completions,
- // e.g., loading in a list of commands from an API.
- completion.getCompletion(function(completions) {
- (completions || []).forEach(function(completion) {
- console.log(completion);
- });
- if (exitProcess){
- process.exit(0);
- }
- });
- return;
- }
- });
- validation.nonOptionCount(argv);
- validation.missingArgumentValue(argv);
- validation.requiredArguments(argv);
- if (strict) {
- validation.unknownArguments(argv, aliases);
- }
- validation.customChecks(argv, aliases);
- validation.implications(argv);
- setPlaceholderKeys(argv);
- return argv;
- }
- function setPlaceholderKeys (argv) {
- Object.keys(options.key).forEach(function(key) {
- if (typeof argv[key] === 'undefined') argv[key] = undefined;
- });
- }
- sigletonify(self);
- return self;
- };
- // rebase an absolute path to a relative one with respect to a base directory
- // exported for tests
- exports.rebase = rebase;
- function rebase (base, dir) {
- return path.relative(base, dir);
- };
- /* Hack an instance of Argv with process.argv into Argv
- so people can do
- require('yargs')(['--beeble=1','-z','zizzle']).argv
- to parse a list of args and
- require('yargs').argv
- to get a parsed version of process.argv.
- */
- function sigletonify(inst) {
- Object.keys(inst).forEach(function (key) {
- if (key === 'argv') {
- Argv.__defineGetter__(key, inst.__lookupGetter__(key));
- } else {
- Argv[key] = typeof inst[key] == 'function'
- ? inst[key].bind(inst)
- : inst[key];
- }
- });
- }
|