Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80756 views
1
var assert = require('assert'),
2
path = require('path'),
3
Completion = require('./lib/completion'),
4
Parser = require('./lib/parser'),
5
Usage = require('./lib/usage'),
6
Validation = require('./lib/validation');
7
8
Argv(process.argv.slice(2));
9
10
var exports = module.exports = Argv;
11
function Argv (processArgs, cwd) {
12
processArgs = processArgs || []; // handle calling yargs().
13
14
var self = {};
15
var completion = null;
16
var usage = null;
17
var validation = null;
18
19
if (!cwd) cwd = process.cwd();
20
21
self.$0 = process.argv
22
.slice(0,2)
23
.map(function (x) {
24
// ignore the node bin, specify this in your
25
// bin file with #!/usr/bin/env node
26
if (~x.indexOf('node')) return;
27
var b = rebase(cwd, x);
28
return x.match(/^\//) && b.length < x.length
29
? b : x
30
})
31
.join(' ').trim();
32
;
33
34
if (process.env._ != undefined && process.argv[1] == process.env._) {
35
self.$0 = process.env._.replace(
36
path.dirname(process.execPath) + '/', ''
37
);
38
}
39
40
var options;
41
self.resetOptions = self.reset = function () {
42
// put yargs back into its initial
43
// state, this is useful for creating a
44
// nested CLI.
45
options = {
46
array: [],
47
boolean: [],
48
string: [],
49
narg: {},
50
key: {},
51
alias: {},
52
default: {},
53
defaultDescription: {},
54
requiresArg: [],
55
count: [],
56
normalize: [],
57
config: []
58
};
59
60
usage = Usage(self); // handle usage output.
61
validation = Validation(self, usage); // handle arg validation.
62
completion = Completion(self, usage);
63
64
demanded = {};
65
66
exitProcess = true;
67
strict = false;
68
helpOpt = null;
69
versionOpt = null;
70
completionOpt = null;
71
72
return self;
73
};
74
self.resetOptions();
75
76
self.boolean = function (bools) {
77
options.boolean.push.apply(options.boolean, [].concat(bools));
78
return self;
79
};
80
81
self.array = function (arrays) {
82
options.array.push.apply(options.array, [].concat(arrays));
83
return self;
84
}
85
86
self.nargs = function (key, n) {
87
if (typeof key === 'object') {
88
Object.keys(key).forEach(function(k) {
89
self.nargs(k, key[k]);
90
});
91
} else {
92
options.narg[key] = n;
93
}
94
return self;
95
}
96
97
self.normalize = function (strings) {
98
options.normalize.push.apply(options.normalize, [].concat(strings));
99
return self;
100
};
101
102
self.config = function (configs) {
103
options.config.push.apply(options.config, [].concat(configs));
104
return self;
105
};
106
107
self.example = function (cmd, description) {
108
usage.example(cmd, description);
109
return self;
110
};
111
112
self.command = function (cmd, description) {
113
usage.command(cmd, description);
114
return self;
115
};
116
117
self.string = function (strings) {
118
options.string.push.apply(options.string, [].concat(strings));
119
return self;
120
};
121
122
self.default = function (key, value, defaultDescription) {
123
if (typeof key === 'object') {
124
Object.keys(key).forEach(function (k) {
125
self.default(k, key[k]);
126
});
127
}
128
else {
129
options.defaultDescription[key] = defaultDescription;
130
options.default[key] = value;
131
}
132
return self;
133
};
134
135
self.alias = function (x, y) {
136
if (typeof x === 'object') {
137
Object.keys(x).forEach(function (key) {
138
self.alias(key, x[key]);
139
});
140
}
141
else {
142
options.alias[x] = (options.alias[x] || []).concat(y);
143
}
144
return self;
145
};
146
147
self.count = function(counts) {
148
options.count.push.apply(options.count, [].concat(counts));
149
return self;
150
};
151
152
var demanded = {};
153
self.demand = self.required = self.require = function (keys, msg) {
154
if (typeof keys == 'number') {
155
if (!demanded._) demanded._ = { count: 0, msg: null };
156
demanded._.count += keys;
157
demanded._.msg = msg;
158
}
159
else if (Array.isArray(keys)) {
160
keys.forEach(function (key) {
161
self.demand(key, msg);
162
});
163
}
164
else {
165
if (typeof msg === 'string') {
166
demanded[keys] = { msg: msg };
167
}
168
else if (msg === true || typeof msg === 'undefined') {
169
demanded[keys] = { msg: null };
170
}
171
}
172
173
return self;
174
};
175
self.getDemanded = function() {
176
return demanded;
177
};
178
179
self.requiresArg = function (requiresArgs) {
180
options.requiresArg.push.apply(options.requiresArg, [].concat(requiresArgs));
181
return self;
182
};
183
184
self.implies = function (key, value) {
185
validation.implies(key, value);
186
return self;
187
};
188
189
self.usage = function (msg, opts) {
190
if (!opts && typeof msg === 'object') {
191
opts = msg;
192
msg = null;
193
}
194
195
usage.usage(msg);
196
197
if (opts) self.options(opts);
198
199
return self;
200
};
201
202
self.epilogue = self.epilog = function (msg) {
203
usage.epilog(msg);
204
return self;
205
};
206
207
self.fail = function (f) {
208
usage.failFn(f);
209
return self;
210
};
211
212
self.check = function (f) {
213
validation.check(f);
214
return self;
215
};
216
217
self.defaults = self.default;
218
219
self.describe = function (key, desc) {
220
usage.describe(key, desc);
221
return self;
222
};
223
224
self.parse = function (args) {
225
return parseArgs(args);
226
};
227
228
self.option = self.options = function (key, opt) {
229
if (typeof key === 'object') {
230
Object.keys(key).forEach(function (k) {
231
self.options(k, key[k]);
232
});
233
}
234
else {
235
assert(typeof opt === 'object', 'second argument to option must be an object');
236
237
options.key[key] = true; // track manually set keys.
238
239
if (opt.alias) self.alias(key, opt.alias);
240
241
var demand = opt.demand || opt.required || opt.require;
242
243
if (demand) {
244
self.demand(key, demand);
245
}
246
if ('default' in opt) {
247
self.default(key, opt.default);
248
}
249
if ('nargs' in opt) {
250
self.nargs(key, opt.nargs);
251
}
252
if (opt.boolean || opt.type === 'boolean') {
253
self.boolean(key);
254
if (opt.alias) self.boolean(opt.alias);
255
}
256
if (opt.array || opt.type === 'array') {
257
self.array(key);
258
if (opt.alias) self.array(opt.alias);
259
}
260
if (opt.string || opt.type === 'string') {
261
self.string(key);
262
if (opt.alias) self.string(opt.alias);
263
}
264
if (opt.count || opt.type === 'count') {
265
self.count(key);
266
}
267
268
var desc = opt.describe || opt.description || opt.desc;
269
if (desc) {
270
self.describe(key, desc);
271
}
272
273
if (opt.requiresArg) {
274
self.requiresArg(key);
275
}
276
}
277
278
return self;
279
};
280
self.getOptions = function() {
281
return options;
282
};
283
284
self.wrap = function (cols) {
285
usage.wrap(cols);
286
return self;
287
};
288
289
var strict = false;
290
self.strict = function () {
291
strict = true;
292
return self;
293
};
294
self.getStrict = function () {
295
return strict;
296
}
297
298
self.showHelp = function (fn) {
299
usage.showHelp(fn);
300
return self;
301
};
302
303
var versionOpt = null;
304
self.version = function (ver, opt, msg) {
305
versionOpt = opt || 'version';
306
usage.version(ver);
307
self.describe(versionOpt, msg || 'Show version number');
308
return self;
309
};
310
311
var helpOpt = null;
312
self.addHelpOpt = function (opt, msg) {
313
helpOpt = opt;
314
self.describe(opt, msg || 'Show help');
315
return self;
316
};
317
318
self.showHelpOnFail = function (enabled, message) {
319
usage.showHelpOnFail(enabled, message);
320
return self;
321
};
322
323
var exitProcess = true;
324
self.exitProcess = function (enabled) {
325
if (typeof enabled !== 'boolean') {
326
enabled = true;
327
}
328
exitProcess = enabled;
329
return self;
330
};
331
self.getExitProcess = function () {
332
return exitProcess;
333
}
334
335
self.help = function () {
336
if (arguments.length > 0) return self.addHelpOpt.apply(self, arguments);
337
338
if (!self.parsed) parseArgs(processArgs); // run parser, if it has not already been executed.
339
340
return usage.help();
341
};
342
343
var completionOpt = null,
344
completionCommand = null;
345
self.completion = function(cmd, desc, fn) {
346
// a function to execute when generating
347
// completions can be provided as the second
348
// or third argument to completion.
349
if (typeof desc === 'function') {
350
fn = desc;
351
desc = null;
352
}
353
354
// register the completion command.
355
completionCommand = cmd;
356
completionOpt = completion.completionKey;
357
self.command(completionCommand, desc || 'generate bash completion script');
358
359
// a function can be provided
360
if (fn) completion.registerFunction(fn);
361
362
return self;
363
};
364
365
self.showCompletionScript = function($0) {
366
$0 = $0 || self.$0;
367
console.log(completion.generateCompletionScript($0));
368
return self;
369
};
370
371
self.getUsageInstance = function () {
372
return usage;
373
};
374
375
self.getValidationInstance = function () {
376
return validation;
377
}
378
379
Object.defineProperty(self, 'argv', {
380
get : function () {
381
var args = null;
382
383
try {
384
args = parseArgs(processArgs);
385
} catch (err) {
386
usage.fail(err.message);
387
}
388
389
return args;
390
},
391
enumerable : true
392
});
393
394
function parseArgs (args) {
395
var parsed = Parser(args, options),
396
argv = parsed.argv,
397
aliases = parsed.aliases;
398
399
argv.$0 = self.$0;
400
401
self.parsed = parsed;
402
403
// generate a completion script for adding to ~/.bashrc.
404
if (completionCommand && ~argv._.indexOf(completionCommand)) {
405
self.showCompletionScript();
406
if (exitProcess){
407
process.exit(0);
408
}
409
}
410
411
Object.keys(argv).forEach(function(key) {
412
if (key === helpOpt) {
413
self.showHelp('log');
414
if (exitProcess){
415
process.exit(0);
416
}
417
}
418
else if (key === versionOpt) {
419
usage.showVersion();
420
if (exitProcess){
421
process.exit(0);
422
}
423
}
424
else if (key === completionOpt) {
425
// we allow for asynchronous completions,
426
// e.g., loading in a list of commands from an API.
427
completion.getCompletion(function(completions) {
428
(completions || []).forEach(function(completion) {
429
console.log(completion);
430
});
431
432
if (exitProcess){
433
process.exit(0);
434
}
435
});
436
return;
437
}
438
});
439
440
validation.nonOptionCount(argv);
441
validation.missingArgumentValue(argv);
442
validation.requiredArguments(argv);
443
444
if (strict) {
445
validation.unknownArguments(argv, aliases);
446
}
447
448
validation.customChecks(argv, aliases);
449
validation.implications(argv);
450
setPlaceholderKeys(argv);
451
452
return argv;
453
}
454
455
function setPlaceholderKeys (argv) {
456
Object.keys(options.key).forEach(function(key) {
457
if (typeof argv[key] === 'undefined') argv[key] = undefined;
458
});
459
}
460
461
sigletonify(self);
462
return self;
463
};
464
465
// rebase an absolute path to a relative one with respect to a base directory
466
// exported for tests
467
exports.rebase = rebase;
468
function rebase (base, dir) {
469
return path.relative(base, dir);
470
};
471
472
/* Hack an instance of Argv with process.argv into Argv
473
so people can do
474
require('yargs')(['--beeble=1','-z','zizzle']).argv
475
to parse a list of args and
476
require('yargs').argv
477
to get a parsed version of process.argv.
478
*/
479
function sigletonify(inst) {
480
Object.keys(inst).forEach(function (key) {
481
if (key === 'argv') {
482
Argv.__defineGetter__(key, inst.__lookupGetter__(key));
483
} else {
484
Argv[key] = typeof inst[key] == 'function'
485
? inst[key].bind(inst)
486
: inst[key];
487
}
488
});
489
}
490
491