Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50663 views
1
var path = require('path');
2
var wordwrap = require('wordwrap');
3
4
/* Hack an instance of Argv with process.argv into Argv
5
so people can do
6
require('optimist')(['--beeble=1','-z','zizzle']).argv
7
to parse a list of args and
8
require('optimist').argv
9
to get a parsed version of process.argv.
10
*/
11
12
var inst = Argv(process.argv.slice(2));
13
Object.keys(inst).forEach(function (key) {
14
Argv[key] = typeof inst[key] == 'function'
15
? inst[key].bind(inst)
16
: inst[key];
17
});
18
19
var exports = module.exports = Argv;
20
function Argv (args, cwd) {
21
var self = {};
22
if (!cwd) cwd = process.cwd();
23
24
self.$0 = process.argv
25
.slice(0,2)
26
.map(function (x) {
27
var b = rebase(cwd, x);
28
return x.match(/^\//) && b.length < x.length
29
? b : x
30
})
31
.join(' ')
32
;
33
34
if (process.argv[1] == process.env._) {
35
self.$0 = process.env._.replace(
36
path.dirname(process.execPath) + '/', ''
37
);
38
}
39
40
var flags = { bools : {}, strings : {} };
41
42
self.boolean = function (bools) {
43
if (!Array.isArray(bools)) {
44
bools = [].slice.call(arguments);
45
}
46
47
bools.forEach(function (name) {
48
flags.bools[name] = true;
49
});
50
51
return self;
52
};
53
54
self.string = function (strings) {
55
if (!Array.isArray(strings)) {
56
strings = [].slice.call(arguments);
57
}
58
59
strings.forEach(function (name) {
60
flags.strings[name] = true;
61
});
62
63
return self;
64
};
65
66
var aliases = {};
67
self.alias = function (x, y) {
68
if (typeof x === 'object') {
69
Object.keys(x).forEach(function (key) {
70
self.alias(key, x[key]);
71
});
72
}
73
else if (Array.isArray(y)) {
74
y.forEach(function (yy) {
75
self.alias(x, yy);
76
});
77
}
78
else {
79
var zs = (aliases[x] || []).concat(aliases[y] || []).concat(x, y);
80
aliases[x] = zs.filter(function (z) { return z != x });
81
aliases[y] = zs.filter(function (z) { return z != y });
82
}
83
84
return self;
85
};
86
87
var demanded = {};
88
self.demand = function (keys) {
89
if (typeof keys == 'number') {
90
if (!demanded._) demanded._ = 0;
91
demanded._ += keys;
92
}
93
else if (Array.isArray(keys)) {
94
keys.forEach(function (key) {
95
self.demand(key);
96
});
97
}
98
else {
99
demanded[keys] = true;
100
}
101
102
return self;
103
};
104
105
var usage;
106
self.usage = function (msg, opts) {
107
if (!opts && typeof msg === 'object') {
108
opts = msg;
109
msg = null;
110
}
111
112
usage = msg;
113
114
if (opts) self.options(opts);
115
116
return self;
117
};
118
119
function fail (msg) {
120
self.showHelp();
121
if (msg) console.error(msg);
122
process.exit(1);
123
}
124
125
var checks = [];
126
self.check = function (f) {
127
checks.push(f);
128
return self;
129
};
130
131
var defaults = {};
132
self.default = function (key, value) {
133
if (typeof key === 'object') {
134
Object.keys(key).forEach(function (k) {
135
self.default(k, key[k]);
136
});
137
}
138
else {
139
defaults[key] = value;
140
}
141
142
return self;
143
};
144
145
var descriptions = {};
146
self.describe = function (key, desc) {
147
if (typeof key === 'object') {
148
Object.keys(key).forEach(function (k) {
149
self.describe(k, key[k]);
150
});
151
}
152
else {
153
descriptions[key] = desc;
154
}
155
return self;
156
};
157
158
self.parse = function (args) {
159
return Argv(args).argv;
160
};
161
162
self.option = self.options = function (key, opt) {
163
if (typeof key === 'object') {
164
Object.keys(key).forEach(function (k) {
165
self.options(k, key[k]);
166
});
167
}
168
else {
169
if (opt.alias) self.alias(key, opt.alias);
170
if (opt.demand) self.demand(key);
171
if (opt.default) self.default(key, opt.default);
172
173
if (opt.boolean || opt.type === 'boolean') {
174
self.boolean(key);
175
}
176
if (opt.string || opt.type === 'string') {
177
self.string(key);
178
}
179
180
var desc = opt.describe || opt.description || opt.desc;
181
if (desc) {
182
self.describe(key, desc);
183
}
184
}
185
186
return self;
187
};
188
189
var wrap = null;
190
self.wrap = function (cols) {
191
wrap = cols;
192
return self;
193
};
194
195
self.showHelp = function (fn) {
196
if (!fn) fn = console.error;
197
fn(self.help());
198
};
199
200
self.help = function () {
201
var keys = Object.keys(
202
Object.keys(descriptions)
203
.concat(Object.keys(demanded))
204
.concat(Object.keys(defaults))
205
.reduce(function (acc, key) {
206
if (key !== '_') acc[key] = true;
207
return acc;
208
}, {})
209
);
210
211
var help = keys.length ? [ 'Options:' ] : [];
212
213
if (usage) {
214
help.unshift(usage.replace(/\$0/g, self.$0), '');
215
}
216
217
var switches = keys.reduce(function (acc, key) {
218
acc[key] = [ key ].concat(aliases[key] || [])
219
.map(function (sw) {
220
return (sw.length > 1 ? '--' : '-') + sw
221
})
222
.join(', ')
223
;
224
return acc;
225
}, {});
226
227
var switchlen = longest(Object.keys(switches).map(function (s) {
228
return switches[s] || '';
229
}));
230
231
var desclen = longest(Object.keys(descriptions).map(function (d) {
232
return descriptions[d] || '';
233
}));
234
235
keys.forEach(function (key) {
236
var kswitch = switches[key];
237
var desc = descriptions[key] || '';
238
239
if (wrap) {
240
desc = wordwrap(switchlen + 4, wrap)(desc)
241
.slice(switchlen + 4)
242
;
243
}
244
245
var spadding = new Array(
246
Math.max(switchlen - kswitch.length + 3, 0)
247
).join(' ');
248
249
var dpadding = new Array(
250
Math.max(desclen - desc.length + 1, 0)
251
).join(' ');
252
253
var type = null;
254
255
if (flags.bools[key]) type = '[boolean]';
256
if (flags.strings[key]) type = '[string]';
257
258
if (!wrap && dpadding.length > 0) {
259
desc += dpadding;
260
}
261
262
var prelude = ' ' + kswitch + spadding;
263
var extra = [
264
type,
265
demanded[key]
266
? '[required]'
267
: null
268
,
269
defaults[key] !== undefined
270
? '[default: ' + JSON.stringify(defaults[key]) + ']'
271
: null
272
,
273
].filter(Boolean).join(' ');
274
275
var body = [ desc, extra ].filter(Boolean).join(' ');
276
277
if (wrap) {
278
var dlines = desc.split('\n');
279
var dlen = dlines.slice(-1)[0].length
280
+ (dlines.length === 1 ? prelude.length : 0)
281
282
body = desc + (dlen + extra.length > wrap - 2
283
? '\n'
284
+ new Array(wrap - extra.length + 1).join(' ')
285
+ extra
286
: new Array(wrap - extra.length - dlen + 1).join(' ')
287
+ extra
288
);
289
}
290
291
help.push(prelude + body);
292
});
293
294
help.push('');
295
return help.join('\n');
296
};
297
298
Object.defineProperty(self, 'argv', {
299
get : parseArgs,
300
enumerable : true,
301
});
302
303
function parseArgs () {
304
var argv = { _ : [], $0 : self.$0 };
305
Object.keys(flags.bools).forEach(function (key) {
306
setArg(key, defaults[key] || false);
307
});
308
309
function setArg (key, val) {
310
var num = Number(val);
311
var value = typeof val !== 'string' || isNaN(num) ? val : num;
312
if (flags.strings[key]) value = val;
313
314
if (key in argv && !flags.bools[key]) {
315
if (!Array.isArray(argv[key])) {
316
argv[key] = [ argv[key] ];
317
}
318
argv[key].push(value);
319
}
320
else {
321
argv[key] = value;
322
}
323
324
(aliases[key] || []).forEach(function (x) {
325
argv[x] = argv[key];
326
});
327
}
328
329
for (var i = 0; i < args.length; i++) {
330
var arg = args[i];
331
332
if (arg === '--') {
333
argv._.push.apply(argv._, args.slice(i + 1));
334
break;
335
}
336
else if (arg.match(/^--.+=/)) {
337
var m = arg.match(/^--([^=]+)=(.*)/);
338
setArg(m[1], m[2]);
339
}
340
else if (arg.match(/^--no-.+/)) {
341
var key = arg.match(/^--no-(.+)/)[1];
342
setArg(key, false);
343
}
344
else if (arg.match(/^--.+/)) {
345
var key = arg.match(/^--(.+)/)[1];
346
var next = args[i + 1];
347
if (next !== undefined && !next.match(/^-/)
348
&& !flags.bools[key]) {
349
setArg(key, next);
350
i++;
351
}
352
else if (flags.bools[key] && /true|false/.test(next)) {
353
setArg(key, next === 'true');
354
i++;
355
}
356
else {
357
setArg(key, true);
358
}
359
}
360
else if (arg.match(/^-[^-]+/)) {
361
var letters = arg.slice(1,-1).split('');
362
363
var broken = false;
364
for (var j = 0; j < letters.length; j++) {
365
if (letters[j+1] && letters[j+1].match(/\W/)) {
366
setArg(letters[j], arg.slice(j+2));
367
broken = true;
368
break;
369
}
370
else {
371
setArg(letters[j], true);
372
}
373
}
374
375
if (!broken) {
376
var key = arg.slice(-1)[0];
377
378
if (args[i+1] && !args[i+1].match(/^-/)
379
&& !flags.bools[key]) {
380
setArg(key, args[i+1]);
381
i++;
382
}
383
else if (args[i+1] && flags.bools[key] && /true|false/.test(args[i+1])) {
384
setArg(key, args[i+1] === 'true');
385
i++;
386
}
387
else {
388
setArg(key, true);
389
}
390
}
391
}
392
else {
393
var n = Number(arg);
394
argv._.push(flags.strings['_'] || isNaN(n) ? arg : n);
395
}
396
}
397
398
Object.keys(defaults).forEach(function (key) {
399
if (!(key in argv)) {
400
argv[key] = defaults[key];
401
}
402
});
403
404
if (demanded._ && argv._.length < demanded._) {
405
fail('Not enough non-option arguments: got '
406
+ argv._.length + ', need at least ' + demanded._
407
);
408
}
409
410
var missing = [];
411
Object.keys(demanded).forEach(function (key) {
412
if (!argv[key]) missing.push(key);
413
});
414
415
if (missing.length) {
416
fail('Missing required arguments: ' + missing.join(', '));
417
}
418
419
checks.forEach(function (f) {
420
try {
421
if (f(argv) === false) {
422
fail('Argument check failed: ' + f.toString());
423
}
424
}
425
catch (err) {
426
fail(err)
427
}
428
});
429
430
return argv;
431
}
432
433
function longest (xs) {
434
return Math.max.apply(
435
null,
436
xs.map(function (x) { return x.length })
437
);
438
}
439
440
return self;
441
};
442
443
// rebase an absolute path to a relative one with respect to a base directory
444
// exported for tests
445
exports.rebase = rebase;
446
function rebase (base, dir) {
447
var ds = path.normalize(dir).split('/').slice(1);
448
var bs = path.normalize(base).split('/').slice(1);
449
450
for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
451
ds.splice(0, i); bs.splice(0, i);
452
453
var p = path.normalize(
454
bs.map(function () { return '..' }).concat(ds).join('/')
455
).replace(/\/$/,'').replace(/^$/, '.');
456
return p.match(/^[.\/]/) ? p : './' + p;
457
};
458
459