344 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			344 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								var path = require('path');
							 | 
						||
| 
								 | 
							
								var minimist = require('minimist');
							 | 
						||
| 
								 | 
							
								var wordwrap = require('wordwrap');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*  Hack an instance of Argv with process.argv into Argv
							 | 
						||
| 
								 | 
							
								    so people can do
							 | 
						||
| 
								 | 
							
								        require('optimist')(['--beeble=1','-z','zizzle']).argv
							 | 
						||
| 
								 | 
							
								    to parse a list of args and
							 | 
						||
| 
								 | 
							
								        require('optimist').argv
							 | 
						||
| 
								 | 
							
								    to get a parsed version of process.argv.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var inst = Argv(process.argv.slice(2));
							 | 
						||
| 
								 | 
							
								Object.keys(inst).forEach(function (key) {
							 | 
						||
| 
								 | 
							
								    Argv[key] = typeof inst[key] == 'function'
							 | 
						||
| 
								 | 
							
								        ? inst[key].bind(inst)
							 | 
						||
| 
								 | 
							
								        : inst[key];
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var exports = module.exports = Argv;
							 | 
						||
| 
								 | 
							
								function Argv (processArgs, cwd) {
							 | 
						||
| 
								 | 
							
								    var self = {};
							 | 
						||
| 
								 | 
							
								    if (!cwd) cwd = process.cwd();
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.$0 = process.argv
							 | 
						||
| 
								 | 
							
								        .slice(0,2)
							 | 
						||
| 
								 | 
							
								        .map(function (x) {
							 | 
						||
| 
								 | 
							
								            var b = rebase(cwd, x);
							 | 
						||
| 
								 | 
							
								            return x.match(/^\//) && b.length < x.length
							 | 
						||
| 
								 | 
							
								                ? b : x
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								        .join(' ')
							 | 
						||
| 
								 | 
							
								    ;
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    if (process.env._ != undefined && process.argv[1] == process.env._) {
							 | 
						||
| 
								 | 
							
								        self.$0 = process.env._.replace(
							 | 
						||
| 
								 | 
							
								            path.dirname(process.execPath) + '/', ''
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var options = {
							 | 
						||
| 
								 | 
							
								        boolean: [],
							 | 
						||
| 
								 | 
							
								        string: [],
							 | 
						||
| 
								 | 
							
								        alias: {},
							 | 
						||
| 
								 | 
							
								        default: []
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.boolean = function (bools) {
							 | 
						||
| 
								 | 
							
								        options.boolean.push.apply(options.boolean, [].concat(bools));
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.string = function (strings) {
							 | 
						||
| 
								 | 
							
								        options.string.push.apply(options.string, [].concat(strings));
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.default = function (key, value) {
							 | 
						||
| 
								 | 
							
								        if (typeof key === 'object') {
							 | 
						||
| 
								 | 
							
								            Object.keys(key).forEach(function (k) {
							 | 
						||
| 
								 | 
							
								                self.default(k, key[k]);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            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;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var demanded = {};
							 | 
						||
| 
								 | 
							
								    self.demand = function (keys) {
							 | 
						||
| 
								 | 
							
								        if (typeof keys == 'number') {
							 | 
						||
| 
								 | 
							
								            if (!demanded._) demanded._ = 0;
							 | 
						||
| 
								 | 
							
								            demanded._ += keys;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (Array.isArray(keys)) {
							 | 
						||
| 
								 | 
							
								            keys.forEach(function (key) {
							 | 
						||
| 
								 | 
							
								                self.demand(key);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            demanded[keys] = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var usage;
							 | 
						||
| 
								 | 
							
								    self.usage = function (msg, opts) {
							 | 
						||
| 
								 | 
							
								        if (!opts && typeof msg === 'object') {
							 | 
						||
| 
								 | 
							
								            opts = msg;
							 | 
						||
| 
								 | 
							
								            msg = null;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        usage = msg;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if (opts) self.options(opts);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function fail (msg) {
							 | 
						||
| 
								 | 
							
								        self.showHelp();
							 | 
						||
| 
								 | 
							
								        if (msg) console.error(msg);
							 | 
						||
| 
								 | 
							
								        process.exit(1);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var checks = [];
							 | 
						||
| 
								 | 
							
								    self.check = function (f) {
							 | 
						||
| 
								 | 
							
								        checks.push(f);
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var descriptions = {};
							 | 
						||
| 
								 | 
							
								    self.describe = function (key, desc) {
							 | 
						||
| 
								 | 
							
								        if (typeof key === 'object') {
							 | 
						||
| 
								 | 
							
								            Object.keys(key).forEach(function (k) {
							 | 
						||
| 
								 | 
							
								                self.describe(k, key[k]);
							 | 
						||
| 
								 | 
							
								            });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else {
							 | 
						||
| 
								 | 
							
								            descriptions[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 {
							 | 
						||
| 
								 | 
							
								            if (opt.alias) self.alias(key, opt.alias);
							 | 
						||
| 
								 | 
							
								            if (opt.demand) self.demand(key);
							 | 
						||
| 
								 | 
							
								            if (typeof opt.default !== 'undefined') {
							 | 
						||
| 
								 | 
							
								                self.default(key, opt.default);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (opt.boolean || opt.type === 'boolean') {
							 | 
						||
| 
								 | 
							
								                self.boolean(key);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (opt.string || opt.type === 'string') {
							 | 
						||
| 
								 | 
							
								                self.string(key);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var desc = opt.describe || opt.description || opt.desc;
							 | 
						||
| 
								 | 
							
								            if (desc) {
							 | 
						||
| 
								 | 
							
								                self.describe(key, desc);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var wrap = null;
							 | 
						||
| 
								 | 
							
								    self.wrap = function (cols) {
							 | 
						||
| 
								 | 
							
								        wrap = cols;
							 | 
						||
| 
								 | 
							
								        return self;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.showHelp = function (fn) {
							 | 
						||
| 
								 | 
							
								        if (!fn) fn = console.error;
							 | 
						||
| 
								 | 
							
								        fn(self.help());
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    self.help = function () {
							 | 
						||
| 
								 | 
							
								        var keys = Object.keys(
							 | 
						||
| 
								 | 
							
								            Object.keys(descriptions)
							 | 
						||
| 
								 | 
							
								            .concat(Object.keys(demanded))
							 | 
						||
| 
								 | 
							
								            .concat(Object.keys(options.default))
							 | 
						||
| 
								 | 
							
								            .reduce(function (acc, key) {
							 | 
						||
| 
								 | 
							
								                if (key !== '_') acc[key] = true;
							 | 
						||
| 
								 | 
							
								                return acc;
							 | 
						||
| 
								 | 
							
								            }, {})
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        var help = keys.length ? [ 'Options:' ] : [];
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if (usage) {
							 | 
						||
| 
								 | 
							
								            help.unshift(usage.replace(/\$0/g, self.$0), '');
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        var switches = keys.reduce(function (acc, key) {
							 | 
						||
| 
								 | 
							
								            acc[key] = [ key ].concat(options.alias[key] || [])
							 | 
						||
| 
								 | 
							
								                .map(function (sw) {
							 | 
						||
| 
								 | 
							
								                    return (sw.length > 1 ? '--' : '-') + sw
							 | 
						||
| 
								 | 
							
								                })
							 | 
						||
| 
								 | 
							
								                .join(', ')
							 | 
						||
| 
								 | 
							
								            ;
							 | 
						||
| 
								 | 
							
								            return acc;
							 | 
						||
| 
								 | 
							
								        }, {});
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        var switchlen = longest(Object.keys(switches).map(function (s) {
							 | 
						||
| 
								 | 
							
								            return switches[s] || '';
							 | 
						||
| 
								 | 
							
								        }));
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        var desclen = longest(Object.keys(descriptions).map(function (d) { 
							 | 
						||
| 
								 | 
							
								            return descriptions[d] || '';
							 | 
						||
| 
								 | 
							
								        }));
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        keys.forEach(function (key) {
							 | 
						||
| 
								 | 
							
								            var kswitch = switches[key];
							 | 
						||
| 
								 | 
							
								            var desc = descriptions[key] || '';
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (wrap) {
							 | 
						||
| 
								 | 
							
								                desc = wordwrap(switchlen + 4, wrap)(desc)
							 | 
						||
| 
								 | 
							
								                    .slice(switchlen + 4)
							 | 
						||
| 
								 | 
							
								                ;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var spadding = new Array(
							 | 
						||
| 
								 | 
							
								                Math.max(switchlen - kswitch.length + 3, 0)
							 | 
						||
| 
								 | 
							
								            ).join(' ');
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var dpadding = new Array(
							 | 
						||
| 
								 | 
							
								                Math.max(desclen - desc.length + 1, 0)
							 | 
						||
| 
								 | 
							
								            ).join(' ');
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var type = null;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (options.boolean[key]) type = '[boolean]';
							 | 
						||
| 
								 | 
							
								            if (options.string[key]) type = '[string]';
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (!wrap && dpadding.length > 0) {
							 | 
						||
| 
								 | 
							
								                desc += dpadding;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var prelude = '  ' + kswitch + spadding;
							 | 
						||
| 
								 | 
							
								            var extra = [
							 | 
						||
| 
								 | 
							
								                type,
							 | 
						||
| 
								 | 
							
								                demanded[key]
							 | 
						||
| 
								 | 
							
								                    ? '[required]'
							 | 
						||
| 
								 | 
							
								                    : null
							 | 
						||
| 
								 | 
							
								                ,
							 | 
						||
| 
								 | 
							
								                options.default[key] !== undefined
							 | 
						||
| 
								 | 
							
								                    ? '[default: ' + JSON.stringify(options.default[key]) + ']'
							 | 
						||
| 
								 | 
							
								                    : null
							 | 
						||
| 
								 | 
							
								                ,
							 | 
						||
| 
								 | 
							
								            ].filter(Boolean).join('  ');
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            var body = [ desc, extra ].filter(Boolean).join('  ');
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (wrap) {
							 | 
						||
| 
								 | 
							
								                var dlines = desc.split('\n');
							 | 
						||
| 
								 | 
							
								                var dlen = dlines.slice(-1)[0].length
							 | 
						||
| 
								 | 
							
								                    + (dlines.length === 1 ? prelude.length : 0)
							 | 
						||
| 
								 | 
							
								                
							 | 
						||
| 
								 | 
							
								                body = desc + (dlen + extra.length > wrap - 2
							 | 
						||
| 
								 | 
							
								                    ? '\n'
							 | 
						||
| 
								 | 
							
								                        + new Array(wrap - extra.length + 1).join(' ')
							 | 
						||
| 
								 | 
							
								                        + extra
							 | 
						||
| 
								 | 
							
								                    : new Array(wrap - extra.length - dlen + 1).join(' ')
							 | 
						||
| 
								 | 
							
								                        + extra
							 | 
						||
| 
								 | 
							
								                );
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            help.push(prelude + body);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        help.push('');
							 | 
						||
| 
								 | 
							
								        return help.join('\n');
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    Object.defineProperty(self, 'argv', {
							 | 
						||
| 
								 | 
							
								        get : function () { return parseArgs(processArgs) },
							 | 
						||
| 
								 | 
							
								        enumerable : true,
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function parseArgs (args) {
							 | 
						||
| 
								 | 
							
								        var argv = minimist(args, options);
							 | 
						||
| 
								 | 
							
								        argv.$0 = self.$0;
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if (demanded._ && argv._.length < demanded._) {
							 | 
						||
| 
								 | 
							
								            fail('Not enough non-option arguments: got '
							 | 
						||
| 
								 | 
							
								                + argv._.length + ', need at least ' + demanded._
							 | 
						||
| 
								 | 
							
								            );
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        var missing = [];
							 | 
						||
| 
								 | 
							
								        Object.keys(demanded).forEach(function (key) {
							 | 
						||
| 
								 | 
							
								            if (!argv[key]) missing.push(key);
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        if (missing.length) {
							 | 
						||
| 
								 | 
							
								            fail('Missing required arguments: ' + missing.join(', '));
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        checks.forEach(function (f) {
							 | 
						||
| 
								 | 
							
								            try {
							 | 
						||
| 
								 | 
							
								                if (f(argv) === false) {
							 | 
						||
| 
								 | 
							
								                    fail('Argument check failed: ' + f.toString());
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            catch (err) {
							 | 
						||
| 
								 | 
							
								                fail(err)
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        });
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        return argv;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    function longest (xs) {
							 | 
						||
| 
								 | 
							
								        return Math.max.apply(
							 | 
						||
| 
								 | 
							
								            null,
							 | 
						||
| 
								 | 
							
								            xs.map(function (x) { return x.length })
							 | 
						||
| 
								 | 
							
								        );
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    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) {
							 | 
						||
| 
								 | 
							
								    var ds = path.normalize(dir).split('/').slice(1);
							 | 
						||
| 
								 | 
							
								    var bs = path.normalize(base).split('/').slice(1);
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
							 | 
						||
| 
								 | 
							
								    ds.splice(0, i); bs.splice(0, i);
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    var p = path.normalize(
							 | 
						||
| 
								 | 
							
								        bs.map(function () { return '..' }).concat(ds).join('/')
							 | 
						||
| 
								 | 
							
								    ).replace(/\/$/,'').replace(/^$/, '.');
							 | 
						||
| 
								 | 
							
								    return p.match(/^[.\/]/) ? p : './' + p;
							 | 
						||
| 
								 | 
							
								};
							 |