Browse Source

-Initial commit

develop
Corey Hart 12 years ago
commit
596e52f1bc
  1. 1
      .gitignore
  2. 5
      .lintignore.json
  3. 7
      Makefile
  4. 1
      build/lint.js
  5. 1
      index.js
  6. 434
      lib/argv.js
  7. 17
      package.json

1
.gitignore

@ -0,0 +1 @@
node_modules/

5
.lintignore.json

@ -0,0 +1,5 @@
{
"ignore": [
"node_modules/"
]
}

7
Makefile

@ -0,0 +1,7 @@
.PHONY: all test
all: lint
lint:
@node build/lint.js

1
build/lint.js

@ -0,0 +1 @@
require( 'nlint' ).render( __dirname + '/../' );

1
index.js

@ -0,0 +1 @@
module.exports = require( './lib/argv.js' );

434
lib/argv.js

@ -0,0 +1,434 @@
var PATH = require( 'path' ),
toString = Object.prototype.toString,
rhome = /^\~\//,
rroot = /^\//,
rlistcsv = /^(list|csv)\,[a-z]+$/,
rdash = /^\-/,
rddash = /^\-\-/,
ristarget = /^[^\-]/,
SCRIPT_NAME = ( process.argv[ 1 ] || '' ).split( '/' ).pop(),
helpOption = {
name: 'help',
short: 'h',
type: function(){ return true; },
description: 'Displays help information about this script',
example: "'" + SCRIPT_NAME + " -h' or '" + SCRIPT_NAME + " --help'",
onset: function( args ) {
self.help( args.mod );
process.exit( 0 );
}
},
self;
// argv module
module.exports = self = {
// Default script name
name: SCRIPT_NAME,
// Default description to the triggered script 'node script'
description: 'Usage: ' + SCRIPT_NAME + ' [options]',
// Modules
mods: {},
// Shorthand options
short: { h: helpOption },
// Options
options: { help: helpOption },
// List of common types
types: {
string: function( value ) {
return value.toString();
},
path: function( value ) {
if ( rhome.exec( value ) ) {
value = PATH.normalize( process.env.HOME + '/' + value.replace( rhome, '' ) );
}
else if ( ! rroot.exec( value ) ) {
value = PATH.normalize( process.cwd() + '/' + value );
}
return value;
},
'int': function( value ) {
return parseInt( value, 10 );
},
'float': function( value ) {
return parseFloat( value, 10 );
},
'boolean': function( value ) {
return ( value == 'true' || value === '1' );
},
list: function( value, name, options ) {
if ( ! options[ name ] ) {
options[ name ] = [];
}
options[ name ].push( value );
return options[ name ];
},
csv: function( value ) {
return value.split( ',' );
},
'listcsv-combo': function( type ) {
var parts = type.split( ',' ),
primary = parts.shift(),
secondary = parts.shift();
return function( value, name, options, args ) {
// Entry is going to be an array
if ( ! options[ name ] ) {
options[ name ] = [];
}
// Channel to csv or list
if ( primary == 'csv' ) {
value.split( ',' ).forEach(function( val ) {
options[ name ].push( self.types[ secondary ]( val, name, options, args ) );
});
}
else {
options[ name ].push( self.types[ secondary ]( value, name, options, args ) );
}
return options[ name ];
};
}
},
// Creates custom type function
type: function( name, callback ) {
if ( callback === undefined ) {
return self.types[ name ];
}
else if ( self.isFunction( callback ) ) {
self.types[ name ] = callback;
}
else if ( callback === null && self.types.hasOwnProperty( name ) ) {
delete self.types[ name ];
}
return self;
},
// Setting version number, and auto setting version option
version: function( v ) {
self.option({
name: 'version',
short: 'v',
type: function(){ return true; },
description: 'Displays version info',
example: "'" + self.name + " -v' or '" + self.name + " --version'",
onset: function( args ) {
console.log( v + "\n" );
process.exit( 0 );
}
});
return self;
},
// Adding options to definitions list
option: function( mod, object ) {
if ( object === undefined ) {
object = mod;
mod = undefined;
}
// Iterate over array for multi entry
if ( self.isArray( object ) ) {
object.forEach(function( entry ) {
self.option( mod, entry );
});
}
// Handle edge case
else if ( ! self.isObject( object ) ) {
throw new Error( 'No option definition provided' + ( mod ? ' for module ' + mod : '' ) );
}
// Handle module definition
else if ( object.mod ) {
self.mod( object );
}
// Correct the object
else {
if ( ! object.name ) {
throw new Error( 'No name provided for option' );
}
else if ( ! object.type ) {
throw new Error( 'No type proveded for option' );
}
object.description = object.description || '';
object.type = self.isFunction( object.type ) ? object.type :
self.isString( object.type ) && rlistcsv.exec( object.type ) ? self.types[ 'listcsv-combo' ]( object.type ) :
self.isString( object.type ) && self.types[ object.type ] ? self.types[ object.type ] :
self.types.string;
// Apply to module
if ( mod ) {
if ( ! self.mods[ mod ] ) {
self.mods[ mod ] = { mod: mod, options: {}, short: {} };
}
// Attach option to submodule
mod = self.mods[ mod ];
mod.options[ object.name ] = object;
// Attach shorthand
if ( object.short ) {
mod.short[ object.short ] = object;
}
}
// Apply to root options
else {
self.options[ object.name ] = object;
// Attach shorthand option
if ( object.short ) {
self.short[ object.short ] = object;
}
}
}
return self;
},
// Creating module
mod: function( object ) {
var mod;
// Allow multi mod setup
if ( self.isArray( object ) ) {
object.forEach(function( value ) {
self.mod( value );
});
}
// Handle edge case
else if ( ! self.isObject( object ) ) {
throw new Error( 'No mod definition provided' );
}
// Force mod name
else if ( ! object.mod ) {
throw new Error( "Expecting 'mod' entry for module" );
}
// Create object if not already done so
else if ( ! self.mods[ object.mod ] ) {
self.mods[ object.mod ] = { mod: object.mod, options: {}, short: {} };
}
// Setup
mod = self.mods[ object.mod ];
mod.description = object.description || mod.description;
// Attach each option
self.option( mod.mod, object.options );
return self;
},
// Cleans out current options
clear: function(){
self.options = {};
self.mods = {};
return self;
},
// Description setup
info: function( mod, description ) {
if ( description === undefined ) {
self.description = mod;
}
else if ( self.mods[ mod ] ) {
self.mods[ mod ] = description;
}
return self;
},
// Prints out the help doc
help: function( mod ) {
var output = [], name, option;
// Printing out just a module's definitions
if ( mod && ( mod = self.mods[ mod ] ) ) {
output = [ '', mod.description, '' ];
for ( name in mod.options ) {
option = mod.options[ name ];
output.push( "\t--" +option.name + ( option.short ? ', -' + option.short : '' ) );
output.push( "\t\t" + option.description );
if ( option.example ) {
output.push( "\t\t" + option.example );
}
// Spacing
output.push( "" );
}
}
// Printing out just the root options
else {
output = [ '', self.description, '' ];
for ( name in self.options ) {
option = self.options[ name ];
output.push( "\t--" +option.name + ( option.short ? ', -' + option.short : '' ) );
output.push( "\t\t" + option.description );
if ( option.example ) {
output.push( "\t\t" + option.example );
}
// Spacing
output.push( "" );
}
}
// Print out the output
console.log( output.join( "\n" ) + "\n\n" );
return self;
},
// Runs the arguments parser
_run: function( argv ) {
var args = { targets: [], options: {} },
opts = self.options,
shortOpts = self.short,
skip, option, index, value, arg;
// Allow for passing of arguments list
argv = self.isArray( argv ) ? argv : process.argv.slice( 2 );
// Switch to module's options when used
if ( argv.length && ristarget.exec( argv[ 0 ] ) && self.mods[ argv[ 0 ] ] ) {
args.mod = argv.shift();
opts = self.mods[ args.mod ].options;
shortOpts = self.mods[ args.mod ].short;
}
// Iterate over arguments
argv.forEach(function( arg, i ) {
// Allow skipping of arguments
if ( skip ) {
return ( skip = false );
}
// Full option '--option'
else if ( rddash.exec( arg ) ) {
arg = arg.replace( rddash, '' );
// Default no value to true
if ( ( index = arg.indexOf( '=' ) ) !== -1 ) {
value = arg.substr( index + 1 );
arg = arg.substr( 0, index );
}
else {
value = 'true';
}
// Be strict, if option doesn't exist, throw and error
if ( ! ( option = opts[ arg ] ) ) {
throw "Option '--" + arg + "' not supported";
}
// Send through type converter
args.options[ arg ] = option.type( value, arg, args.options, args );
// Trigger onset callback when option is set
if ( self.isFunction( option.onset ) ) {
option.onset( args );
}
}
// Shorthand option '-o'
else if ( rdash.exec( arg ) ) {
arg = arg.replace( rdash, '' );
if ( arg.length > 1 ) {
arg.split( '' ).forEach(function( character ) {
if ( ! ( option = shortOpts[ character ] ) ) {
throw "Option '-" + character + "' not supported";
}
args.options[ option.name ] = option.type( value, option.name, args.options, args );
});
}
else {
// Check next option for target association
if ( argv[ i + 1 ] && ristarget.exec( argv[ i + 1 ] ) ) {
value = argv[ i + 1 ];
skip = true;
}
else {
value = 'true';
}
// Ensure that an option exists
if ( ! ( option = shortOpts[ arg ] ) ) {
throw "Option '-" + arg + "' not supported";
}
// Convert it
args.options[ option.name ] = option.type( value, option.name, args.options, args );
// Trigger onset callback when option is set
if ( self.isFunction( option.onset ) ) {
option.onset( args );
}
}
}
// Targets
else {
args.targets.push( arg );
}
});
return args;
},
run: function( argv ) {
try {
return self._run( argv );
}
catch ( e ) {
if ( ! self.isString( e ) ) {
throw e;
}
console.log( "\n" + e + ". Trigger '" + self.name + " -h' for more details.\n\n" );
process.exit( 1 );
}
}
};
// Type tests
"Boolean Number String Function Array Date RegExp Object Error".split(' ').forEach(function( method ) {
if ( method == 'Array' ) {
return ( self.isArray = Array.isArray );
}
else if ( method == 'Error' ) {
self.isError = function( object ) {
return object && ( object instanceof Error );
};
return;
}
var match = '[object ' + method + ']';
self[ 'is' + method ] = function( object ) {
return object !== undefined && object !== null && toString.call( object ) == match;
};
});

17
package.json

@ -0,0 +1,17 @@
{
"name": "argv",
"description": "CLI Argument Parser",
"url": "https://github.com/codenothing/argv",
"keywords": [ "cli", "argv", "options" ],
"author": "Corey Hart <[email protected]>",
"version": "0.0.1",
"main": "./index.js",
"engines": {
"node": ">=0.6.10"
},
"dependencies": {
},
"devDependencies": {
"nlint": "0.0.1"
}
}
Loading…
Cancel
Save