1 | var path = require('path');
|
2 | var wordwrap = require('wordwrap');
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
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.env._ != undefined && 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 | args = args_;
|
160 | return self.argv;
|
161 | };
|
162 |
|
163 | self.option = self.options = function (key, opt) {
|
164 | if (typeof key === 'object') {
|
165 | Object.keys(key).forEach(function (k) {
|
166 | self.options(k, key[k]);
|
167 | });
|
168 | }
|
169 | else {
|
170 | if (opt.alias) self.alias(key, opt.alias);
|
171 | if (opt.demand) self.demand(key);
|
172 | if (typeof opt.default !== 'undefined') {
|
173 | self.default(key, opt.default);
|
174 | }
|
175 |
|
176 | if (opt.boolean || opt.type === 'boolean') {
|
177 | self.boolean(key);
|
178 | }
|
179 | if (opt.string || opt.type === 'string') {
|
180 | self.string(key);
|
181 | }
|
182 |
|
183 | var desc = opt.describe || opt.description || opt.desc;
|
184 | if (desc) {
|
185 | self.describe(key, desc);
|
186 | }
|
187 | }
|
188 |
|
189 | return self;
|
190 | };
|
191 |
|
192 | var wrap = null;
|
193 | self.wrap = function (cols) {
|
194 | wrap = cols;
|
195 | return self;
|
196 | };
|
197 |
|
198 | self.showHelp = function (fn) {
|
199 | if (!fn) fn = console.error;
|
200 | fn(self.help());
|
201 | };
|
202 |
|
203 | self.help = function () {
|
204 | var keys = Object.keys(
|
205 | Object.keys(descriptions)
|
206 | .concat(Object.keys(demanded))
|
207 | .concat(Object.keys(defaults))
|
208 | .reduce(function (acc, key) {
|
209 | if (key !== '_') acc[key] = true;
|
210 | return acc;
|
211 | }, {})
|
212 | );
|
213 |
|
214 | var help = keys.length ? [ 'Options:' ] : [];
|
215 |
|
216 | if (usage) {
|
217 | help.unshift(usage.replace(/\$0/g, self.$0), '');
|
218 | }
|
219 |
|
220 | var switches = keys.reduce(function (acc, key) {
|
221 | acc[key] = [ key ].concat(aliases[key] || [])
|
222 | .map(function (sw) {
|
223 | return (sw.length > 1 ? '--' : '-') + sw
|
224 | })
|
225 | .join(', ')
|
226 | ;
|
227 | return acc;
|
228 | }, {});
|
229 |
|
230 | var switchlen = longest(Object.keys(switches).map(function (s) {
|
231 | return switches[s] || '';
|
232 | }));
|
233 |
|
234 | var desclen = longest(Object.keys(descriptions).map(function (d) {
|
235 | return descriptions[d] || '';
|
236 | }));
|
237 |
|
238 | keys.forEach(function (key) {
|
239 | var kswitch = switches[key];
|
240 | var desc = descriptions[key] || '';
|
241 |
|
242 | if (wrap) {
|
243 | desc = wordwrap(switchlen + 4, wrap)(desc)
|
244 | .slice(switchlen + 4)
|
245 | ;
|
246 | }
|
247 |
|
248 | var spadding = new Array(
|
249 | Math.max(switchlen - kswitch.length + 3, 0)
|
250 | ).join(' ');
|
251 |
|
252 | var dpadding = new Array(
|
253 | Math.max(desclen - desc.length + 1, 0)
|
254 | ).join(' ');
|
255 |
|
256 | var type = null;
|
257 |
|
258 | if (flags.bools[key]) type = '[boolean]';
|
259 | if (flags.strings[key]) type = '[string]';
|
260 |
|
261 | if (!wrap && dpadding.length > 0) {
|
262 | desc += dpadding;
|
263 | }
|
264 |
|
265 | var prelude = ' ' + kswitch + spadding;
|
266 | var extra = [
|
267 | type,
|
268 | demanded[key]
|
269 | ? '[required]'
|
270 | : null
|
271 | ,
|
272 | defaults[key] !== undefined
|
273 | ? '[default: ' + JSON.stringify(defaults[key]) + ']'
|
274 | : null
|
275 | ,
|
276 | ].filter(Boolean).join(' ');
|
277 |
|
278 | var body = [ desc, extra ].filter(Boolean).join(' ');
|
279 |
|
280 | if (wrap) {
|
281 | var dlines = desc.split('\n');
|
282 | var dlen = dlines.slice(-1)[0].length
|
283 | + (dlines.length === 1 ? prelude.length : 0)
|
284 |
|
285 | body = desc + (dlen + extra.length > wrap - 2
|
286 | ? '\n'
|
287 | + new Array(wrap - extra.length + 1).join(' ')
|
288 | + extra
|
289 | : new Array(wrap - extra.length - dlen + 1).join(' ')
|
290 | + extra
|
291 | );
|
292 | }
|
293 |
|
294 | help.push(prelude + body);
|
295 | });
|
296 |
|
297 | help.push('');
|
298 | return help.join('\n');
|
299 | };
|
300 |
|
301 | Object.defineProperty(self, 'argv', {
|
302 | get : parseArgs,
|
303 | enumerable : true,
|
304 | });
|
305 |
|
306 | function parseArgs () {
|
307 | var argv = { _ : [], $0 : self.$0 };
|
308 | Object.keys(flags.bools).forEach(function (key) {
|
309 | setArg(key, defaults[key] || false);
|
310 | });
|
311 |
|
312 | function setArg (key, val) {
|
313 | var num = Number(val);
|
314 | var value = typeof val !== 'string' || isNaN(num) ? val : num;
|
315 | if (flags.strings[key]) value = val;
|
316 |
|
317 | setKey(argv, key.split('.'), value);
|
318 |
|
319 | (aliases[key] || []).forEach(function (x) {
|
320 | argv[x] = argv[key];
|
321 | });
|
322 | }
|
323 |
|
324 | for (var i = 0; i < args.length; i++) {
|
325 | var arg = args[i];
|
326 |
|
327 | if (arg === '--') {
|
328 | argv._.push.apply(argv._, args.slice(i + 1));
|
329 | break;
|
330 | }
|
331 | else if (arg.match(/^--.+=/)) {
|
332 |
|
333 |
|
334 |
|
335 | var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
|
336 | setArg(m[1], m[2]);
|
337 | }
|
338 | else if (arg.match(/^--no-.+/)) {
|
339 | var key = arg.match(/^--no-(.+)/)[1];
|
340 | setArg(key, false);
|
341 | }
|
342 | else if (arg.match(/^--.+/)) {
|
343 | var key = arg.match(/^--(.+)/)[1];
|
344 | var next = args[i + 1];
|
345 | if (next !== undefined && !next.match(/^-/)
|
346 | && !flags.bools[key]
|
347 | && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
|
348 | setArg(key, next);
|
349 | i++;
|
350 | }
|
351 | else if (/^(true|false)$/.test(next)) {
|
352 | setArg(key, next === 'true');
|
353 | i++;
|
354 | }
|
355 | else {
|
356 | setArg(key, true);
|
357 | }
|
358 | }
|
359 | else if (arg.match(/^-[^-]+/)) {
|
360 | var letters = arg.slice(1,-1).split('');
|
361 |
|
362 | var broken = false;
|
363 | for (var j = 0; j < letters.length; j++) {
|
364 | var next = arg.slice(j+2);
|
365 |
|
366 | if (next === '-') {
|
367 | setArg(letters[j], next)
|
368 | continue;
|
369 | }
|
370 |
|
371 | if (/[A-Za-z]/.test(letters[j])
|
372 | && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
|
373 | setArg(letters[j], next);
|
374 | break;
|
375 | }
|
376 |
|
377 | if (letters[j+1] && letters[j+1].match(/\W/)) {
|
378 | setArg(letters[j], arg.slice(j+2));
|
379 | broken = true;
|
380 | break;
|
381 | }
|
382 | else {
|
383 | setArg(letters[j], true);
|
384 | }
|
385 | }
|
386 |
|
387 | var key = arg.slice(-1)[0];
|
388 | if (!broken && key !== '-') {
|
389 |
|
390 | if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
|
391 | && !flags.bools[key]
|
392 | && (aliases[key] ? !flags.bools[aliases[key]] : true)) {
|
393 | setArg(key, args[i+1]);
|
394 | i++;
|
395 | }
|
396 | else if (args[i+1] && /true|false/.test(args[i+1])) {
|
397 | setArg(key, args[i+1] === 'true');
|
398 | i++;
|
399 | }
|
400 | else {
|
401 | setArg(key, true);
|
402 | }
|
403 | }
|
404 | }
|
405 | else {
|
406 | var n = Number(arg);
|
407 | argv._.push(flags.strings['_'] || isNaN(n) ? arg : n);
|
408 | }
|
409 | }
|
410 |
|
411 | Object.keys(defaults).forEach(function (key) {
|
412 | if (!(key in argv)) {
|
413 | argv[key] = defaults[key];
|
414 | if (key in aliases) {
|
415 | argv[aliases[key]] = defaults[key];
|
416 | }
|
417 | }
|
418 | });
|
419 |
|
420 | if (demanded._ && argv._.length < demanded._) {
|
421 | fail('Not enough non-option arguments: got '
|
422 | + argv._.length + ', need at least ' + demanded._
|
423 | );
|
424 | }
|
425 |
|
426 | var missing = [];
|
427 | Object.keys(demanded).forEach(function (key) {
|
428 | if (!argv[key]) missing.push(key);
|
429 | });
|
430 |
|
431 | if (missing.length) {
|
432 | fail('Missing required arguments: ' + missing.join(', '));
|
433 | }
|
434 |
|
435 | checks.forEach(function (f) {
|
436 | try {
|
437 | if (f(argv) === false) {
|
438 | fail('Argument check failed: ' + f.toString());
|
439 | }
|
440 | }
|
441 | catch (err) {
|
442 | fail(err)
|
443 | }
|
444 | });
|
445 |
|
446 | return argv;
|
447 | }
|
448 |
|
449 | function longest (xs) {
|
450 | return Math.max.apply(
|
451 | null,
|
452 | xs.map(function (x) { return x.length })
|
453 | );
|
454 | }
|
455 |
|
456 | return self;
|
457 | };
|
458 |
|
459 |
|
460 |
|
461 | exports.rebase = rebase;
|
462 | function rebase (base, dir) {
|
463 | var ds = path.normalize(dir).split('/').slice(1);
|
464 | var bs = path.normalize(base).split('/').slice(1);
|
465 |
|
466 | for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
|
467 | ds.splice(0, i); bs.splice(0, i);
|
468 |
|
469 | var p = path.normalize(
|
470 | bs.map(function () { return '..' }).concat(ds).join('/')
|
471 | ).replace(/\/$/,'').replace(/^$/, '.');
|
472 | return p.match(/^[.\/]/) ? p : './' + p;
|
473 | };
|
474 |
|
475 | function setKey (obj, keys, value) {
|
476 | var o = obj;
|
477 | keys.slice(0,-1).forEach(function (key) {
|
478 | if (o[key] === undefined) o[key] = {};
|
479 | o = o[key];
|
480 | });
|
481 |
|
482 | var key = keys[keys.length - 1];
|
483 | if (o[key] === undefined || typeof o[key] === 'boolean') {
|
484 | o[key] = value;
|
485 | }
|
486 | else if (Array.isArray(o[key])) {
|
487 | o[key].push(value);
|
488 | }
|
489 | else {
|
490 | o[key] = [ o[key], value ];
|
491 | }
|
492 | }
|