Websocket
[VSoRC/.git] / node_modules / node-static / node_modules / optimist / index.js
1 var path = require('path');
2 var minimist = require('minimist');
3 var wordwrap = require('wordwrap');
4
5 /*  Hack an instance of Argv with process.argv into Argv
6     so people can do
7         require('optimist')(['--beeble=1','-z','zizzle']).argv
8     to parse a list of args and
9         require('optimist').argv
10     to get a parsed version of process.argv.
11 */
12
13 var inst = Argv(process.argv.slice(2));
14 Object.keys(inst).forEach(function (key) {
15     Argv[key] = typeof inst[key] == 'function'
16         ? inst[key].bind(inst)
17         : inst[key];
18 });
19
20 var exports = module.exports = Argv;
21 function Argv (processArgs, cwd) {
22     var self = {};
23     if (!cwd) cwd = process.cwd();
24     
25     self.$0 = process.argv
26         .slice(0,2)
27         .map(function (x) {
28             var b = rebase(cwd, x);
29             return x.match(/^\//) && b.length < x.length
30                 ? b : x
31         })
32         .join(' ')
33     ;
34     
35     if (process.env._ != undefined && process.argv[1] == process.env._) {
36         self.$0 = process.env._.replace(
37             path.dirname(process.execPath) + '/', ''
38         );
39     }
40     
41     var options = {
42         boolean: [],
43         string: [],
44         alias: {},
45         default: []
46     };
47     
48     self.boolean = function (bools) {
49         options.boolean.push.apply(options.boolean, [].concat(bools));
50         return self;
51     };
52     
53     self.string = function (strings) {
54         options.string.push.apply(options.string, [].concat(strings));
55         return self;
56     };
57     
58     self.default = function (key, value) {
59         if (typeof key === 'object') {
60             Object.keys(key).forEach(function (k) {
61                 self.default(k, key[k]);
62             });
63         }
64         else {
65             options.default[key] = value;
66         }
67         return self;
68     };
69     
70     self.alias = function (x, y) {
71         if (typeof x === 'object') {
72             Object.keys(x).forEach(function (key) {
73                 self.alias(key, x[key]);
74             });
75         }
76         else {
77             options.alias[x] = (options.alias[x] || []).concat(y);
78         }
79         return self;
80     };
81     
82     var demanded = {};
83     self.demand = function (keys) {
84         if (typeof keys == 'number') {
85             if (!demanded._) demanded._ = 0;
86             demanded._ += keys;
87         }
88         else if (Array.isArray(keys)) {
89             keys.forEach(function (key) {
90                 self.demand(key);
91             });
92         }
93         else {
94             demanded[keys] = true;
95         }
96         
97         return self;
98     };
99     
100     var usage;
101     self.usage = function (msg, opts) {
102         if (!opts && typeof msg === 'object') {
103             opts = msg;
104             msg = null;
105         }
106         
107         usage = msg;
108         
109         if (opts) self.options(opts);
110         
111         return self;
112     };
113     
114     function fail (msg) {
115         self.showHelp();
116         if (msg) console.error(msg);
117         process.exit(1);
118     }
119     
120     var checks = [];
121     self.check = function (f) {
122         checks.push(f);
123         return self;
124     };
125     
126     var descriptions = {};
127     self.describe = function (key, desc) {
128         if (typeof key === 'object') {
129             Object.keys(key).forEach(function (k) {
130                 self.describe(k, key[k]);
131             });
132         }
133         else {
134             descriptions[key] = desc;
135         }
136         return self;
137     };
138     
139     self.parse = function (args) {
140         return parseArgs(args);
141     };
142     
143     self.option = self.options = function (key, opt) {
144         if (typeof key === 'object') {
145             Object.keys(key).forEach(function (k) {
146                 self.options(k, key[k]);
147             });
148         }
149         else {
150             if (opt.alias) self.alias(key, opt.alias);
151             if (opt.demand) self.demand(key);
152             if (typeof opt.default !== 'undefined') {
153                 self.default(key, opt.default);
154             }
155             
156             if (opt.boolean || opt.type === 'boolean') {
157                 self.boolean(key);
158             }
159             if (opt.string || opt.type === 'string') {
160                 self.string(key);
161             }
162             
163             var desc = opt.describe || opt.description || opt.desc;
164             if (desc) {
165                 self.describe(key, desc);
166             }
167         }
168         
169         return self;
170     };
171     
172     var wrap = null;
173     self.wrap = function (cols) {
174         wrap = cols;
175         return self;
176     };
177     
178     self.showHelp = function (fn) {
179         if (!fn) fn = console.error;
180         fn(self.help());
181     };
182     
183     self.help = function () {
184         var keys = Object.keys(
185             Object.keys(descriptions)
186             .concat(Object.keys(demanded))
187             .concat(Object.keys(options.default))
188             .reduce(function (acc, key) {
189                 if (key !== '_') acc[key] = true;
190                 return acc;
191             }, {})
192         );
193         
194         var help = keys.length ? [ 'Options:' ] : [];
195         
196         if (usage) {
197             help.unshift(usage.replace(/\$0/g, self.$0), '');
198         }
199         
200         var switches = keys.reduce(function (acc, key) {
201             acc[key] = [ key ].concat(options.alias[key] || [])
202                 .map(function (sw) {
203                     return (sw.length > 1 ? '--' : '-') + sw
204                 })
205                 .join(', ')
206             ;
207             return acc;
208         }, {});
209         
210         var switchlen = longest(Object.keys(switches).map(function (s) {
211             return switches[s] || '';
212         }));
213         
214         var desclen = longest(Object.keys(descriptions).map(function (d) { 
215             return descriptions[d] || '';
216         }));
217         
218         keys.forEach(function (key) {
219             var kswitch = switches[key];
220             var desc = descriptions[key] || '';
221             
222             if (wrap) {
223                 desc = wordwrap(switchlen + 4, wrap)(desc)
224                     .slice(switchlen + 4)
225                 ;
226             }
227             
228             var spadding = new Array(
229                 Math.max(switchlen - kswitch.length + 3, 0)
230             ).join(' ');
231             
232             var dpadding = new Array(
233                 Math.max(desclen - desc.length + 1, 0)
234             ).join(' ');
235             
236             var type = null;
237             
238             if (options.boolean[key]) type = '[boolean]';
239             if (options.string[key]) type = '[string]';
240             
241             if (!wrap && dpadding.length > 0) {
242                 desc += dpadding;
243             }
244             
245             var prelude = '  ' + kswitch + spadding;
246             var extra = [
247                 type,
248                 demanded[key]
249                     ? '[required]'
250                     : null
251                 ,
252                 options.default[key] !== undefined
253                     ? '[default: ' + JSON.stringify(options.default[key]) + ']'
254                     : null
255                 ,
256             ].filter(Boolean).join('  ');
257             
258             var body = [ desc, extra ].filter(Boolean).join('  ');
259             
260             if (wrap) {
261                 var dlines = desc.split('\n');
262                 var dlen = dlines.slice(-1)[0].length
263                     + (dlines.length === 1 ? prelude.length : 0)
264                 
265                 body = desc + (dlen + extra.length > wrap - 2
266                     ? '\n'
267                         + new Array(wrap - extra.length + 1).join(' ')
268                         + extra
269                     : new Array(wrap - extra.length - dlen + 1).join(' ')
270                         + extra
271                 );
272             }
273             
274             help.push(prelude + body);
275         });
276         
277         help.push('');
278         return help.join('\n');
279     };
280     
281     Object.defineProperty(self, 'argv', {
282         get : function () { return parseArgs(processArgs) },
283         enumerable : true,
284     });
285     
286     function parseArgs (args) {
287         var argv = minimist(args, options);
288         argv.$0 = self.$0;
289         
290         if (demanded._ && argv._.length < demanded._) {
291             fail('Not enough non-option arguments: got '
292                 + argv._.length + ', need at least ' + demanded._
293             );
294         }
295         
296         var missing = [];
297         Object.keys(demanded).forEach(function (key) {
298             if (!argv[key]) missing.push(key);
299         });
300         
301         if (missing.length) {
302             fail('Missing required arguments: ' + missing.join(', '));
303         }
304         
305         checks.forEach(function (f) {
306             try {
307                 if (f(argv) === false) {
308                     fail('Argument check failed: ' + f.toString());
309                 }
310             }
311             catch (err) {
312                 fail(err)
313             }
314         });
315         
316         return argv;
317     }
318     
319     function longest (xs) {
320         return Math.max.apply(
321             null,
322             xs.map(function (x) { return x.length })
323         );
324     }
325     
326     return self;
327 };
328
329 // rebase an absolute path to a relative one with respect to a base directory
330 // exported for tests
331 exports.rebase = rebase;
332 function rebase (base, dir) {
333     var ds = path.normalize(dir).split('/').slice(1);
334     var bs = path.normalize(base).split('/').slice(1);
335     
336     for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
337     ds.splice(0, i); bs.splice(0, i);
338     
339     var p = path.normalize(
340         bs.map(function () { return '..' }).concat(ds).join('/')
341     ).replace(/\/$/,'').replace(/^$/, '.');
342     return p.match(/^[.\/]/) ? p : './' + p;
343 };