mirror of
				https://github.com/therootcompany/greenlock.js.git
				synced 2024-11-16 17:29:00 +00:00 
			
		
		
		
	
		
			
	
	
		
			235 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			235 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var CLI = module.exports; | ||
|  | 
 | ||
|  | var defaultConf; | ||
|  | var defaultOpts; | ||
|  | var bags = []; | ||
|  | 
 | ||
|  | CLI.parse = function(conf) { | ||
|  | 	var opts = (defaultOpts = {}); | ||
|  | 	defaultConf = conf; | ||
|  | 
 | ||
|  | 	Object.keys(conf).forEach(function(k) { | ||
|  | 		var v = conf[k]; | ||
|  | 		var aliases = v[5]; | ||
|  | 		var bag; | ||
|  | 		var bagName; | ||
|  | 
 | ||
|  | 		// the name of the argument set is now the 0th argument
 | ||
|  | 		v.unshift(k); | ||
|  | 		// v[0] flagname
 | ||
|  | 		// v[1] short flagname
 | ||
|  | 		// v[2] description
 | ||
|  | 		// v[3] type
 | ||
|  | 		// v[4] default value
 | ||
|  | 		// v[5] aliases
 | ||
|  | 
 | ||
|  | 		if ('bag' === v[3]) { | ||
|  | 			bag = v[0]; // 'bag-option-xxxx' => '--bag-option-'
 | ||
|  | 			bag = '--' + bag.replace(/xxx.*/, ''); | ||
|  | 			bags.push(bag); | ||
|  | 
 | ||
|  | 			bagName = toBagName(bag.replace(/^--/, '')); | ||
|  | 			opts[bagName] = {}; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if ('json' === v[3]) { | ||
|  | 			bagName = toBagName(v[0].replace(/-json$/, '')); // 'bag-option-json' => 'bagOptionOpts'
 | ||
|  | 			opts[bagName] = {}; | ||
|  | 		} else if ('ignore' !== v[3] && 'undefined' !== typeof v[4]) { | ||
|  | 			// set the default values (where 'undefined' is not an allowed value)
 | ||
|  | 			opts[toCamel(k)] = v[4]; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (!aliases) { | ||
|  | 			aliases = []; | ||
|  | 		} else if ('string' === typeof aliases) { | ||
|  | 			aliases = aliases.split(','); | ||
|  | 		} | ||
|  | 		aliases.forEach(function(alias) { | ||
|  | 			if (alias in conf) { | ||
|  | 				throw new Error( | ||
|  | 					"Cannot alias '" + | ||
|  | 						alias + | ||
|  | 						"' from '" + | ||
|  | 						k + | ||
|  | 						"': option already exists" | ||
|  | 				); | ||
|  | 			} | ||
|  | 			conf[alias] = v; | ||
|  | 		}); | ||
|  | 	}); | ||
|  | }; | ||
|  | 
 | ||
|  | CLI.main = function(cb, args) { | ||
|  | 	var leftovers = []; | ||
|  | 	var conf = defaultConf; | ||
|  | 	var opts = defaultOpts; | ||
|  | 
 | ||
|  | 	if (!opts) { | ||
|  | 		throw new Error("you didn't call `CLI.parse(configuration)`"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// TODO what's the existing API for this?
 | ||
|  | 	if (!args) { | ||
|  | 		args = process.argv.slice(2); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var flag; | ||
|  | 	var cnf; | ||
|  | 	var typ; | ||
|  | 
 | ||
|  | 	function grab(bag) { | ||
|  | 		var bagName = toBagName(bag); | ||
|  | 		if (bag !== flag.slice(0, bag.length)) { | ||
|  | 			return false; | ||
|  | 		} | ||
|  | 		console.log(bagName, toCamel(flag.slice(bag.length))); | ||
|  | 		opts[bagName][toCamel(flag.slice(bag.length))] = args.shift(); | ||
|  | 		return true; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	while (args.length) { | ||
|  | 		// take one off the top
 | ||
|  | 		flag = args.shift(); | ||
|  | 
 | ||
|  | 		// mind the gap
 | ||
|  | 		if ('--' === flag) { | ||
|  | 			leftovers = leftovers.concat(args); | ||
|  | 			break; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// help!
 | ||
|  | 		if ( | ||
|  | 			'--help' === flag || | ||
|  | 			'-h' === flag || | ||
|  | 			'/?' === flag || | ||
|  | 			'help' === flag | ||
|  | 		) { | ||
|  | 			printHelp(conf); | ||
|  | 			process.exit(1); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// only long names are actually used
 | ||
|  | 		if ('--' !== flag.slice(0, 2)) { | ||
|  | 			console.error("Unrecognized argument '" + flag + "'"); | ||
|  | 			process.exit(1); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		cnf = conf[flag.slice(2)]; | ||
|  | 		if (!cnf) { | ||
|  | 			// look for arbitrary flags
 | ||
|  | 			if (bags.some(grab)) { | ||
|  | 				continue; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// other arbitrary args are not used
 | ||
|  | 			console.error("Unrecognized flag '" + flag + "'"); | ||
|  | 			process.exit(1); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// encourage switching to non-aliased version
 | ||
|  | 		if (flag !== '--' + cnf[0]) { | ||
|  | 			console.warn( | ||
|  | 				"use of '" + | ||
|  | 					flag + | ||
|  | 					"' is deprecated, use '--" + | ||
|  | 					cnf[0] + | ||
|  | 					"' instead" | ||
|  | 			); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// look for xxx-json flags
 | ||
|  | 		if ('json' === cnf[3]) { | ||
|  | 			try { | ||
|  | 				var json = JSON.parse(args.shift()); | ||
|  | 				var bagName = toBagName(cnf[0].replace(/-json$/, '')); | ||
|  | 				Object.keys(json).forEach(function(k) { | ||
|  | 					opts[bagName][k] = json[k]; | ||
|  | 				}); | ||
|  | 			} catch (e) { | ||
|  | 				console.error("Could not parse option '" + flag + "' as JSON:"); | ||
|  | 				console.error(e.message); | ||
|  | 				process.exit(1); | ||
|  | 			} | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// set booleans, otherwise grab the next arg in line
 | ||
|  | 		typ = cnf[3]; | ||
|  | 		// TODO --no-<whatever> to negate
 | ||
|  | 		if (Boolean === typ || 'boolean' === typ) { | ||
|  | 			opts[toCamel(cnf[0])] = true; | ||
|  | 			continue; | ||
|  | 		} | ||
|  | 		opts[toCamel(cnf[0])] = args.shift(); | ||
|  | 		continue; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	cb(leftovers, opts); | ||
|  | }; | ||
|  | 
 | ||
|  | function toCamel(str) { | ||
|  | 	return str.replace(/-([a-z0-9])/g, function(m) { | ||
|  | 		return m[1].toUpperCase(); | ||
|  | 	}); | ||
|  | } | ||
|  | 
 | ||
|  | function toBagName(bag) { | ||
|  | 	// trim leading and trailing '-'
 | ||
|  |   bag = bag.replace(/^-+/g, '').replace(/-+$/g, '') | ||
|  | 	return toCamel(bag) + 'Opts'; // '--bag-option-' => bagOptionOpts
 | ||
|  | } | ||
|  | 
 | ||
|  | function printHelp(conf) { | ||
|  | 	var flagLen = 0; | ||
|  | 	var typeLen = 0; | ||
|  | 	var defLen = 0; | ||
|  | 
 | ||
|  | 	Object.keys(conf).forEach(function(k) { | ||
|  | 		flagLen = Math.max(flagLen, conf[k][0].length); | ||
|  | 		typeLen = Math.max(typeLen, conf[k][3].length); | ||
|  | 		if ('undefined' !== typeof conf[k][4]) { | ||
|  | 			defLen = Math.max( | ||
|  | 				defLen, | ||
|  | 				'(Default: )'.length + String(conf[k][4]).length | ||
|  | 			); | ||
|  | 		} | ||
|  | 	}); | ||
|  | 
 | ||
|  | 	Object.keys(conf).forEach(function(k) { | ||
|  | 		var v = conf[k]; | ||
|  | 
 | ||
|  | 		// skip aliases
 | ||
|  | 		if (v[0] !== k) { | ||
|  | 			return; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var def = v[4]; | ||
|  | 		if ('undefined' === typeof def) { | ||
|  | 			def = ''; | ||
|  | 		} else { | ||
|  | 			def = '(default: ' + JSON.stringify(def) + ')'; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		var msg = | ||
|  | 			' --' + | ||
|  | 			v[0].padEnd(flagLen) + | ||
|  | 			' ' + | ||
|  | 			v[3].padStart(typeLen + 1) + | ||
|  | 			' ' + | ||
|  | 			(v[2] || '') + | ||
|  | 			' ' + | ||
|  | 			def; /*.padStart(defLen)*/ | ||
|  | 		// v[0] flagname
 | ||
|  | 		// v[1] short flagname
 | ||
|  | 		// v[2] description
 | ||
|  | 		// v[3] type
 | ||
|  | 		// v[4] default value
 | ||
|  | 		// v[5] aliases
 | ||
|  | 
 | ||
|  | 		console.info(msg); | ||
|  | 	}); | ||
|  | } |