[Pkg-javascript-commits] [node-nomnom] 01/02: Imported Upstream version 1.8.1
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Fri Mar 20 20:47:44 UTC 2015
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository node-nomnom.
commit 64904f5383324dfde760ee722ad55c5c9963883a
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Tue Mar 17 22:30:20 2015 +0100
Imported Upstream version 1.8.1
---
.npmignore | 1 +
LICENSE | 20 ++
README.md | 325 ++++++++++++++++++++++++++++++
nomnom.js | 584 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
num-vals-fix.diff | 31 +++
package.json | 28 +++
test.js | 23 +++
test/callback.js | 33 +++
test/commands.js | 120 +++++++++++
test/expected.js | 60 ++++++
test/matching.js | 70 +++++++
test/option.js | 44 ++++
test/transform.js | 65 ++++++
test/usage.js | 121 +++++++++++
test/values.js | 75 +++++++
15 files changed, 1600 insertions(+)
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8092929
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Heather Arthur
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5864a09
--- /dev/null
+++ b/README.md
@@ -0,0 +1,325 @@
+# nomnom
+nomnom is an option parser for node. It noms your args and gives them back to you in a hash.
+
+```javascript
+var opts = require("nomnom")
+ .option('debug', {
+ abbr: 'd',
+ flag: true,
+ help: 'Print debugging info'
+ })
+ .option('config', {
+ abbr: 'c',
+ default: 'config.json',
+ help: 'JSON file with tests to run'
+ })
+ .option('version', {
+ flag: true,
+ help: 'print version and exit',
+ callback: function() {
+ return "version 1.2.4";
+ }
+ })
+ .parse();
+
+if (opts.debug)
+ // do stuff
+```
+
+You don't have to specify anything if you don't want to:
+
+```javascript
+var opts = require("nomnom").parse();
+
+var url = opts[0]; // get the first positional arg
+var file = opts.file // see if --file was specified
+var verbose = opts.v // see if -v was specified
+var extras = opts._ // get an array of the unmatched, positional args
+```
+
+# Install
+for [node.js](http://nodejs.org/) and [npm](http://github.com/isaacs/npm):
+
+ npm install nomnom
+
+# More Details
+Nomnom supports args like `-d`, `--debug`, `--no-debug`, `--file=test.txt`, `--file test.txt`, `-f test.txt`, `-xvf`, and positionals. Positionals are arguments that don't fit the `-a` or `--atomic` format and aren't attached to an option.
+
+Values are JSON parsed, so `--debug=true --count=3 --file=log.txt` would give you:
+
+```
+{
+ "debug": true,
+ "count": 3,
+ "file": "log.txt"
+}
+```
+
+# Commands
+Nomnom supports command-based interfaces (e.g. with git: `git add -p` and `git rebase -i` where `add` and `rebase` are the commands):
+
+```javascript
+var parser = require("nomnom");
+
+parser.command('browser')
+ .callback(function(opts) {
+ runBrowser(opts.url);
+ })
+ .help("run browser tests");
+
+parser.command('sanity')
+ .option('outfile', {
+ abbr: 'o',
+ help: "file to write results to"
+ })
+ .option('config', {
+ abbr: 'c',
+ default: 'config.json',
+ help: "json manifest of tests to run"
+ })
+ .callback(function(opts) {
+ runSanity(opts.filename);
+ })
+ .help("run the sanity tests")
+
+parser.parse();
+```
+
+Each command generates its own usage message when `-h` or `--help` is specified with the command.
+
+# Usage
+Nomnom prints out a usage message if `--help` or `-h` is an argument. Usage for these options in `test.js`:
+
+```javascript
+var opts = require("nomnom")
+ .script("runtests")
+ .options({
+ path: {
+ position: 0,
+ help: "Test file to run",
+ list: true
+ },
+ config: {
+ abbr: 'c',
+ metavar: 'FILE',
+ help: "Config file with tests to run"
+ },
+ debug: {
+ abbr: 'd',
+ flag: true,
+ help: "Print debugging info"
+ }
+ }).parse();
+```
+
+...would look like this:
+
+ usage: runtests <path>... [options]
+
+ path Test file to run
+
+ options:
+ -c FILE, --config FILE Config file with tests to run
+ -d, --debug Print debugging info
+
+# Options
+You can either add a specification for an option with `nomnom.option('name', spec)` or pass the specifications to `nomnom.options()` as a hash keyed on option name. Each option specification can have the following fields:
+
+#### abbr and full
+`abbr` is the single character string to match to this option, `full` is the full-length string (defaults to the name of the option).
+
+This option matches `-d` and `--debug` on the command line:
+
+```javascript
+nomnom.option('debug', {
+ abbr: 'd'
+})
+```
+
+This option matches `-n 3`, `--num-lines 12` on the command line:
+
+```javascript
+nomnom.option('numLines', {
+ abbr: 'n',
+ full: 'num-lines'
+})
+```
+
+#### flag
+
+If this is set to true, the option acts as a flag and doesn't swallow the next value on the command line. Default is `false`, so normally if you had a command line `--config test.js`, `config` would get a value of `test.js` in the options hash. Whereas if you specify:
+
+```javascript
+nomnom.option('config', {
+ flag: true
+})
+```
+
+`config` would get a value of `true` in the options hash, and `test.js` would be a free positional arg.
+
+#### metavar
+
+`metavar` is used in the usage printout e.g. `"PATH"` in `"-f PATH, --file PATH"`.
+
+#### string
+
+A shorthand for `abbr`, `full`, and `metavar`. For example, to attach an option to `-c` and `--config` use a `string: "-c FILE, --config=FILE"`
+
+#### help
+
+A string description of the option for the usage printout.
+
+#### default
+
+The value to give the option if it's not specified in the arguments.
+
+#### type
+
+If you don't want the option JSON-parsed, specify type `"string"`.
+
+#### callback
+
+A callback that will be executed as soon as the option is encountered. If the callback returns a string it will print the string and exit:
+
+```javascript
+nomnom.option('count', {
+ callback: function(count) {
+ if (count != parseInt(count)) {
+ return "count must be an integer";
+ }
+ }
+})
+```
+
+#### position
+
+The position of the option if it's a positional argument. If the option should be matched to the first positional arg use position `0`, etc.
+
+#### list
+
+Specifies that the option is a list. Appending can be achieved by specifying the arg more than once on the command line:
+
+ node test.js --file=test1.js --file=test2.js
+
+If the option has a `position` and `list` is `true`, all positional args including and after `position` will be appended to the array.
+
+#### required
+
+If this is set to `true` and the option isn't in the args, a message will be printed and the program will exit.
+
+#### choices
+
+A list of the possible values for the option (e.g. `['run', 'test', 'open']`). If the parsed value isn't in the list a message will be printed and the program will exit.
+
+#### transform
+
+A function that takes the value of the option as entered and returns a new value that will be seen as the value of the option.
+
+```javascript
+nomnom.option('date', {
+ abbr: 'd',
+ transform: function(timestamp) {
+ return new Date(timestamp);
+ }
+})
+```
+
+#### hidden
+
+Option won't be printed in the usage
+
+
+# Parser interface
+`require("nomnom")` will give you the option parser. You can also make an instance of a parser with `require("nomnom")()`. You can chain any of these functions off of a parser:
+
+#### option
+
+Add an option specification with the given name:
+
+```javascript
+nomnom.option('debug', {
+ abbr: 'd',
+ flag: true,
+ help: "Print debugging info"
+})
+```
+
+#### options
+
+Add options as a hash keyed by option name, good for a cli with tons of options like [this example](http://github.com/harthur/replace/blob/master/bin/replace.js):
+
+```javascript
+nomnom.options({
+ debug: {
+ abbr: 'd',
+ flag: true,
+ help: "Print debugging info"
+ },
+ fruit: {
+ help: "Fruit to buy"
+ }
+})
+```
+
+#### usage
+
+The string that will override the default generated usage message.
+
+#### help
+
+A string that is appended to the usage.
+
+#### script
+
+Nomnom can't detect the alias used to run your script. You can use `script` to provide the correct name for the usage printout instead of e.g. `node test.js`.
+
+#### printer
+
+Overrides the usage printing function.
+
+#### command
+
+Takes a command name and gives you a command object on which you can chain command options.
+
+#### nocommand
+
+Gives a command object that will be used when no command is called.
+
+#### nocolors
+
+Disables coloring of the usage message.
+
+#### parse
+
+Parses node's `process.argv` and returns the parsed options hash. You can also provide argv:
+
+```javascript
+var opts = nomnom.parse(["-xvf", "--atomic=true"])
+```
+
+#### nom
+
+The same as `parse()`.
+
+# Command interface
+A command is specified with `nomnom.command('name')`. All these functions can be chained on a command:
+
+#### option
+
+Add an option specifically for this command.
+
+#### options
+
+Add options for this command as a hash of options keyed by name.
+
+#### callback
+
+A callback that will be called with the parsed options when the command is used.
+
+#### help
+
+A help string describing the function of this command.
+
+#### usage
+
+Override the default generated usage string for this command.
diff --git a/nomnom.js b/nomnom.js
new file mode 100644
index 0000000..3155974
--- /dev/null
+++ b/nomnom.js
@@ -0,0 +1,584 @@
+var _ = require("underscore"), chalk = require('chalk');
+
+
+function ArgParser() {
+ this.commands = {}; // expected commands
+ this.specs = {}; // option specifications
+}
+
+ArgParser.prototype = {
+ /* Add a command to the expected commands */
+ command : function(name) {
+ var command;
+ if (name) {
+ command = this.commands[name] = {
+ name: name,
+ specs: {}
+ };
+ }
+ else {
+ command = this.fallback = {
+ specs: {}
+ };
+ }
+
+ // facilitates command('name').options().cb().help()
+ var chain = {
+ options : function(specs) {
+ command.specs = specs;
+ return chain;
+ },
+ opts : function(specs) {
+ // old API
+ return this.options(specs);
+ },
+ option : function(name, spec) {
+ command.specs[name] = spec;
+ return chain;
+ },
+ callback : function(cb) {
+ command.cb = cb;
+ return chain;
+ },
+ help : function(help) {
+ command.help = help;
+ return chain;
+ },
+ usage : function(usage) {
+ command._usage = usage;
+ return chain;
+ }
+ };
+ return chain;
+ },
+
+ nocommand : function() {
+ return this.command();
+ },
+
+ options : function(specs) {
+ this.specs = specs;
+ return this;
+ },
+
+ opts : function(specs) {
+ // old API
+ return this.options(specs);
+ },
+
+ globalOpts : function(specs) {
+ // old API
+ return this.options(specs);
+ },
+
+ option : function(name, spec) {
+ this.specs[name] = spec;
+ return this;
+ },
+
+ usage : function(usage) {
+ this._usage = usage;
+ return this;
+ },
+
+ printer : function(print) {
+ this.print = print;
+ return this;
+ },
+
+ script : function(script) {
+ this._script = script;
+ return this;
+ },
+
+ scriptName : function(script) {
+ // old API
+ return this.script(script);
+ },
+
+ help : function(help) {
+ this._help = help;
+ return this;
+ },
+
+ colors: function() {
+ // deprecated - colors are on by default now
+ return this;
+ },
+
+ nocolors : function() {
+ this._nocolors = true;
+ return this;
+ },
+
+ parseArgs : function(argv) {
+ // old API
+ return this.parse(argv);
+ },
+
+ nom : function(argv) {
+ return this.parse(argv);
+ },
+
+ parse : function(argv) {
+ this.print = this.print || function(str, code) {
+ console.log(str);
+ process.exit(code || 0);
+ };
+ this._help = this._help || "";
+ this._script = this._script || process.argv[0] + " "
+ + require('path').basename(process.argv[1]);
+ this.specs = this.specs || {};
+
+ var argv = argv || process.argv.slice(2);
+
+ var arg = Arg(argv[0]).isValue && argv[0],
+ command = arg && this.commands[arg],
+ commandExpected = !_(this.commands).isEmpty();
+
+ if (commandExpected) {
+ if (command) {
+ _(this.specs).extend(command.specs);
+ this._script += " " + command.name;
+ if (command.help) {
+ this._help = command.help;
+ }
+ this.command = command;
+ }
+ else if (arg) {
+ return this.print(this._script + ": no such command '" + arg + "'", 1);
+ }
+ else {
+ // no command but command expected e.g. 'git -v'
+ var helpStringBuilder = {
+ list : function() {
+ return 'one of: ' + _(this.commands).keys().join(", ");
+ },
+ twoColumn : function() {
+ // find the longest command name to ensure horizontal alignment
+ var maxLength = _(this.commands).max(function (cmd) {
+ return cmd.name.length;
+ }).name.length;
+
+ // create the two column text strings
+ var cmdHelp = _.map(this.commands, function(cmd, name) {
+ var diff = maxLength - name.length;
+ var pad = new Array(diff + 4).join(" ");
+ return " " + [ name, pad, cmd.help ].join(" ");
+ });
+ return "\n" + cmdHelp.join("\n");
+ }
+ };
+
+ // if there are a small number of commands and all have help strings,
+ // display them in a two column table; otherwise use the brief version.
+ // The arbitrary choice of "20" comes from the number commands git
+ // displays as "common commands"
+ var helpType = 'list';
+ if (_(this.commands).size() <= 20) {
+ if (_(this.commands).every(function (cmd) { return cmd.help; })) {
+ helpType = 'twoColumn';
+ }
+ }
+
+ this.specs.command = {
+ position: 0,
+ help: helpStringBuilder[helpType].call(this)
+ }
+
+ if (this.fallback) {
+ _(this.specs).extend(this.fallback.specs);
+ this._help = this.fallback.help;
+ } else {
+ this.specs.command.required = true;
+ }
+ }
+ }
+
+ if (this.specs.length === undefined) {
+ // specs is a hash not an array
+ this.specs = _(this.specs).map(function(opt, name) {
+ opt.name = name;
+ return opt;
+ });
+ }
+ this.specs = this.specs.map(function(opt) {
+ return Opt(opt);
+ });
+
+ if (argv.indexOf("--help") >= 0 || argv.indexOf("-h") >= 0) {
+ return this.print(this.getUsage());
+ }
+
+ var options = {};
+ var args = argv.map(function(arg) {
+ return Arg(arg);
+ })
+ .concat(Arg());
+
+ var positionals = [];
+
+ /* parse the args */
+ var that = this;
+ args.reduce(function(arg, val) {
+ /* positional */
+ if (arg.isValue) {
+ positionals.push(arg.value);
+ }
+ else if (arg.chars) {
+ var last = arg.chars.pop();
+
+ /* -cfv */
+ (arg.chars).forEach(function(ch) {
+ that.setOption(options, ch, true);
+ });
+
+ /* -v key */
+ if (!that.opt(last).flag) {
+ if (val.isValue) {
+ that.setOption(options, last, val.value);
+ return Arg(); // skip next turn - swallow arg
+ }
+ else {
+ that.print("'-" + (that.opt(last).name || last) + "'"
+ + " expects a value\n\n" + that.getUsage(), 1);
+ }
+ }
+ else {
+ /* -v */
+ that.setOption(options, last, true);
+ }
+
+ }
+ else if (arg.full) {
+ var value = arg.value;
+
+ /* --key */
+ if (value === undefined) {
+ /* --key value */
+ if (!that.opt(arg.full).flag) {
+ if (val.isValue) {
+ that.setOption(options, arg.full, val.value);
+ return Arg();
+ }
+ else {
+ that.print("'--" + (that.opt(arg.full).name || arg.full) + "'"
+ + " expects a value\n\n" + that.getUsage(), 1);
+ }
+ }
+ else {
+ /* --flag */
+ value = true;
+ }
+ }
+ that.setOption(options, arg.full, value);
+ }
+ return val;
+ });
+
+ positionals.forEach(function(pos, index) {
+ this.setOption(options, index, pos);
+ }, this);
+
+ options._ = positionals;
+
+ this.specs.forEach(function(opt) {
+ if (opt.default !== undefined && options[opt.name] === undefined) {
+ options[opt.name] = opt.default;
+ }
+ }, this);
+
+ // exit if required arg isn't present
+ this.specs.forEach(function(opt) {
+ if (opt.required && options[opt.name] === undefined) {
+ var msg = opt.name + " argument is required";
+ msg = this._nocolors ? msg : chalk.red(msg);
+
+ this.print("\n" + msg + "\n" + this.getUsage(), 1);
+ }
+ }, this);
+
+ if (command && command.cb) {
+ command.cb(options);
+ }
+ else if (this.fallback && this.fallback.cb) {
+ this.fallback.cb(options);
+ }
+
+ return options;
+ },
+
+ getUsage : function() {
+ if (this.command && this.command._usage) {
+ return this.command._usage;
+ }
+ else if (this.fallback && this.fallback._usage) {
+ return this.fallback._usage;
+ }
+ if (this._usage) {
+ return this._usage;
+ }
+
+ // todo: use a template
+ var str = "\n"
+ if (!this._nocolors) {
+ str += chalk.bold("Usage:");
+ }
+ else {
+ str += "Usage:";
+ }
+ str += " " + this._script;
+
+ var positionals = _(this.specs).select(function(opt) {
+ return opt.position != undefined;
+ })
+ positionals = _(positionals).sortBy(function(opt) {
+ return opt.position;
+ });
+ var options = _(this.specs).select(function(opt) {
+ return opt.position === undefined;
+ });
+
+ // assume there are no gaps in the specified pos. args
+ positionals.forEach(function(pos) {
+ str += " ";
+ var posStr = pos.string;
+ if (!posStr) {
+ posStr = pos.name || "arg" + pos.position;
+ if (pos.required) {
+ posStr = "<" + posStr + ">";
+ } else {
+ posStr = "[" + posStr + "]";
+ }
+ if (pos.list) {
+ posStr += "...";
+ }
+ }
+ str += posStr;
+ });
+
+ if (options.length) {
+ if (!this._nocolors) {
+ // must be a better way to do this
+ str += chalk.blue(" [options]");
+ }
+ else {
+ str += " [options]";
+ }
+ }
+
+ if (options.length || positionals.length) {
+ str += "\n\n";
+ }
+
+ function spaces(length) {
+ var spaces = "";
+ for (var i = 0; i < length; i++) {
+ spaces += " ";
+ }
+ return spaces;
+ }
+ var longest = positionals.reduce(function(max, pos) {
+ return pos.name.length > max ? pos.name.length : max;
+ }, 0);
+
+ positionals.forEach(function(pos) {
+ var posStr = pos.string || pos.name;
+ str += posStr + spaces(longest - posStr.length) + " ";
+ if (!this._nocolors) {
+ str += chalk.grey(pos.help || "")
+ }
+ else {
+ str += (pos.help || "")
+ }
+ str += "\n";
+ }, this);
+ if (positionals.length && options.length) {
+ str += "\n";
+ }
+
+ if (options.length) {
+ if (!this._nocolors) {
+ str += chalk.blue("Options:");
+ }
+ else {
+ str += "Options:";
+ }
+ str += "\n"
+
+ var longest = options.reduce(function(max, opt) {
+ return opt.string.length > max && !opt.hidden ? opt.string.length : max;
+ }, 0);
+
+ options.forEach(function(opt) {
+ if (!opt.hidden) {
+ str += " " + opt.string + spaces(longest - opt.string.length) + " ";
+
+ var defaults = (opt.default != null ? " [" + opt.default + "]" : "");
+ var help = opt.help ? opt.help + defaults : "";
+ str += this._nocolors ? help: chalk.grey(help);
+
+ str += "\n";
+ }
+ }, this);
+ }
+
+ if (this._help) {
+ str += "\n" + this._help;
+ }
+ return str;
+ }
+};
+
+ArgParser.prototype.opt = function(arg) {
+ // get the specified opt for this parsed arg
+ var match = Opt({});
+ this.specs.forEach(function(opt) {
+ if (opt.matches(arg)) {
+ match = opt;
+ }
+ });
+ return match;
+};
+
+ArgParser.prototype.setOption = function(options, arg, value) {
+ var option = this.opt(arg);
+ if (option.callback) {
+ var message = option.callback(value);
+
+ if (typeof message == "string") {
+ this.print(message, 1);
+ }
+ }
+
+ if (option.type != "string") {
+ try {
+ // infer type by JSON parsing the string
+ value = JSON.parse(value)
+ }
+ catch(e) {}
+ }
+
+ if (option.transform) {
+ value = option.transform(value);
+ }
+
+ var name = option.name || arg;
+ if (option.choices && option.choices.indexOf(value) == -1) {
+ this.print(name + " must be one of: " + option.choices.join(", "), 1);
+ }
+
+ if (option.list) {
+ if (!options[name]) {
+ options[name] = [value];
+ }
+ else {
+ options[name].push(value);
+ }
+ }
+ else {
+ options[name] = value;
+ }
+};
+
+
+/* an arg is an item that's actually parsed from the command line
+ e.g. "-l", "log.txt", or "--logfile=log.txt" */
+var Arg = function(str) {
+ var abbrRegex = /^\-(\w+?)$/,
+ fullRegex = /^\-\-(no\-)?(.+?)(?:=(.+))?$/,
+ valRegex = /^[^\-].*/;
+
+ var charMatch = abbrRegex.exec(str),
+ chars = charMatch && charMatch[1].split("");
+
+ var fullMatch = fullRegex.exec(str),
+ full = fullMatch && fullMatch[2];
+
+ var isValue = str !== undefined && (str === "" || valRegex.test(str));
+ var value;
+ if (isValue) {
+ value = str;
+ }
+ else if (full) {
+ value = fullMatch[1] ? false : fullMatch[3];
+ }
+
+ return {
+ str: str,
+ chars: chars,
+ full: full,
+ value: value,
+ isValue: isValue
+ }
+}
+
+
+/* an opt is what's specified by the user in opts hash */
+var Opt = function(opt) {
+ var strings = (opt.string || "").split(","),
+ abbr, full, metavar;
+ for (var i = 0; i < strings.length; i++) {
+ var string = strings[i].trim(),
+ matches;
+ if (matches = string.match(/^\-([^-])(?:\s+(.*))?$/)) {
+ abbr = matches[1];
+ metavar = matches[2];
+ }
+ else if (matches = string.match(/^\-\-(.+?)(?:[=\s]+(.+))?$/)) {
+ full = matches[1];
+ metavar = metavar || matches[2];
+ }
+ }
+
+ matches = matches || [];
+ var abbr = opt.abbr || abbr, // e.g. v from -v
+ full = opt.full || full, // e.g. verbose from --verbose
+ metavar = opt.metavar || metavar; // e.g. PATH from '--config=PATH'
+
+ var string;
+ if (opt.string) {
+ string = opt.string;
+ }
+ else if (opt.position === undefined) {
+ string = "";
+ if (abbr) {
+ string += "-" + abbr;
+ if (metavar)
+ string += " " + metavar
+ string += ", ";
+ }
+ string += "--" + (full || opt.name);
+ if (metavar) {
+ string += " " + metavar;
+ }
+ }
+
+ opt = _(opt).extend({
+ name: opt.name || full || abbr,
+ string: string,
+ abbr: abbr,
+ full: full,
+ metavar: metavar,
+ matches: function(arg) {
+ return opt.full == arg || opt.abbr == arg || opt.position == arg
+ || opt.name == arg || (opt.list && arg >= opt.position);
+ }
+ });
+ return opt;
+}
+
+
+var createParser = function() {
+ return new ArgParser();
+}
+
+var nomnom = createParser();
+
+for (var i in nomnom) {
+ if (typeof nomnom[i] == "function") {
+ createParser[i] = _(nomnom[i]).bind(nomnom);
+ }
+}
+
+module.exports = createParser;
diff --git a/num-vals-fix.diff b/num-vals-fix.diff
new file mode 100644
index 0000000..3c4f167
--- /dev/null
+++ b/num-vals-fix.diff
@@ -0,0 +1,31 @@
+diff --git a/test/values.js b/test/values.js
+index efad789..7fa1078 100644
+--- a/test/values.js
++++ b/test/values.js
+@@ -26,6 +26,12 @@ var opts = {
+ },
+ def2: {
+ default: "val1"
++ },
++ "2d": {
++ flag: true
++ },
++ "3d": {
++ abbr: "3"
+ }
+ }
+
+@@ -80,8 +86,12 @@ exports.testDash = function(test) {
+ };
+
+ exports.testNumbers = function(test) {
+- var options = parser.parseArgs(['sum', '-1', '2.5', '-3.5', '4']);
++ var options = parser.parseArgs(['sum', '-1', '2.5', '-3.5', '4', '--2d', '-3', 'test']);
+
+ test.deepEqual(options.list3, ['-1', '2.5', '-3.5', '4']);
++ test.strictEqual(options['2d'], true);
++ test.strictEqual(options['3d'], "test")
+ test.done();
+ };
++
++
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6556f58
--- /dev/null
+++ b/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "nomnom",
+ "description": "Option parser with generated usage and commands",
+ "version": "1.8.1",
+ "author": "Heather Arthur <fayearthur at gmail.com>",
+ "scripts": {
+ "test": "./node_modules/.bin/nodeunit test/*.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/harthur/nomnom.git"
+ },
+ "main": "./nomnom",
+ "keywords": [
+ "arguments",
+ "option parser",
+ "command line",
+ "options",
+ "parser"
+ ],
+ "dependencies": {
+ "underscore": "~1.6.0",
+ "chalk": "~0.4.0"
+ },
+ "devDependencies": {
+ "nodeunit": "~0.7.4"
+ }
+}
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..db99a1e
--- /dev/null
+++ b/test.js
@@ -0,0 +1,23 @@
+var opts = require("./nomnom")
+ .option('debug', {
+ abbr: 'd',
+ flag: true,
+ help: 'Print debugging info'
+ })
+ .option('config', {
+ abbr: 'c',
+ default: 'config.json',
+ help: 'JSON file with tests to run'
+ })
+ .option('version', {
+ flag: true,
+ help: 'print version and exit',
+ callback: function() {
+ return "version 1.2.4";
+ }
+ })
+ .parse();
+
+if (opts.debug) {
+ console.log("debug")
+}
diff --git a/test/callback.js b/test/callback.js
new file mode 100644
index 0000000..3d512db
--- /dev/null
+++ b/test/callback.js
@@ -0,0 +1,33 @@
+var nomnom = require("../nomnom");
+
+exports.testVersion = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ date: {
+ callback: function(date) {
+ test.equal(date, "2010-02-03", "date should match value")
+ }
+ }
+ }).parse(["--date=2010-02-03"]);
+
+ test.done();
+}
+
+exports.testReturnString = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ version: {
+ flag: true,
+ callback: function() {
+ return "v0.3";
+ }
+ }
+ })
+ .printer(function(string) {
+ test.equal(0, string.indexOf("v0.3"))
+ test.done();
+ })
+ .parse(["--version"]);
+}
\ No newline at end of file
diff --git a/test/commands.js b/test/commands.js
new file mode 100644
index 0000000..1fbb60f
--- /dev/null
+++ b/test/commands.js
@@ -0,0 +1,120 @@
+var nomnom = require("../nomnom");
+
+function strip(str) {
+ return str.replace(/\s+/g, '');
+}
+
+exports.testCallback = function(test) {
+ test.expect(1);
+
+ var parser = nomnom();
+ parser.command('run').callback(function(options) {
+ test.equal(options.v, 3);
+ });
+ parser.command('other').callback(function() {
+ test.ok(false, "callback for other command shouldn't be called");
+ });
+
+ parser.parse(["run","-v", "3"]);
+ test.done();
+}
+
+exports.testMissingCommand = function(test) {
+ test.expect(1);
+
+ var parser = nomnom().scriptName("test");
+
+ parser.command('run');
+
+ parser.printer(function(string) {
+ test.equal(string, "test: no such command 'other'");
+ test.done();
+ });
+
+ parser.parse(["other"]);
+}
+
+exports.testNoCommand = function(test) {
+ test.expect(2);
+
+ var parser = nomnom();
+
+ parser.nocommand()
+ .options({
+ version: {
+ flag: true
+ }
+ })
+ .callback(function(options) {
+ test.strictEqual(options.version, true);
+ })
+ .usage("fallback usage");
+
+ parser.command('run');
+
+ var options = parser.parse(["--version"]);
+
+ test.strictEqual(options.version, true);
+ test.done();
+}
+
+function createParser() {
+ var parser = nomnom().scriptName("test")
+ .options({
+ debug: {
+ flag: true
+ }
+ });
+
+ parser.command('run')
+ .options({
+ file: {
+ help: 'file to run'
+ }
+ })
+ .help("run all");
+
+ parser.command('test').usage("test usage");
+
+ parser.nocommand()
+ .options({
+ verbose: {
+ flag: true
+ }
+ })
+ .help("nocommand");
+
+ return parser;
+}
+
+exports.testUsage = function(test) {
+ test.expect(4);
+
+ var parser = createParser();
+ parser.printer(function(string) {
+ test.equal(strip(string), "testusage");
+ });
+ parser.parse(["test", "-h"]);
+
+ parser = createParser().nocolors();
+ parser.printer(function(string) {
+ test.equal(strip(string), "Usage:testrun[options]Options:--debug--filefiletorunrunall");
+ });
+ parser.parse(["run", "-h"]);
+
+ parser = createParser().nocolors();
+ parser.printer(function(string) {
+ test.equal(strip(string), "Usage:test[command][options]commandoneof:run,testOptions:--debug--verbosenocommand");
+ });
+ parser.parse(["-h"]);
+
+ parser = createParser().nocolors();
+ parser.nocommand()
+ .usage("fallback");
+ parser.printer(function(string) {
+ test.equal(strip(string), "fallback");
+ });
+ parser.parse(["-h"]);
+
+ test.done();
+}
diff --git a/test/expected.js b/test/expected.js
new file mode 100644
index 0000000..f52bd57
--- /dev/null
+++ b/test/expected.js
@@ -0,0 +1,60 @@
+var nomnom = require("../nomnom");
+
+var opts = {
+ file: {
+ position: 0,
+ required: true
+ }
+}
+
+var parser = nomnom().options(opts);
+
+exports.testFlag = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ file: {
+ position: 0,
+ }
+ })
+ .printer(function(string) {
+ test.equal(0, string.indexOf("'--key1' expects a value"))
+ test.done();
+ })
+ .parse(["--key1"]);
+}
+
+exports.testRequired = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ file: {
+ required: true
+ }
+ })
+ .printer(function(string) {
+ test.equal(0, string.trim().indexOf("file argument is required"))
+ test.done();
+ })
+ .nocolors()
+ .parse([]);
+}
+
+exports.testChoices = function(test) {
+ test.expect(2);
+
+ var parser = nomnom().options({
+ color: {
+ choices: ['green', 'blue']
+ }
+ })
+ .printer(function(string) {
+ test.equal(0, string.indexOf("color must be one of: green, blue"))
+ });
+
+ parser.parse(['--color', 'red']);
+
+ var options = parser.parse(['--color', 'green']);
+ test.equal(options.color, 'green');
+ test.done();
+}
diff --git a/test/matching.js b/test/matching.js
new file mode 100644
index 0000000..8d347eb
--- /dev/null
+++ b/test/matching.js
@@ -0,0 +1,70 @@
+var nomnom = require("../nomnom");
+
+var opts = {
+ pos1: {
+ position: 0
+ },
+ pos2: {
+ position: 1
+ },
+ flag1: {
+ flag: true
+ },
+ debug: {
+ abbr: 'd'
+ },
+ numLines: {
+ abbr: 'n',
+ full: 'num-lines'
+ },
+ version: {
+ string: '-v, --version'
+ },
+ config: {
+ string: '-c FILE, --config=FILE'
+ },
+ skey : {
+ string: '-k val'
+ },
+ skey2: {
+ string: '-k2 val2, --key2 val2'
+ },
+ skey3: {
+ string: '--key3=val'
+ },
+ skey4: {
+ string: '--key4=val, -y val'
+ }
+}
+
+var parser = nomnom().options(opts);
+
+exports.testPositional = function(test) {
+ var options = parser.parse(["--flag1", "val1", "--config", "file", "val2"]);
+
+ test.equal(options.pos1, "val1");
+ test.equal(options.pos2, "val2");
+ test.deepEqual(options._, ["val1", "val2"])
+ test.done();
+}
+
+exports.testAbbr = function(test) {
+ var options = parser.parse(["-d", "yes", "--num-lines", "3"]);
+
+ test.equal(options.debug, "yes")
+ test.equal(options.numLines, 3)
+ test.done();
+}
+
+exports.testString = function(test) {
+ var options = parser.parse(["-k", "val", "--config=test.js",
+ "--key2", "val2", "--key3", "val3", "--key4=val4", "-v", "v0.3"]);
+
+ test.equal(options.version, "v0.3")
+ test.equal(options.config, "test.js")
+ test.equal(options.skey, "val")
+ test.equal(options.skey2, "val2")
+ test.equal(options.skey3, "val3")
+ test.equal(options.skey4, "val4")
+ test.done();
+}
diff --git a/test/option.js b/test/option.js
new file mode 100644
index 0000000..e3934d7
--- /dev/null
+++ b/test/option.js
@@ -0,0 +1,44 @@
+var nomnom = require("../nomnom");
+
+var parser = nomnom()
+ .option('debug', {
+ abbr: 'x',
+ flag: true,
+ help: 'Print debugging info'
+ })
+ .option('config', {
+ abbr: 'c',
+ default: 'config.json',
+ help: 'JSON file with tests to run'
+ })
+ .option('version', {
+ flag: true,
+ help: 'print version and exit',
+ callback: function() {
+ return "version 1.2.4";
+ }
+ });
+
+
+exports.testOption = function(test) {
+ var opts = parser.parse(["-x", "--no-verbose"]);
+
+ test.strictEqual(opts.debug, true);
+ test.equal(opts.config, "config.json");
+ test.done();
+}
+
+
+exports.testCommandOption = function(test) {
+ var parser = nomnom()
+ parser.command('test')
+ .option('fruit', {
+ abbr: 'f',
+ flag: true
+ })
+
+ var opts = parser.parse(["test", "-f"]);
+
+ test.strictEqual(opts.fruit, true);
+ test.done();
+}
diff --git a/test/transform.js b/test/transform.js
new file mode 100644
index 0000000..666a6a2
--- /dev/null
+++ b/test/transform.js
@@ -0,0 +1,65 @@
+var nomnom = require("../nomnom");
+
+var parser = nomnom()
+ .option("addr", {
+ abbr: "a",
+ help: "host:port address",
+ transform: function(value) {
+ var parts = value.split(":");
+ return {host: parts[0], port: Number(parts[1])};
+ }
+ })
+ .option("string", {
+ abbr: "s",
+ help: "always a string",
+ transform: function(value) {
+ return value.toString();
+ }
+ });
+
+
+exports.testTransformComplexValue = function(test) {
+ var opts = parser.parse(["-a", "localhost:1234"]);
+
+ test.strictEqual(opts.addr.host, "localhost");
+ test.strictEqual(opts.addr.port, 1234);
+ test.done();
+};
+
+
+exports.testTransformString = function(test) {
+ var opts = parser.parse(["-s", "3"]);
+
+ test.strictEqual(opts.string, "3");
+ test.done();
+};
+
+
+exports.testTransformCommand = function(test) {
+ test.expect(1);
+
+ var parser = nomnom().scriptName("test")
+ .options({
+ addr: {
+ transform: function(value) {
+ var parts = value.split(":");
+ return {host: parts[0], port: Number(parts[1])};
+ }
+ }
+ });
+
+ parser.command("run")
+ .options({
+ string: {
+ transform: function(value) {
+ return value.toString();
+ }
+ }
+ })
+ .callback(function(options) {
+ test.strictEqual(options.string, "true");
+ });
+
+ parser.parse(["run", "--string=true"]);
+ test.done();
+};
diff --git a/test/usage.js b/test/usage.js
new file mode 100644
index 0000000..9a08179
--- /dev/null
+++ b/test/usage.js
@@ -0,0 +1,121 @@
+var nomnom = require("../nomnom");
+
+function strip(str) {
+ return str.replace(/\s+/g, '');
+};
+
+var opts = {
+ apple: {
+ abbr: 'a',
+ help: 'how many apples'
+ },
+
+ banana: {
+ full: "b-nana"
+ },
+
+ carrot: {
+ string: '-c NUM, --carrots=NUM'
+ },
+
+ dill: {
+ metavar: 'PICKLE'
+ },
+
+ egg: {
+ position: 0,
+ help: 'robin'
+ }
+}
+
+var parser = nomnom().options(opts).help("all the best foods").scriptName("test").nocolors();
+
+var expected = "Usage:test[egg][options]eggrobinOptions:-a,--applehowmanyapples--b-nana-cNUM,--carrots=NUM--dillPICKLEallthebestfoods"
+
+exports.testH = function(test) {
+ test.expect(1);
+
+ parser.printer(function(string) {
+ test.equal(strip(string), expected)
+ test.done();
+ })
+ .nocolors()
+ .parse(["-h"]);
+}
+
+exports.testHelp = function(test) {
+ test.expect(1);
+
+ parser.printer(function(string) {
+ test.equal(strip(string), expected)
+ test.done();
+ })
+ .nocolors()
+ .parse(["--help"]);
+}
+
+exports.testScriptName = function(test) {
+ test.expect(1);
+
+ nomnom()
+ .script("test")
+ .printer(function(string) {
+ test.equal(strip(string),"Usage:test")
+ test.done();
+ })
+ .nocolors()
+ .parse(["-h"]);
+}
+
+exports.testUsage = function(test) {
+ test.expect(1);
+
+ parser
+ .usage("test usage")
+ .printer(function(string) {
+ test.equal(string, "test usage")
+ test.done();
+ })
+ .nocolors()
+ .parse(["--help"]);
+}
+
+exports.testHidden = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ file: {
+ hidden: true
+ }
+ })
+ .scriptName("test")
+ .printer(function(string) {
+ test.equal(strip("Usage:test[options]Options:"), strip(string))
+ test.done();
+ })
+ .nocolors()
+ .parse(["-h"]);
+}
+
+exports.testRequiredOptional = function(test) {
+ test.expect(1);
+
+ nomnom().options({
+ foo: {
+ position: 0,
+ required: true,
+ help: 'The foo'
+ },
+ bar: {
+ position: 1,
+ help: 'The bar'
+ }
+ })
+ .scriptName("test")
+ .printer(function(string) {
+ test.equal(strip("Usage:test<foo>[bar]fooThefoobarThebar"), strip(string))
+ test.done();
+ })
+ .nocolors()
+ .parse(["-h"]);
+}
diff --git a/test/values.js b/test/values.js
new file mode 100644
index 0000000..797807e
--- /dev/null
+++ b/test/values.js
@@ -0,0 +1,75 @@
+var nomnom = require("../nomnom");
+
+var opts = {
+ debug: {
+ flag: true
+ },
+ verbose: {
+ flag: true,
+ default: true
+ },
+ list1: {
+ list: true
+ },
+ list2: {
+ list: true
+ },
+ list3: {
+ position: 1,
+ list: true
+ },
+ num1: {
+ type: "string"
+ },
+ def1: {
+ default: "val1"
+ },
+ def2: {
+ default: "val1"
+ }
+}
+
+var parser = nomnom().options(opts);
+
+exports.testFlag = function(test) {
+ var options = parser.parse(["--debug", "pos0", "--no-verbose"]);
+
+ test.strictEqual(options.debug, true);
+ test.strictEqual(options.verbose, false);
+ test.equal(options[0], "pos0");
+ test.equal(options._[0], "pos0");
+ test.done();
+}
+
+exports.testList = function(test) {
+ var options = parser.parse(["pos0", "pos1", "--list1=val0", "--list2", "val1",
+ "--list2", "val2", "pos2"]);
+
+ test.deepEqual(options.list1, ["val0"]);
+ test.deepEqual(options.list2, ["val1", "val2"]);
+ test.deepEqual(options.list3, ["pos1", "pos2"]);
+ test.done();
+}
+
+exports.testDefault = function(test) {
+ var options = parser.parse(["--def2", "val2", "--def3", "val3"]);
+
+ test.strictEqual(options.def1, "val1");
+ test.strictEqual(options.def2, "val2");
+ test.strictEqual(options.def3, "val3");
+ test.done();
+}
+
+exports.testTypes = function(test) {
+ var options = parser.parseArgs(["", "-x", "3.14", "-w", "true", "-q", "120",
+ "--num1", "4"]);
+
+ test.strictEqual(options[0], "");
+ test.strictEqual(options.x, 3.14);
+ test.strictEqual(options.w, true);
+ test.strictEqual(options.q, 120);
+ test.strictEqual(options.num1, "4");
+ test.done();
+}
+
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-nomnom.git
More information about the Pkg-javascript-commits
mailing list