1/* 2 * Jake JavaScript build tool 3 * Copyright 2112 Matthew Eernisse (mde@fleegix.org) 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17*/ 18 19let parseargs = {}; 20let isOpt = function (arg) { return arg.indexOf('-') === 0; }; 21let removeOptPrefix = function (opt) { return opt.replace(/^--/, '').replace(/^-/, ''); }; 22 23/** 24 * @constructor 25 * Parses a list of command-line args into a key/value object of 26 * options and an array of positional commands. 27 * @ param {Array} opts A list of options in the following format: 28 * [{full: 'foo', abbr: 'f'}, {full: 'bar', abbr: 'b'}]] 29 */ 30parseargs.Parser = function (opts) { 31 // A key/value object of matching options parsed out of the args 32 this.opts = {}; 33 this.taskNames = null; 34 this.envVars = null; 35 36 // Data structures used for parsing 37 this.reg = opts; 38 this.shortOpts = {}; 39 this.longOpts = {}; 40 41 let self = this; 42 [].forEach.call(opts, function (item) { 43 self.shortOpts[item.abbr] = item; 44 self.longOpts[item.full] = item; 45 }); 46}; 47 48parseargs.Parser.prototype = new function () { 49 50 let _trueOrNextVal = function (argParts, args) { 51 if (argParts[1]) { 52 return argParts[1]; 53 } 54 else { 55 return (!args[0] || isOpt(args[0])) ? 56 true : args.shift(); 57 } 58 }; 59 60 /** 61 * Parses an array of arguments into options and positional commands 62 * @param {Array} args The command-line args to parse 63 */ 64 this.parse = function (args) { 65 let cmds = []; 66 let cmd; 67 let envVars = {}; 68 let opts = {}; 69 let arg; 70 let argItem; 71 let argParts; 72 let cmdItems; 73 let taskNames = []; 74 let preempt; 75 76 while (args.length) { 77 arg = args.shift(); 78 79 if (isOpt(arg)) { 80 arg = removeOptPrefix(arg); 81 argParts = arg.split('='); 82 argItem = this.longOpts[argParts[0]] || this.shortOpts[argParts[0]]; 83 if (argItem) { 84 // First-encountered preemptive opt takes precedence -- no further opts 85 // or possibility of ambiguity, so just look for a value, or set to 86 // true and then bail 87 if (argItem.preempts) { 88 opts[argItem.full] = _trueOrNextVal(argParts, args); 89 preempt = true; 90 break; 91 } 92 // If the opt requires a value, see if we can get a value from the 93 // next arg, or infer true from no-arg -- if it's followed by another 94 // opt, throw an error 95 if (argItem.expectValue || argItem.allowValue) { 96 opts[argItem.full] = _trueOrNextVal(argParts, args); 97 if (argItem.expectValue && !opts[argItem.full]) { 98 throw new Error(argItem.full + ' option expects a value.'); 99 } 100 } 101 else { 102 opts[argItem.full] = true; 103 } 104 } 105 } 106 else { 107 cmds.unshift(arg); 108 } 109 } 110 111 if (!preempt) { 112 // Parse out any env-vars and task-name 113 while ((cmd = cmds.pop())) { 114 cmdItems = cmd.split('='); 115 if (cmdItems.length > 1) { 116 envVars[cmdItems[0]] = cmdItems[1]; 117 } 118 else { 119 taskNames.push(cmd); 120 } 121 } 122 123 } 124 125 return { 126 opts: opts, 127 envVars: envVars, 128 taskNames: taskNames 129 }; 130 }; 131 132}; 133 134module.exports = parseargs; 135