var fs = require('fs'),
path = require('path'),
flatiron = require('../../flatiron'),
common = flatiron.common,
director = require('director');
exports.name = 'cli';
exports.attach = function (options) {
var app = this;
options = options || {};
app.cli = app.cli || {};
options.argv = common.mixin({}, app.cli.argv || {}, options.argv || {});
options.prompt = common.mixin({}, app.cli.prompt || {}, options.prompt || {});
app.cli = common.mixin({}, app.cli, options);
if (app.cli.notFoundUsage == undefined) {
app.cli.notFoundUsage = true;
}
exports.argv.call(this, app.cli.argv);
app.use(flatiron.plugins.inspect);
if (app.cli.version && app.version && (this.argv.v || this.argv.version)) {
console.log(app.version);
process.exit(0);
}
exports.prompt.call(this, app.cli.prompt);
app.router = new director.cli.Router().configure({
async: app.async || app.cli.async
});
app.start = function (options, callback) {
if (!callback && typeof options === 'function') {
callback = options;
options = {};
}
callback = callback || function () {};
app.init(options, function (err) {
if (err) {
return callback(err);
}
app.router.dispatch('on', app.argv._.join(' '), app.log, callback);
});
};
app.cmd = function (path, handler) {
app.router.on(path, handler);
};
exports.commands.call(this);
};
exports.init = function (done) {
var app = this,
logger;
if (!app.log.help) {
logger = app.log.get('default');
logger.cli().extend(app.log);
}
if (app.config) {
app.config.use('argv', {
type: 'literal',
store: app.argv
});
app.config.env();
}
done();
};
exports.argv = function (options) {
var optimist;
if (options && Object.keys(options).length) {
optimist = require('optimist').options(options);
this.showOptions = optimist.help;
this.argv = optimist.argv;
}
else {
optimist = require('optimist');
this.showOptions = optimist.help;
this.argv = optimist.argv;
}
};
exports.commands = function (options) {
var app = this;
function showUsage(target) {
target = Array.isArray(target) ? target : target.split('\n');
target.forEach(function (line) {
app.log.help(line);
});
var lines = app.showOptions().split('\n').filter(Boolean);
if (lines.length) {
app.log.help('');
lines.forEach(function (line) {
app.log.help(line);
});
}
}
app.usage = app.cli.usage;
app.cli.source = app.cli.dir || app.cli.source;
app.commands = app.commands || {};
function loadCommand(name, command, silent) {
var resource = app.commands[name];
if (resource && (!command || resource[command])) {
return true;
}
if (app.cli.source) {
if (!app.cli.sourceDir) {
try {
var stats = fs.statSync(app.cli.source);
app.cli.sourceDir = stats.isDirectory();
}
catch (ex) {
if (app.cli.notFoundUsage) {
showUsage(app.usage || [
'Cannot find commands for ' + name.magenta
]);
}
return false;
}
}
try {
if (app.cli.sourceDir) {
app.commands[name] = require(path.join(app.cli.source, name));
}
else {
app.commands = common.mixin(app.commands, require(app.cli.source));
}
return true;
}
catch (err) {
if (!err.message.match(/^Cannot find module/) || (name && err.message.indexOf(name) === -1)) {
throw err;
}
if (!silent) {
if (app.cli.notFoundUsage) {
showUsage(app.usage || [
'Cannot find commands for ' + name.magenta
]);
}
}
return false;
}
}
}
function ensureDestroy(callback) {
app.prompt.get(['destroy'], function (err, result) {
if (result.destroy !== 'yes' && result.destroy !== 'y') {
app.log.warn('Destructive operation cancelled');
return callback(true);
}
callback();
});
}
function executeCommand(parts, callback) {
var name,
shouldLoad = true,
command,
usage;
if (typeof parts === 'undefined' || typeof parts === 'function') {
throw(new Error('parts is a required argument of type Array'));
}
name = parts.shift();
if (app.cli.source || app.commands[name]) {
if (app.commands[name]) {
shouldLoad = false;
if (typeof app.commands[name] != 'function' && !app.commands[name][parts[0]]) {
shouldLoad = true;
}
}
if (shouldLoad && !loadCommand(name, parts[0])) {
return callback();
}
command = app.commands[name];
while (command) {
usage = command.usage;
if (!app.argv.h && !app.argv.help && typeof command === 'function') {
while (parts.length + 1 < command.length) {
parts.push(null);
}
if (command.destructive) {
return ensureDestroy(function (err) {
return err ? callback() : command.apply(app, parts.concat(callback));
})
}
command.apply(app, parts.concat(callback));
return;
}
command = command[parts.shift()];
}
if (usage || app.cli.usage) {
showUsage(usage || app.cli.usage);
callback(false);
}
}
else if (app.usage) {
showUsage(app.cli.usage);
callback(true);
}
}
exports.executeCommand = executeCommand;
app.alias = function (target, source) {
app.commands.__defineGetter__(target, function () {
var resource = source.resource || source.command || source,
command = source.resource ? source.command : null;
loadCommand(resource, command, true);
resource = app.commands[resource];
if (resource) {
return source.resource && source.command
? resource[source.command]
: resource;
}
});
};
app.router.notfound = function (callback) {
executeCommand(app.argv._.slice(), callback);
};
app.cmd(/help ([^\s]+)?\s?([^\s]+)?/, app.showHelp = function showHelp() {
var args = Array.prototype.slice.call(arguments).filter(Boolean),
callback = typeof args[args.length - 1] === 'function' && args.pop(),
resource,
usage;
function displayAndRespond(found) {
showUsage(usage || app.usage);
if (!found) {
app.log.warn('Cannot find help for ' + args.join(' ').magenta);
}
if (callback) {
callback();
}
}
if (!loadCommand(args[0], args[1], true)) {
return displayAndRespond(false);
}
resource = app.commands[args[0]];
usage = resource.usage;
for (var i = 1; i < args.length; i++) {
if (!resource[args[i]]) {
return displayAndRespond(false);
}
else if (resource[args[i]].usage) {
resource = resource[args[i]];
usage = resource.usage;
}
}
displayAndRespond(true);
});
};
exports.prompt = function (options) {
options = options || {};
this.__defineGetter__('prompt', function () {
if (!this._prompt) {
var prompt = require('prompt'),
self = this;
prompt.allowEmpty = options.allowEmpty || prompt.allowEmpty;
prompt.message = options.message || prompt.message;
prompt.delimiter = options.delimiter || prompt.delimiter;
prompt.properties = options.properties || prompt.properties;
prompt.properties.destroy = {
name: 'destroy',
message: 'This operation cannot be undone, Would you like to proceed?',
default: 'yes'
};
['start', 'pause', 'resume', 'prompt', 'invalid'].forEach(function (ev) {
prompt.on(ev, function () {
var args = Array.prototype.slice.call(arguments);
self.emit.apply(self, [['prompt', ev]].concat(args));
});
});
this._prompt = prompt;
this._prompt.start().pause();
}
return this._prompt;
});
};