node-commandline
Version:
Smart command line parser and handler for node js.
221 lines (185 loc) • 6.7 kB
JavaScript
/*
* Smart command-line parser for Node JS.
* Copyright© 2012 by Daan Kets (Blackbit Consulting, http://www.blackbit.be)
* Licensed under the Apache 2.0 license.
*/
/**
* Function for evaluating if a string starts with a sub string.
* @param someString The string to test.
* @param subString The substring to test for.
* @return {Boolean} true if the sub string is found at position 0 within someString.
*/
function startsWith(someString, subString) {
"use strict";
return someString.indexOf(subString) === 0;
}
var Argument = function (name, options) {
"use strict";
var self = this;
self.name = name;
self.required = (options && options.required) ? options.required : false;
self.type = (options && options.type) ? options.type : 'boolean';
if (options && options.hasOwnProperty('order')) {
self.order = Number(options.order);
}
self.sequenced = (options && options.sequenced) ? true : false;
if (self.type === 'boolean') {
self.allowedValues = [true, false];
} else if (options && options.hasOwnProperty('allowedValues')) {
self.allowedValues = options.allowedValues;
}
self.parse = function (value) {
switch (self.type) {
case 'boolean':
return !(value && value === 'false' && value !== 'no' && value !== '-1' && value !== 'null');
case 'number':
return Number(value);
case 'date':
return Date.parse(value);
case 'object':
return JSON.parse(value);
case 'string':
default:
return value;
}
};
self.toString = function () {
var template = (self.order != undefined) ? "<${name}(${value})>" : "-${name}:<${value}>";
if (!self.required) {
template = "[" + template + "]";
}
if (self.type === 'boolean' && self.order === undefined) {
template = template.replace(':', '[:');
template = template + "]";
}
if (self.sequenced) {
template = template.replace(':', ' ');
}
var value = self.type;
if (self.allowedValues) {
value = "{";
for (var i in self.allowedValues) {
if (self.allowedValues.hasOwnProperty(i)) {
value = value + self.allowedValues[i] + "|";
}
}
value = value.substring(0, value.length - 1);
value = value + '}';
}
var output = template.replace('${name}', self.name);
output = output.replace('${value}', value);
return output;
};
};
/**
* Parse a command line. Accepts any number of arguments,
* @return {object} A command instance.
*/
function parseCommandLine() {
"use strict";
var self = this;
var command = {orderedArguments : []};
var sequenced = false;
for (var i in arguments) {
if (sequenced) {
sequenced = false;
continue;
}
if (arguments.hasOwnProperty(i)) {
var argument = arguments[i];
if (startsWith(argument, '-')) { // Named argument
var colonPosition = argument.indexOf(':');
if (colonPosition === -1) {
colonPosition = argument.length;
}
var name = argument.substring(1, colonPosition);
var value = undefined;
if (argument.length > colonPosition + 1) {
value = argument.substring(colonPosition + 1);
}
if (self.args.hasOwnProperty(name)) {
if (self.args[name].sequenced) {
if (i < arguments.length - 1) {
value = arguments[Number(i) + 1];
sequenced = true;
} else {
if (self.args[name].required) {
throw new Error('Missing required value for argument ' + name);
}
}
}
value = self.args[name].parse(value);
}
command[name] = value;
} else { // Ordered argument
command.orderedArguments.push(argument);
}
}
}
for (var argumentName in self.args) {
if (self.args.hasOwnProperty(argumentName)) {
argument = self.args[argumentName];
if (argument.order !== undefined && (argument.order > -1)) {
if (command.orderedArguments.length >= argument.order) {
command[argumentName] = command.orderedArguments[argument.order];
} else {
if (argument.required) {
throw new Error('Missing required argument ' + argumentName);
}
}
} else {
if (argument.required && !command.hasOwnProperty(argumentName)) {
throw new Error('Missing required argument ' + argumentName);
}
}
}
}
return command;
}
/**
* The CommandLine class allows you to model a command line. After creation, it will allow you to parse a command line,
* output a usage line and more.
*
* @constructor Constructs a new command line model/
*/
var CommandLine = function (name) {
"use strict";
var self = this;
self.name = name;
self.args = {};
self.addArgument = function (name, options) {
if (options) {
// Define an argument.
self.args[name] = new Argument(name, options);
} else {
self.args[name] = new Argument(name);
}
return self;
};
self.getArgument = function (name) {
return self.args[name];
};
self.parse = parseCommandLine;
self.parseNode = function () {
var args = Array.prototype.slice.call(arguments);
var command = self.parse.apply(this, args.slice(2, args.length));
command._executable = arguments[0];
command._script = arguments[1];
return command;
};
self.toString = function () {
var result = "";
for (var argumentName in self.args) {
if (self.args.hasOwnProperty(argumentName)) {
result = result + self.args[argumentName].toString() + " ";
}
}
result = result.trim();
return self.name + " " + result;
};
};
/**
* Export the CommandLine class.
* @type {Function}
*/
module.exports.CommandLine = CommandLine;