jashkenas--coffeescript/lib/coffeescript/optparse.js

231 lines
8.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Generated by CoffeeScript 2.0.2
(function() {
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat,
slice = [].slice;
({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, k, 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 options
// 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 = 2 <= multiOpts.length ? slice.call(multiOpts, 0, j = multiOpts.length - 1) : (j = 0, []), lastOpt = multiOpts[j++];
for (k = 0, len1 = innerOpts.length; k < len1; k++) {
({rule, flag} = innerOpts[k]);
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);