├── 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 | --------------------------------------------------------------------------------