123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
-
- var fs = require('fs');
- var path = require('path');
- var nopt = require('nopt');
- var util = require('./util');
- var events = require('events');
-
- var collectable = require('./actions/collectable');
- var commandable = require('./actions/commandable');
-
- module.exports = noptify;
- noptify.Noptify = Noptify;
-
- // noptify is a little wrapper around `nopt` module adding a more expressive,
- // commander-like, API and few helpers.
- //
- // Examples
- //
- // var program = noptify(process.argv, { program: 'name' })
- // .version('0.0.1')
- // .option('port', '-p', 'Port to listen on (default: 35729)', Number)
- // .option('pid', 'Path to the generated PID file', String)
- //
- // var opts = program.parse();
- //
- // Returns an instance of `Noptify`
- function noptify(args, options) {
- return new Noptify(args, options);
- }
-
- // Noptify provides the API to parse out option, shorthands and generate the
- // proper generic help output.
- //
- // - args - The Array of arguments to parse (default: `process.argv`);
- // - options - An hash of options with the following properties
- // - program - The program name to use in usage output
- //
- // Every noptify instance is created with two options, `-h, --help` and `-v,
- // --version`.
- function Noptify(args, options) {
- events.EventEmitter.call(this);
- options = this.options = options || {};
- this.args = args || process.argv;
- this._program = options.program || (path.basename(this.args[this.args[0] === 'node' ? 1 : 0]));
-
- this._shorthands = {};
- this._commands = {};
- this._routes = [];
- this._steps = [];
- this.nopt = {};
-
- this.option('help', '-h', 'Show help usage');
- this.option('version', '-v', 'Show package version');
- }
-
- util.inherits(Noptify, events.EventEmitter);
-
- // Inherits from each actions' mixins
- //
- // XXX: consider making it optional? with a `.use()` method?
- util.extend(Noptify.prototype, collectable);
- util.extend(Noptify.prototype, commandable);
-
-
- // Parse the provided options and shorthands, pass them through `nopt` and
- // return the result.
- //
- // When `opts.help` is set, the help output is displayed and `help`
- // event is emitted. The process exists with `0` status, the help output is
- // automatically displayed and the `help` event is emitted.
- //
- // Examples
- //
- // var program = noptify(['foo', '--help'])
- // .on('help', function() {
- // console.log('Examples');
- // console.log('');
- // console.log(' foo bar --baz > foo.txt');
- // });
- //
- // var opts = program.parse();
- // // ... Help output ...
- // // ... Custom help output ...
- // // ... Exit ...
- //
- //
- Noptify.prototype.parse = function parse(argv, position) {
- argv = argv || this.args;
- var options = this._options.reduce(function(opts, opt) {
- opts[opt.name] = opt.type;
- return opts;
- }, {});
-
- this._options.forEach(function(opt) {
- if(!opt.shorthand) return;
- this.shorthand(opt.shorthand, '--' + opt.name);
- }, this);
-
- var opts = nopt(options, this._shorthands, argv, position);
- if(opts.version) {
- console.log(this._version);
- process.exit(0);
- }
-
- var registered = this.registered(opts.argv.remain);
- if(opts.help) {
- if(registered && registered.help) {
- registered.help().emit('help');
- } else {
- this.help().emit('help');
- }
-
- process.exit(0);
- }
-
- this.nopt = opts;
-
- // check remaining args and registered command, for each match, remove the
- // argument from remaining args and trigger the handler, ideally the handler
- // can be another subprogram, for now simple functions
-
- if(this.routeCommand) process.nextTick(this.routeCommand.bind(this));
- if(this.stdin && this._readFromStdin) process.nextTick(this.stdin.bind(this));
- return opts;
- };
-
- // Define the program version.
- Noptify.prototype.version = function version(ver) {
- this._version = ver;
- return this;
- };
-
- // Define the program property.
- Noptify.prototype.program = function program(value) {
- this._program = value || '';
- return this;
- };
-
- // Define `name` option with optional shorthands, optional description and optional type.
- Noptify.prototype.option = function option(name, shorthand, description, type) {
- this._options = this._options || [];
- if(!description) {
- description = shorthand;
- shorthand = '';
- }
-
- if(!type) {
- if(typeof description === 'function') {
- type = description;
- description = shorthand;
- shorthand = '';
- } else {
- type = String;
- }
- }
-
- shorthand = shorthand.replace(/^-*/, ''),
-
- this._options.push({
- name: name,
- shorthand: shorthand.replace(/^-*/, ''),
- description: description || (name + ': ' + type.name),
- usage: (shorthand ? '-' + shorthand + ', ': '' ) + '--' + name,
- type: type
- });
-
- return this;
- };
-
- // Stores the given `shorthands` Hash of options to be `parse()`-d by nopt
- // later on.
-
- Noptify.prototype.shorthand =
- Noptify.prototype.shorthands = function shorthands(options, value) {
- if(typeof options === 'string' && value) {
- this._shorthands[options] = value;
- return this;
- }
-
- Object.keys(options).forEach(function(shorthand) {
- this._shorthands[shorthand] = options[shorthand];
- }, this);
- return this;
- };
-
- // Simply output to stdout the Usage and Help output.
- Noptify.prototype.help = function help() {
- var buf = '';
- buf += '\n Usage: ' + this._program + ' [options]';
- buf += '\n';
- buf += '\n Options:\n';
-
- var maxln = Math.max.apply(Math, this._options.map(function(opts) {
- return opts.usage.length;
- }));
-
- var options = this._options.map(function(opts) {
- return ' ' + pad(opts.usage, maxln + 5) + '\t- ' + opts.description;
- });
-
- buf += options.join('\n');
-
- // part of help input ? --list-shorthands ?
- var shorthands = Object.keys(this._shorthands);
- if(shorthands.length) {
- buf += '\n\n Shorthands:\n';
- maxln = Math.max.apply(Math, Object.keys(this._shorthands).map(function(key) {
- return key.length;
- }));
- buf += Object.keys(this._shorthands).map(function(shorthand) {
- return ' --' + pad(shorthand, maxln + 1) + '\t\t' + this._shorthands[shorthand];
- }, this).join('\n');
- }
-
- buf += '\n';
-
- console.log(buf);
- return this;
- };
-
- // Helpers
-
- // command API
-
- Noptify.prototype.run = function run(fn) {
- if(fn) {
- this._steps.push(fn);
- return this;
- }
-
- if(!this.nopt.argv) return this.parse();
-
- var steps = this._steps;
- var self = this;
- (function next(step) {
- if(!step) return;
- var async = /function\s*\(\w+/.test(step + '');
- if(!async) {
- step();
- return next(steps.shift());
- }
-
- step(function(err) {
- if(err) return self.emit('error', err);
- next(steps.shift());
- });
- })(steps.shift());
- };
-
- function pad(str, max) {
- var ln = max - str.length;
- return ln > 0 ? str + new Array(ln).join(' ') : str;
- }
|