UNPKG

uni

Version:
228 lines (192 loc) 5.39 kB
'use strict'; var Registry = require('npm-registry') , dot = require('dot-component') , GitHulk = require('githulk') , shelly = require('shelljs') , fuse = require('fusing') , npm = require('../npm') , git = require('../git'); /** * The representation of a single CLI command. The CLI command will execute * various of execution steps in order to complete it's given task. * * @constructor * @param {Uni} uni Uni instance that used this command. * @api public */ function CMD(uni) { if (!(this instanceof CMD)) return new CMD(uni); this.fuse(); // // Create a pre-bound git and npm constructor so they have access to the `uni` // instance and can access our uni and the configuration. // this.git = git.bind(git, uni); this.npm = npm.bind(npm, uni); this.uni = uni; this.githulk = new GitHulk({ token: uni.conf.get('token') || process.env.GITHUB || process.env.GITHULK }); } fuse(CMD); /** * The different execution stages that are required to complete the command. * * @type {Object} * @public */ CMD.writable('steps', {}); /** * Expose the shelljs, but we've pre-silenced it so it doesn't output any * pointless information while we do our magic. * * @type {Function} * @public */ shelly.config.silent = true; CMD.readable('shelly', shelly); /** * Execute the various of execution steps of a given command. * * @param {Function} fn Completion callback. * @returns {CMD} * @api private */ CMD.readable('run', function run(fn) { var steps = Object.keys(this.steps) , cmd = this; // // Hold on your horses! We've got a --help flag so we shouldn't execute the // command but display the help information instead. // if (this.uni.flag.help) return this.help(); (function iterate(index) { var step = cmd.steps[steps[index++]]; if (!step) return fn(); if (!step.length) { if (step.call(cmd) === false) return; return iterate(index); } /** * Callback for async steps. This callback also exposes the completion * callback so a step can bailout of the iteration process so other steps * will not be triggered. * * @param {Error} err Optional error first argument. * @api private */ function next(err) { delete next.end; if (err) return fn(err); iterate(index); } next.end = fn; step.call(cmd, next); }(0)); return this; }); /** * Replace {template} tags in the given template using the supplied data object. * * @param {String} template Template with {placeholders} * @param {Object} data Template data with keys mapped to placeholders. * @returns {String} Compiled template * @api public */ CMD.readable('replace', function replace(template, data) { var key; while (key = /{[^{]+?}/gm.exec(template)) { key = key[0]; template = template.replace(key, dot.get(data, key.slice(1, -1))); } return template; }); /** * Async iterate over the given array. * * @param {Array} array Array of items to iterate over. * @param {Function} process The function that processes items. * @param {Function} fn Completion callback * @returns {CMD} * @api public */ CMD.readable('each', function each(array, process, fn) { var expected = array.length , processed = 0; /** * Prevents double invocation of the callback function. * * @param {Error} err Optional error first argument. * @param {Mixed} data Result of some sort. * @api private */ function once(err, data) { if (fn) fn(err, data); fn = null; } array.forEach(function forEach(item) { /** * Simple callback processor. If there's an error we immediately call the * callback. Please note that this does not stop any processing that has * been done on the other items. * * @param {Error} err Optional error argument * @api private */ function next(err) { if (err) return once(err); if (++processed === expected) once(); } next.end = once; process(item, next); }); return this; }); /** * Write things to STDOUT. * * @param {String} line Line to log * @returns {CMD} * @api public */ CMD.readable('log', function log(line) { if (!this.uni.flag.silence) console.log(line); }); /** * Calculate the maximum length of the items, we need to know this so we * can properly align and space the values. * * @param {Array} arr The array with items we should should scan * @returns {Number} Max length */ CMD.readable('max', function max(arr) { return Math.max.apply(Math, arr.map(function map(value) { return (value).toString().length; })); }); /** * Display help information that we can find * * @returns {CMD} * @api public */ CMD.readable('help', function halp() { var flags = this.merge(this.flags, this.uni.flags) , max = this.max(Object.keys(flags)) + 4 , uni = this , help = []; help.push(this.description[0].toUpperCase() + this.description.slice(1) +'.'); help.push(''); help.push(this.usage || 'Usage: uni '+ this.uni.command +' [flags]'); help.push(''); help.push('Flags:'); help.push.apply(help, Object.keys(flags).map(function each(cmd) { var description = flags[cmd]; return ' '+ cmd + (new Array(max - cmd.length).join(' ')) + description; })); this.log(help.join('\n')); }); // // Expose the module. // module.exports = CMD;