231 lines
8.2 KiB
JavaScript
231 lines
8.2 KiB
JavaScript
// Generated by CoffeeScript 2.7.0
|
||
(function() {
|
||
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat,
|
||
splice = [].splice;
|
||
|
||
({repeat} = require('./helpers'));
|
||
|
||
// A simple **OptionParser** class to parse option flags from the command-line.
|
||
// Use it like so:
|
||
|
||
// parser = new OptionParser switches, helpBanner
|
||
// options = parser.parse process.argv
|
||
|
||
// The first non-option is considered to be the start of the file (and file
|
||
// option) list, and all subsequent arguments are left unparsed.
|
||
|
||
// The `coffee` command uses an instance of **OptionParser** to parse its
|
||
// command-line arguments in `src/command.coffee`.
|
||
exports.OptionParser = OptionParser = class OptionParser {
|
||
// Initialize with a list of valid options, in the form:
|
||
|
||
// [short-flag, long-flag, description]
|
||
|
||
// Along with an optional banner for the usage help.
|
||
constructor(ruleDeclarations, banner) {
|
||
this.banner = banner;
|
||
this.rules = buildRules(ruleDeclarations);
|
||
}
|
||
|
||
// Parse the list of arguments, populating an `options` object with all of the
|
||
// specified options, and return it. Options after the first non-option
|
||
// argument are treated as arguments. `options.arguments` will be an array
|
||
// containing the remaining arguments. This is a simpler API than many option
|
||
// parsers that allow you to attach callback actions for every flag. Instead,
|
||
// you're responsible for interpreting the options object.
|
||
parse(args) {
|
||
var argument, hasArgument, i, isList, len, name, options, positional, rules;
|
||
// The CoffeeScript option parser is a little odd; options after the first
|
||
// non-option argument are treated as non-option arguments themselves.
|
||
// Optional arguments are normalized by expanding merged flags into multiple
|
||
// flags. This allows you to have `-wl` be the same as `--watch --lint`.
|
||
// Note that executable scripts with a shebang (`#!`) line should use the
|
||
// line `#!/usr/bin/env coffee`, or `#!/absolute/path/to/coffee`, without a
|
||
// `--` argument after, because that will fail on Linux (see #3946).
|
||
({rules, positional} = normalizeArguments(args, this.rules.flagDict));
|
||
options = {};
|
||
// The `argument` field is added to the rule instance non-destructively by
|
||
// `normalizeArguments`.
|
||
for (i = 0, len = rules.length; i < len; i++) {
|
||
({hasArgument, argument, isList, name} = rules[i]);
|
||
if (hasArgument) {
|
||
if (isList) {
|
||
if (options[name] == null) {
|
||
options[name] = [];
|
||
}
|
||
options[name].push(argument);
|
||
} else {
|
||
options[name] = argument;
|
||
}
|
||
} else {
|
||
options[name] = true;
|
||
}
|
||
}
|
||
if (positional[0] === '--') {
|
||
options.doubleDashed = true;
|
||
positional = positional.slice(1);
|
||
}
|
||
options.arguments = positional;
|
||
return options;
|
||
}
|
||
|
||
// Return the help text for this **OptionParser**, listing and describing all
|
||
// of the valid options, for `--help` and such.
|
||
help() {
|
||
var i, len, letPart, lines, ref, rule, spaces;
|
||
lines = [];
|
||
if (this.banner) {
|
||
lines.unshift(`${this.banner}\n`);
|
||
}
|
||
ref = this.rules.ruleList;
|
||
for (i = 0, len = ref.length; i < len; i++) {
|
||
rule = ref[i];
|
||
spaces = 15 - rule.longFlag.length;
|
||
spaces = spaces > 0 ? repeat(' ', spaces) : '';
|
||
letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' ';
|
||
lines.push(' ' + letPart + rule.longFlag + spaces + rule.description);
|
||
}
|
||
return `\n${lines.join('\n')}\n`;
|
||
}
|
||
|
||
};
|
||
|
||
// Helpers
|
||
// -------
|
||
|
||
// Regex matchers for option flags on the command line and their rules.
|
||
LONG_FLAG = /^(--\w[\w\-]*)/;
|
||
|
||
SHORT_FLAG = /^(-\w)$/;
|
||
|
||
MULTI_FLAG = /^-(\w{2,})/;
|
||
|
||
// Matches the long flag part of a rule for an option with an argument. Not
|
||
// applied to anything in process.argv.
|
||
OPTIONAL = /\[(\w+(\*?))\]/;
|
||
|
||
// Build and return the list of option rules. If the optional *short-flag* is
|
||
// unspecified, leave it out by padding with `null`.
|
||
buildRules = function(ruleDeclarations) {
|
||
var flag, flagDict, i, j, len, len1, ref, rule, ruleList, tuple;
|
||
ruleList = (function() {
|
||
var i, len, results;
|
||
results = [];
|
||
for (i = 0, len = ruleDeclarations.length; i < len; i++) {
|
||
tuple = ruleDeclarations[i];
|
||
if (tuple.length < 3) {
|
||
tuple.unshift(null);
|
||
}
|
||
results.push(buildRule(...tuple));
|
||
}
|
||
return results;
|
||
})();
|
||
flagDict = {};
|
||
for (i = 0, len = ruleList.length; i < len; i++) {
|
||
rule = ruleList[i];
|
||
ref = [rule.shortFlag, rule.longFlag];
|
||
// `shortFlag` is null if not provided in the rule.
|
||
for (j = 0, len1 = ref.length; j < len1; j++) {
|
||
flag = ref[j];
|
||
if (!(flag != null)) {
|
||
continue;
|
||
}
|
||
if (flagDict[flag] != null) {
|
||
throw new Error(`flag ${flag} for switch ${rule.name} was already declared for switch ${flagDict[flag].name}`);
|
||
}
|
||
flagDict[flag] = rule;
|
||
}
|
||
}
|
||
return {ruleList, flagDict};
|
||
};
|
||
|
||
// Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
|
||
// description of what the option does.
|
||
buildRule = function(shortFlag, longFlag, description) {
|
||
var match;
|
||
match = longFlag.match(OPTIONAL);
|
||
shortFlag = shortFlag != null ? shortFlag.match(SHORT_FLAG)[1] : void 0;
|
||
longFlag = longFlag.match(LONG_FLAG)[1];
|
||
return {
|
||
name: longFlag.replace(/^--/, ''),
|
||
shortFlag: shortFlag,
|
||
longFlag: longFlag,
|
||
description: description,
|
||
hasArgument: !!(match && match[1]),
|
||
isList: !!(match && match[2])
|
||
};
|
||
};
|
||
|
||
normalizeArguments = function(args, flagDict) {
|
||
var arg, argIndex, flag, i, innerOpts, j, lastOpt, len, len1, multiFlags, multiOpts, needsArgOpt, positional, ref, rule, rules, singleRule, withArg;
|
||
rules = [];
|
||
positional = [];
|
||
needsArgOpt = null;
|
||
for (argIndex = i = 0, len = args.length; i < len; argIndex = ++i) {
|
||
arg = args[argIndex];
|
||
// If the previous argument given to the script was an option that uses the
|
||
// next command-line argument as its argument, create copy of the option’s
|
||
// rule with an `argument` field.
|
||
if (needsArgOpt != null) {
|
||
withArg = Object.assign({}, needsArgOpt.rule, {
|
||
argument: arg
|
||
});
|
||
rules.push(withArg);
|
||
needsArgOpt = null;
|
||
continue;
|
||
}
|
||
multiFlags = (ref = arg.match(MULTI_FLAG)) != null ? ref[1].split('').map(function(flagName) {
|
||
return `-${flagName}`;
|
||
}) : void 0;
|
||
if (multiFlags != null) {
|
||
multiOpts = multiFlags.map(function(flag) {
|
||
var rule;
|
||
rule = flagDict[flag];
|
||
if (rule == null) {
|
||
throw new Error(`unrecognized option ${flag} in multi-flag ${arg}`);
|
||
}
|
||
return {rule, flag};
|
||
});
|
||
// Only the last flag in a multi-flag may have an argument.
|
||
[...innerOpts] = multiOpts, [lastOpt] = splice.call(innerOpts, -1);
|
||
for (j = 0, len1 = innerOpts.length; j < len1; j++) {
|
||
({rule, flag} = innerOpts[j]);
|
||
if (rule.hasArgument) {
|
||
throw new Error(`cannot use option ${flag} in multi-flag ${arg} except as the last option, because it needs an argument`);
|
||
}
|
||
rules.push(rule);
|
||
}
|
||
if (lastOpt.rule.hasArgument) {
|
||
needsArgOpt = lastOpt;
|
||
} else {
|
||
rules.push(lastOpt.rule);
|
||
}
|
||
} else if ([LONG_FLAG, SHORT_FLAG].some(function(pat) {
|
||
return arg.match(pat) != null;
|
||
})) {
|
||
singleRule = flagDict[arg];
|
||
if (singleRule == null) {
|
||
throw new Error(`unrecognized option ${arg}`);
|
||
}
|
||
if (singleRule.hasArgument) {
|
||
needsArgOpt = {
|
||
rule: singleRule,
|
||
flag: arg
|
||
};
|
||
} else {
|
||
rules.push(singleRule);
|
||
}
|
||
} else {
|
||
// This is a positional argument.
|
||
positional = args.slice(argIndex);
|
||
break;
|
||
}
|
||
}
|
||
if (needsArgOpt != null) {
|
||
throw new Error(`value required for ${needsArgOpt.flag}, but it was the last argument provided`);
|
||
}
|
||
return {rules, positional};
|
||
};
|
||
|
||
}).call(this);
|