@zkochan/pnpm
Version:
A fast implementation of npm install
306 lines (210 loc) • 6.2 kB
JavaScript
var EventEmitter = require('events').EventEmitter;
var _ = require('lodash');
var minimist = require('minimist');
var flatten = require('flat-arguments');
var async = require('async');
var commandFactory = require('./command');
var commands = require('./commands');
var flags = require('./flags');
var flagFactory = require('./flag');
var wrappers = require('./wrappers');
var parseCommand = require('./utils/parse-command');
var extendFlags = require('./utils/extend-flags');
var bindAll = require('./utils/bind-all-fns');
var flagRunner = require('./utils/flag-runner');
function parseArgv (processArgv, rawFlags) {
// WIP: parse flags with defaults
var argvOptions = {
alias: {},
default: {}
};
rawFlags.forEach(function (flag) {
var names = flag.name().map(flagFactory.unprefix);
var baseName = names[0];
var aliases = [];
if (names.length > 1) {
aliases = _.rest(names);
}
argvOptions.alias[baseName] = aliases;
});
return minimist(_.slice(processArgv, 2), argvOptions);
}
var Cli = exports.Cli = function (options) {
this.process = {};
this.attributes = {};
this.options = options || {};
this.internals = {
commands: commands(),
flags: flags(),
beforeAlls: wrappers(),
afterAlls: wrappers(),
decorators: {},
plugins: []
};
_.extend(this, EventEmitter.prototype);
};
module.exports = function createCli (options) {
return new Cli(options);
};
// Instantiators
Cli.prototype.command = function () {
var names = flatten(arguments);
var command = commandFactory(names);
this.internals.commands.add(command);
return this.findCommand(names);
};
Cli.prototype.flag = function () {
var names = flatten(arguments);
var flag = flagFactory(names);
this.internals.flags.add(flag);
return this.findFlag(names);
};
Cli.prototype.beforeAll = function () {
this.internals.beforeAlls.add(bindAll(this, arguments));
return this;
};
Cli.prototype.afterAll = function () {
this.internals.afterAlls.add(bindAll(this, arguments));
return this;
};
Cli.prototype.default = function () {
var command = commandFactory();
this.internals.defaultCommand = this.internals.defaultCommand || command;
return command;
};
Cli.prototype.run = function (processArgv, runDone) {
runDone = runDone || function () {};
var self = this;
var command = parseCommand(minimist(_.slice(processArgv, 2)));
// Set up decorators
command.decorators = this.internals.decorators.command;
// Execute command, task, or nothing
var commandToRun =
self.findCommandTask(command.name, command.task) ||
self.findCommand(command.name);
// Parse all falgs with aliases, defaults, etc.
this.argv = parseArgv(
processArgv,
extendFlags(
self.internals.flags.all(),
commandToRun ? commandToRun.internals.flags.all() : []
)
);
command.flags = _.omit(this.argv);
// Set process data
this.process = Object.freeze({
command: command.name,
task: command.task,
data: command.data,
flags: _.omit(this.argv, '_')
});
// Run everything
async.series({
beforeAlls: function (done) {
self.runBeforeAlls(command.data, command.flags, done);
},
flags: function (done) {
var commandFlags;
// If command that is getting run has flags that match
// global flags, we need to run those too
if (commandToRun) {
commandFlags = commandToRun.internals.flags.all();
}
self.runFlags(command.flags, done, commandFlags);
},
command: function (done) {
self.internals.commands.run(command, function (err) {
// Try running default command
if (err && self.internals.defaultCommand) {
command.data = self.argv._;
return self.internals.defaultCommand.run(command.data, command.flags, done);
}
// Finish up no matter what
done();
});
},
afterAlls: function (done) {
self.runAfterAlls(command.data, command.flags, done);
}
}, runDone);
return this;
};
// Runners
Cli.prototype.runFlags = function (flagMap, done, _commandFlags_) {
// Combine global and command-level flags
var flgs = extendFlags(this.internals.flags.all(), _commandFlags_);
// Run flags from flag map
flagRunner(flagMap, flgs, done);
};
Cli.prototype.runFlag = function (name, data, done) {
done = done || function () {};
var flag = this.findFlag(name);
if (!flag) {
throw new Error('Flag does not exist');
}
flag.run(data, done);
return this;
};
Cli.prototype.runBeforeAlls = function (data, flgs, done) {
this.internals.beforeAlls.run(data, flgs, done);
return this;
};
Cli.prototype.runAfterAlls = function (data, flgs, done) {
this.internals.afterAlls.run(data, flgs, done);
return this;
};
// Finders
Cli.prototype.findCommand = function () {
return this.internals.commands.findByName(arguments);
};
Cli.prototype.findCommandTask = function (commandName, taskName) {
var cmd = this.findCommand(commandName);
if (!cmd) {
return;
}
return cmd.findTask(taskName);
};
Cli.prototype.findFlag = function () {
return this.internals.flags.findByName(arguments);
};
Cli.prototype.decorate = function (target, name, handler) {
if (target === 'flag') {
flagFactory.decorators.push({
name: name,
handler: handler
});
}
if (target === 'command') {
commandFactory.decorators.push({
name: name,
handler: handler
});
}
return this;
};
Cli.prototype.set = function (name, value) {
if (_.isObject(name)) {
_.each(name, function (val, key) {
this.attributes[key] = val;
}, this);
}
else {
this.attributes[name] = value;
}
return this;
};
Cli.prototype.get = function (name) {
return this.attributes[name];
};
Cli.prototype.register = function (plugins, done) {
var self = this;
done = done || function () {};
// Force object to array
if (plugins.register) {
plugins = [plugins];
}
async.eachSeries(plugins, function (plugin, doneRegistering) {
plugin.register(self, plugin.options || {}, doneRegistering);
}, done);
return this;
};