├── README.md
├── TODO
├── examples
├── browser-test.html
└── nodejs-test.js
├── lib
└── optparse.js
├── package.json
└── seed.yml
/README.md:
--------------------------------------------------------------------------------
1 | optparse-js
2 | ===========
3 |
4 | Optparse-js is a command line option parser for Javascript. It's slightly based on Ruby's implementation optparse but with some differences (different languages has different needs) such as custom parsers.
5 |
6 | All examples in this readme is using [Node.js](http://nodejs.org/). How ever, the library works with all kinds of Javascript implementations.
7 |
8 |
9 | QUICK START
10 | -----------
11 |
12 | The library defines one class, the OptionParser class. The class constructor takes one single argument, a list with a set of rules. Here is a quick example:
13 |
14 | // Import the sys library
15 | var sys = require('sys');
16 |
17 | // Import the optparse library.
18 | var optparse = require('optparse');
19 |
20 | // Define an option called `help`. We give it a quick alias named `-h`
21 | // and a quick help text.
22 | var switches = [
23 | ['-h', '--help', 'Shows help sections']
24 | ];
25 |
26 | // Create a new OptionParser.
27 | var parser = new optparse.OptionParser(switches);
28 |
29 | // Hook the help option. The callback will be executed when the OptionParser
30 | // hits the switch `-h` or `--help`.
31 | parser.on('help', function() {
32 | console.log('Help');
33 | });
34 |
35 | parser.parse(process.argv);
36 |
37 |
38 | DEFINING RULES
39 | --------------
40 | The OptionParser constructor takes an Array with rules. Each rule is represented by an array (tuple) of two or three values. A typical rule definition may look like this:
41 |
42 | ['-h', '--help', 'Print this help']
43 |
44 |
45 | The first value is optional, and represents an alias for the long-named switch (the second value, in this case `--help`).
46 |
47 | The second argument is the actual rule. The rule must start with a double dash followed by a switch name (in this case ´help´). The OptionParser also supports special option arguments. Define an option argument in the rule by adding a named argument after the leading double dash and switch name (E.G `--port-number PORT_NUMBER`). The argument is then parsed to the option handler. To define an optional option argument, just add a braces around argument in the rule (E.G `--port-number [PORT_NUMBER]`). The OptionParser also supports filter. More on that in in the section called ´Option Filters´.
48 |
49 | The third argument is an optional rule description.
50 |
51 |
52 | OPTION FILTERS
53 | --------------
54 | Filters is a neat feature that let you filter option arguments. The OptionParser itself as already a set of built-in common filter's. These are:
55 |
56 | - NUMBER, supports both decimal and hexadecimal numbers.
57 | - DATE, filters arguments that matches YYYY-MM-DD.
58 | - EMAIL, filters arguments that matches my@email.com.
59 |
60 | It's simple to use any of the filter above in your rule-set. Here is a quick example how to filter number:
61 |
62 | var rules = [
63 | ['--first-option NUMBER', 'Takes a number as argument'],
64 | ['--second-option [NUMBER]', 'Takes an optional number as argument']
65 | ]
66 |
67 | You can add your own set of filter by calling the *parser_instance.filter* method:
68 |
69 | parser.filter('single_char', function(value) {
70 | if(value.length != 1) throw "Filter mismatch.";
71 | return value;
72 | });
73 |
74 |
75 | OPTION PARSER
76 | -------------
77 | The OptionParser class has the following properties and methods:
78 |
79 | ### string banner
80 | An optional usage banner. This text is included when calling `toString`. Default value is: "Usage: [Options]".
81 |
82 |
83 | ### string options_title
84 | An optional title for the options list. This text is included when calling `toString`. Default value is: "Available options:".
85 |
86 |
87 | ### function on(switch_or_arg_index, callback)
88 | Add's a callback for a switch or an argument (defined by index). Switch hooks MUST be typed witout the leading `--`. This example show how to hook a switch:
89 |
90 | parser.on('help', function(optional_argument) {
91 | // Show help section
92 | });
93 |
94 | And this example show how to hook a positional argument (an option without the leading - or --).
95 | Note that positional argument 0 will be "node" and positional argument 1 will be the path of the
96 | script being run. Positional argument 2 will be the first positional argument after the script path:
97 |
98 | parser.on(2, function(opt) {
99 | console.log('The first non-switch option is:' + opt);
100 | });
101 |
102 | It's also possible to define a default handler. The default handler is called when no rule's are meet. Here is an example how to add a ´default handler´:
103 |
104 | parser.on(function(opt) {
105 | console.log('No handler was defined for option:' + opt);
106 | });
107 |
108 | Use the wildcard handler to build a custom `on` handler.
109 |
110 | parser.on('*', function(opt, value) {
111 | console.log('option=' + opt + ', value=' + value);
112 | });
113 |
114 | ### function filter(name, callback)
115 | Adds a new filter extension to the OptionParser instance. The first argument is the name of the filter (trigger). The second argument is the actual filter See the ´OPTION FILTERS´ section for more info.
116 |
117 | It's possible to override the default filters by passing the value "_DEFAULT" to the `name` argument. The name of the filter is automatically transformed into
118 | upper case.
119 |
120 |
121 | ### function halt([callback])
122 | Interrupt's further parsing. This function should be called from an `on` -callbacks, to cancel the parsing. This can be useful when the program should ignore all other arguments (when displaying help or version information).
123 |
124 | The function also takes an optional callback argument. If the callback argument is specified, a `halt` callback will be added (instead of executing the `halt` command).
125 |
126 | Here is an example how to add an `on_halt` callback:
127 |
128 | parser.halt(function() {
129 | console.log('An option callback interupted the parser');
130 | });
131 |
132 |
133 | ### function parse(arguments)
134 | Start's parsing of arguments. This should be the last thing you do.
135 |
136 |
137 | ### function options()
138 | Returns an Array with all defined option rules
139 |
140 |
141 | ### function toString()
142 | Returns a string representation of this OptionParser instance (a formatted help section).
143 |
144 |
145 | MORE EXAMPLES
146 | -------------
147 | See examples/nodejs-test.js and examples/browser-test-html for more info how to
148 | use the script.
149 |
150 |
151 | SUGGESTIONS
152 | -----------
153 | All comments in how to improve this library is very welcome. Feel free post suggestions to the [Issue tracker](http://github.com/jfd/optparse-js/issues), or even better, fork the repository to implement your own features.
154 |
155 |
156 | LICENSE
157 | -------
158 | Released under a MIT-style license.
159 |
160 |
161 | COPYRIGHT
162 | ---------
163 | Copyright (c) 2009 Johan Dahlberg
164 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | - Support for Argument lists (for switches)
--------------------------------------------------------------------------------
/examples/browser-test.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | optparse.js example
7 |
8 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/examples/nodejs-test.js:
--------------------------------------------------------------------------------
1 | // Import the optparse script
2 | var optparse = require('../lib/optparse');
3 |
4 | // Define some options
5 | var SWITCHES = [
6 | ['-i', '--include-file FILE', "Includes a file"],
7 | ['-p', '--print [MESSAGE]', "Prints an optional message on screen"],
8 | ['-d', '--debug', "Enables debug mode"],
9 | ['-H', '--help', "Shows this help section"],
10 | ['--date DATE', "A date. A date is expected E.G. 2009-01-14"],
11 | ['--number NUMBER', "A Number. Supported formats are 123, 123.123, 0xA123"],
12 | ['--other NAME', "No handler defined for this option. Will be handled by the wildcard handler."],
13 | ];
14 |
15 | // Create a new OptionParser with defined switches
16 | var parser = new optparse.OptionParser(SWITCHES), print_summary = true,
17 | first_arg;
18 | parser.banner = 'Usage: nodejs-test.js [options]';
19 |
20 | // Internal variable to store options.
21 | var options = {
22 | debug: true,
23 | files: [],
24 | number: undefined,
25 | date: undefined
26 | };
27 |
28 | // Handle the first argument (switches excluded)
29 | parser.on(0, function(value) {
30 | first_arg = value;
31 | });
32 |
33 | // Handle the --include-file switch
34 | parser.on('include-file', function(name, value) {
35 | options.files.push(value);
36 | });
37 |
38 | // Handle the --print switch
39 | parser.on('print', function(name, value) {
40 | console.log('PRINT: ' + (value || 'No message entered'));
41 | });
42 |
43 | // Handle the --date switch
44 | parser.on('date', function(name, value) {
45 | options.date = value;
46 | });
47 |
48 | // Handle the --number switch
49 | parser.on('number', function(name, value) {
50 | options.number = value;
51 | });
52 |
53 | // Handle the --debug switch
54 | parser.on('debug', function() {
55 | options.debug = true;
56 | });
57 |
58 | // Handle the --help switch
59 | parser.on('help', function() {
60 | console.log(parser.toString());
61 | print_summary = false;
62 | });
63 |
64 | // Set a default handler
65 | parser.on('*', function(opt, value) {
66 | console.log('wild handler for ' + opt + ', value=' + value);
67 | });
68 |
69 | // Parse command line arguments
70 | parser.parse(process.ARGV);
71 |
72 | if(print_summary) {
73 | console.log("First non-switch argument is: " + first_arg);
74 |
75 | // Output all files that was included.
76 | console.log("No of files to include: " + options.files.length);
77 | for(var i = 0; i < options.files.length; i++) {
78 | console.log("File [" + (i + 1) + "]:" + options.files[i]);
79 | }
80 |
81 | // Is debug-mode enabled?
82 | console.log("Debug mode is set to: " + options.debug);
83 |
84 | console.log("Number value is: " + options.number);
85 | console.log("Date value is: " + options.date);
86 | }
87 |
--------------------------------------------------------------------------------
/lib/optparse.js:
--------------------------------------------------------------------------------
1 | // Optparse.js 1.0.3 - Option Parser for Javascript
2 | //
3 | // Copyright (c) 2009 Johan Dahlberg
4 | //
5 | // See README.md for license.
6 | //
7 | var optparse = {};
8 | try{ optparse = exports } catch(e) {}; // Try to export the lib for node.js
9 | (function(self) {
10 | var VERSION = '1.0.3';
11 | var LONG_SWITCH_RE = /^--\w/;
12 | var SHORT_SWITCH_RE = /^-\w/;
13 | var NUMBER_RE = /^(0x[A-Fa-f0-9]+)|([0-9]+\.[0-9]+)|(\d+)$/;
14 | var DATE_RE = /^\d{4}-(0[0-9]|1[0,1,2])-([0,1,2][0-9]|3[0,1])$/;
15 | var EMAIL_RE = /^([0-9a-zA-Z]+([_.-]?[0-9a-zA-Z]+)*@[0-9a-zA-Z]+[0-9,a-z,A-Z,.,-]*(.){1}[a-zA-Z]{2,4})+$/;
16 | var EXT_RULE_RE = /(\-\-[\w_-]+)\s+([\w\[\]_-]+)|(\-\-[\w_-]+)/;
17 | var ARG_OPTIONAL_RE = /\[(.+)\]/;
18 |
19 | // The default switch argument filter to use, when argument name doesnt match
20 | // any other names.
21 | var DEFAULT_FILTER = '_DEFAULT';
22 | var PREDEFINED_FILTERS = {};
23 |
24 | // The default switch argument filter. Parses the argument as text.
25 | function filter_text(value) {
26 | return value;
27 | }
28 |
29 | // Switch argument filter that expects an integer, HEX or a decimal value. An
30 | // exception is throwed if the criteria is not matched.
31 | // Valid input formats are: 0xFFFFFFF, 12345 and 1234.1234
32 | function filter_number(value) {
33 | var m = NUMBER_RE.exec(value);
34 | if(m == null) throw OptError('Expected a number representative');
35 | if(m[1]) {
36 | // The number is in HEX format. Convert into a number, then return it
37 | return parseInt(m[1], 16);
38 | } else {
39 | // The number is in regular- or decimal form. Just run in through
40 | // the float caster.
41 | return parseFloat(m[2] || m[3]);
42 | }
43 | };
44 |
45 | // Switch argument filter that expects a Date expression. The date string MUST be
46 | // formated as: "yyyy-mm-dd" An exception is throwed if the criteria is not
47 | // matched. An DATE object is returned on success.
48 | function filter_date(value) {
49 | var m = DATE_RE.exec(value);
50 | if(m == null) throw OptError('Expected a date representation in the "yyyy-mm-dd" format.');
51 | return new Date(parseInt(m[0]), parseInt(m[1]) - 1, parseInt(m[2]));
52 | };
53 |
54 | // Switch argument filter that expects an email address. An exception is throwed
55 | // if the criteria doesn`t match.
56 | function filter_email(value) {
57 | var m = EMAIL_RE.exec(value);
58 | if(m == null) throw OptError('Excpeted an email address.');
59 | return m[1];
60 | }
61 |
62 | // Register all predefined filters. This dict is used by each OptionParser
63 | // instance, when parsing arguments. Custom filters can be added to the parser
64 | // instance by calling the "add_filter" -method.
65 | PREDEFINED_FILTERS[DEFAULT_FILTER] = filter_text;
66 | PREDEFINED_FILTERS['TEXT'] = filter_text;
67 | PREDEFINED_FILTERS['NUMBER'] = filter_number;
68 | PREDEFINED_FILTERS['DATE'] = filter_date;
69 | PREDEFINED_FILTERS['EMAIL'] = filter_email;
70 |
71 | // Buildes rules from a switches collection. The switches collection is defined
72 | // when constructing a new OptionParser object.
73 | function build_rules(filters, arr) {
74 | var rules = [];
75 | for(var i=0; i> value means that the switch does
112 | // not take anargument.
113 | function build_rule(filters, short, expr, desc) {
114 | var optional, filter;
115 | var m = expr.match(EXT_RULE_RE);
116 | if(m == null) throw OptError('The switch is not well-formed.');
117 | var long = m[1] || m[3];
118 | if(m[2] != undefined) {
119 | // A switch argument is expected. Check if the argument is optional,
120 | // then find a filter that suites.
121 | var optional = ARG_OPTIONAL_RE.test(m[2]);
122 | var optional_match = m[2].match(ARG_OPTIONAL_RE);
123 | var filter_name = optional ? optional_match[1] : m[2];
124 | filter = filters[filter_name];
125 | if(filter === undefined) filter = filters[DEFAULT_FILTER];
126 | }
127 | return {
128 | name: long.substr(2),
129 | short: short,
130 | long: long,
131 | decl: expr,
132 | desc: desc,
133 | optional_arg: optional,
134 | filter: filter
135 | }
136 | }
137 |
138 | // Loop's trough all elements of an array and check if there is valid
139 | // options expression within. An valid option is a token that starts
140 | // double dashes. E.G. --my_option
141 | function contains_expr(arr) {
142 | if(!arr || !arr.length) return false;
143 | var l = arr.length;
144 | while(l-- > 0) if(LONG_SWITCH_RE.test(arr[l])) return true;
145 | return false;
146 | }
147 |
148 | // Extends destination object with members of source object
149 | function extend(dest, src) {
150 | var result = dest;
151 | for(var n in src) {
152 | result[n] = src[n];
153 | }
154 | return result;
155 | }
156 |
157 | // Appends spaces to match specified number of chars
158 | function spaces(arg1, arg2) {
159 | var l, builder = [];
160 | if(arg1.constructor === Number) {
161 | l = arg1;
162 | } else {
163 | if(arg1.length == arg2) return arg1;
164 | l = arg2 - arg1.length;
165 | builder.push(arg1);
166 | }
167 | while(l-- > 0) builder.push(' ');
168 | return builder.join('');
169 | }
170 |
171 | // Create a new Parser object that can be used to parse command line arguments.
172 | //
173 | //
174 | function Parser(rules) {
175 | return new OptionParser(rules);
176 | }
177 |
178 | // Creates an error object with specified error message.
179 | function OptError(msg) {
180 | return new function() {
181 | this.msg = msg;
182 | this.toString = function() {
183 | return this.msg;
184 | }
185 | }
186 | }
187 |
188 | function OptionParser(rules) {
189 | this.banner = 'Usage: [Options]';
190 | this.options_title = 'Available options:'
191 | this._rules = rules;
192 | this._halt = false;
193 | this.filters = extend({}, PREDEFINED_FILTERS);
194 | this.on_args = {};
195 | this.on_switches = {};
196 | this.on_halt = function() {};
197 | this.default_handler = function() {};
198 | }
199 |
200 | OptionParser.prototype = {
201 |
202 | // Adds args and switchs handler.
203 | on: function(value, fn) {
204 | if(value.constructor === Function ) {
205 | this.default_handler = value;
206 | } else if(value.constructor === Number) {
207 | this.on_args[value] = fn;
208 | } else {
209 | this.on_switches[value] = fn;
210 | }
211 | },
212 |
213 | // Adds a custom filter to the parser. It's possible to override the
214 | // default filter by passing the value "_DEFAULT" to the ´´name´´
215 | // argument. The name of the filter is automatically transformed into
216 | // upper case.
217 | filter: function(name, fn) {
218 | this.filters[name.toUpperCase()] = fn;
219 | },
220 |
221 | // Parses specified args. Returns remaining arguments.
222 | parse: function(args) {
223 | var result = [], callback;
224 | var rules = build_rules(this.filters, this._rules);
225 | var tokens = args.concat([]);
226 | var token;
227 | while(this._halt == false && (token = tokens.shift())) {
228 | if(LONG_SWITCH_RE.test(token) || SHORT_SWITCH_RE.test(token)) {
229 | var arg = undefined;
230 | // The token is a long or a short switch. Get the corresponding
231 | // rule, filter and handle it. Pass the switch to the default
232 | // handler if no rule matched.
233 | for(var i = 0; i < rules.length; i++) {
234 | var rule = rules[i];
235 | if(rule.long == token || rule.short == token) {
236 | if(rule.filter !== undefined) {
237 | arg = tokens.shift();
238 | if(arg && (!LONG_SWITCH_RE.test(arg) && !SHORT_SWITCH_RE.test(arg))) {
239 | try {
240 | arg = rule.filter(arg, tokens);
241 | } catch(e) {
242 | throw OptError(token + ': ' + e.toString());
243 | }
244 | } else if(rule.optional_arg) {
245 | if(arg) {
246 | tokens.unshift(arg);
247 | }
248 | } else {
249 | throw OptError('Expected switch argument.');
250 | }
251 | }
252 | callback = this.on_switches[rule.name];
253 | if (!callback) callback = this.on_switches['*'];
254 | if(callback) callback.apply(this, [rule.name, arg]);
255 | break;
256 | }
257 | }
258 | if(i == rules.length) this.default_handler.apply(this, [token]);
259 | } else {
260 | // Did not match long or short switch. Parse the token as a
261 | // normal argument.
262 | callback = this.on_args[result.length];
263 | result.push(token);
264 | if(callback) callback.apply(this, [token]);
265 | }
266 | }
267 | return this._halt ? this.on_halt.apply(this, [tokens]) : result;
268 | },
269 |
270 | // Returns an Array with all defined option rules
271 | options: function() {
272 | return build_rules(this.filters, this._rules);
273 | },
274 |
275 | // Add an on_halt callback if argument ´´fn´´ is specified. on_switch handlers can
276 | // call instance.halt to abort the argument parsing. This can be useful when
277 | // displaying help or version information.
278 | halt: function(fn) {
279 | this._halt = fn === undefined
280 | if(fn) this.on_halt = fn;
281 | },
282 |
283 | // Returns a string representation of this OptionParser instance.
284 | toString: function() {
285 | var builder = [this.banner, '', this.options_title],
286 | shorts = false, longest = 0, rule;
287 | var rules = build_rules(this.filters, this._rules);
288 | for(var i = 0; i < rules.length; i++) {
289 | rule = rules[i];
290 | // Quick-analyze the options.
291 | if(rule.short) shorts = true;
292 | if(rule.decl.length > longest) longest = rule.decl.length;
293 | }
294 | for(var i = 0; i < rules.length; i++) {
295 | var text = spaces(6);
296 | rule = rules[i];
297 | if(shorts) {
298 | if(rule.short) text = spaces(2) + rule.short + ', ';
299 | }
300 | text += spaces(rule.decl, longest) + spaces(3);
301 | text += rule.desc;
302 | builder.push(text);
303 | }
304 | return builder.join('\n');
305 | }
306 | }
307 |
308 | self.VERSION = VERSION;
309 | self.OptionParser = OptionParser;
310 |
311 | })(optparse);
312 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "optparse",
3 | "author": "Johan Dahlberg",
4 | "description": "Command-line option parser",
5 | "keywords": ["option", "parser", "command-line", "cli", "terminal"],
6 | "version": "1.0.5",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/jfd/optparse-js.git"
10 | },
11 | "main": "./lib/optparse"
12 | }
13 |
--------------------------------------------------------------------------------
/seed.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: optparse
3 | description: Command-line option parser
4 | tags: option parser command-line cli terminal
5 | version: 1.0.1
6 |
--------------------------------------------------------------------------------