deepify
Version:
DEEP Development Tools
545 lines (428 loc) • 11 kB
JavaScript
/**
* Created by AlexanderC on 8/7/15.
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Program = undefined;
var _Options = require('./Options');
var _Arguments = require('./Arguments');
var _Argument = require('./Argument');
var _ProgramInstanceRequiredException = require('./Exception/ProgramInstanceRequiredException');
var _Help = require('./Help');
var _InvalidActionException = require('./Exception/InvalidActionException');
var _UnknownOptionException = require('./Exception/UnknownOptionException');
var _ValidationException = require('./Exception/ValidationException');
var _deepLog = require('deep-log');
var _deepLog2 = _interopRequireDefault(_deepLog);
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _os = require('os');
var _os2 = _interopRequireDefault(_os);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class Program {
/**
* @param {String} name
* @param {String} version
* @param {String} description
* @param {String} example
* @param {String} section
*/
constructor(name = null, version = null, description = null, example = null, section = null) {
this._name = name.toLowerCase();
this._version = version;
this._example = example;
this._description = description;
this._section = section;
this._commands = [];
this._inputParsed = false;
this._unmanagedArgs = [];
this._action = () => {};
this._opts = new _Options.Options();
this._args = new _Arguments.Arguments();
this._nodeBinary = Program.NODE_BINARY;
this._scriptPath = null;
}
/**
* @returns {String|*}
*/
get nodeBinary() {
return this._nodeBinary;
}
/**
* @returns {String|null}
*/
get scriptPath() {
return this._scriptPath;
}
/**
* @param {Program} program
* @returns {Program}
*/
inherit(program) {
// @todo: merge args as well?
// this._args.merge(program.args);
this._opts.merge(program.opts);
if (!this.hasCommands) {
this._args.remove('command');
}
this._nodeBinary = program.nodeBinary;
this._scriptPath = this._scriptPath || program.scriptPath;
this._version = this._version || program.version;
this.input([].concat(program.unmanagedArgs));
return this;
}
/**
* @param {Array} args
* @returns {Program}
*/
input(args = null) {
if (args === null) {
args = process.argv;
// @todo: do we have to hook here?
this._nodeBinary = args.shift(); // remove 'node'
this._scriptPath = args.shift(); // remove 'path/to/main/script.js'
_Options.Options.normalizeInputOpts(args);
}
this._opts.populate(args);
this._args.populate(args);
this._unmanagedArgs = args;
this._inputParsed = true;
this._args.populateUnmanaged(args);
return this;
}
/**
* @returns {Program}
*/
defaults() {
this._opts.create('cmd-auto-complete', null, 'Used by bash auto completion', false, true);
this._opts.create('version', 'v', 'Prints command version');
this._opts.create('help', 'h', 'Prints command help');
this._opts.create('loglevel', null, 'Switches log level to error|warn|info|debug|silent');
this._opts.create('flatten', 'F', 'Install share backend node_modules between microservice');
if (this.hasCommands) {
this._args.create('command', 'Command to run', false, false, cmd => !!this.getCommand(cmd));
}
return this;
}
/**
* @returns {Program}
* @private
*/
_validateInput() {
try {
this.args.validate();
this.opts.validate();
} catch (e) {
if (e instanceof _ValidationException.ValidationException) {
e.program = this;
}
throw e;
}
if (this._unmanagedArgs.length > 0) {
let opts = [];
for (let i in this._unmanagedArgs) {
if (!this._unmanagedArgs.hasOwnProperty(i)) {
continue;
}
let item = this._unmanagedArgs[i];
if (!_Argument.Argument._matchNonOption(item)) {
opts.push(item);
}
}
// @todo: remove this hook?
let error = new _UnknownOptionException.UnknownOptionException(...opts);
error.program = this;
throw error;
}
return this;
}
/**
* @private
*/
_overrideDefaultConsole() {
process.env.DEEP_LOG_LEVEL = this._opts.locate('loglevel').value || process.env.DEEP_LOG_LEVEL;
Program._logDriver.overrideJsConsole(false);
}
/**
* @param {Array} args
*/
run(args = null) {
this._overrideDefaultConsole();
if (args || !this._inputParsed) {
this.input(args);
}
let showAutoCompletion = this._opts.locate('cmd-auto-complete');
let version = this._opts.locate('version');
let help = this._opts.locate('help');
let command = this._args.locate('command');
if (this.hasCommands && command && command.exists) {
let subProgram = this.getCommand(command.value);
if (!subProgram) {
console.error('');
console.error(`No such command '${command.value}' found!`);
this._outputListCommands();
this.exit(1);
}
subProgram.inherit(this).run();
return;
}
if (showAutoCompletion && showAutoCompletion.exists) {
Program._logDriver.overrideJsConsole(false, false);
this.help.printAutoCompletion(this.hasCommands && command ? command.value : '');
this.exit(0);
}
if (help.exists) {
this.help.print();
this.exit(0);
} else if (version.exists) {
console.log(this.version);
this.exit(0);
} else if (this.hasCommands && command && !command.exists) {
this.help.print();
this.exit(1);
}
this._validateInput();
try {
Program._logDriver.overrideJsConsole();
this._action.bind(this)(...this._args.listValues());
} catch (e) {
console.error(e.message);
console.error(e.stack);
this.exit(1);
}
}
/**
* @param {Number} code
*/
exit(code) {
process.exit(parseInt(code, 10));
}
/**
* @returns {Program}
* @private
*/
_outputListCommands() {
console.log('');
console.log('Available commands:');
console.log('');
for (let i in this._commands) {
if (!this._commands.hasOwnProperty(i)) {
continue;
}
let command = this._commands[i];
console.log(` ${command.name} - ${command.description}`);
}
console.log('');
return this;
}
/**
* @param {String} name
* @returns {Program|*}
*/
getCommand(name) {
name = name.toLowerCase();
for (let i in this._commands) {
if (!this._commands.hasOwnProperty(i)) {
continue;
}
let command = this._commands[i];
if (command.name === name) {
return command;
}
}
return null;
}
/**
* @param {String} name
* @param {Function} action
* @param {String} description
* @param {String} example
* @param {String} section
* @param {String} version
* @returns {Program}
*/
command(name, action, description = null, example = null, section = null, version = null) {
let subProgram = new Program(name, version, description, example, section);
subProgram.action = action;
this._commands.push(subProgram);
return subProgram;
}
/**
* @param {Program} subProgram
* @returns {Program}
*/
addCommand(subProgram) {
if (!(subProgram instanceof Program)) {
throw new _ProgramInstanceRequiredException.ProgramInstanceRequiredException();
}
this._commands.push(subProgram);
return this;
}
/**
* @returns {Array}
*/
get unmanagedArgs() {
return this._unmanagedArgs;
}
/**
* @param {Function} func
*/
set action(func) {
if (typeof func !== 'function') {
throw new _InvalidActionException.InvalidActionException();
}
this._action = func;
}
/**
* @returns {Function}
*/
get action() {
return this._action;
}
/**
* @returns {Boolean}
*/
get hasCommands() {
return this._commands.length > 0;
}
/**
* @returns {Help}
*/
get help() {
return new _Help.Help(this);
}
/**
* @returns {Boolean}
*/
get inputParsed() {
return this._inputParsed;
}
/**
* @returns {Array}
*/
get commands() {
return this._commands;
}
/**
* @returns {Options}
*/
get opts() {
return this._opts;
}
/**
* @returns {Arguments}
*/
get args() {
return this._args;
}
/**
* @returns {String}
*/
get example() {
return this._example;
}
/**
* @param {String} value
*/
set example(value) {
this._example = value;
}
/**
* @returns {String}
*/
get name() {
return this._name;
}
/**
* @param {String} value
*/
set name(value) {
this._name = value.toLowerCase();
}
/**
* @returns {String}
*/
get version() {
return this._version;
}
/**
* @param {String} value
*/
set version(value) {
this._version = value;
}
/**
* @returns {String}
*/
get description() {
return this._description;
}
/**
* @param {String} value
*/
set description(value) {
this._description = value;
}
/**
* @returns {String}
*/
get section() {
return this._section;
}
/**
* @param {String} section
*/
set section(section) {
this._section = section;
}
/**
* @returns {String}
* @constructor
*/
static get NODE_BINARY() {
return 'node';
}
/**
* @returns {DeepLog}
* @private
*/
static get _logDriver() {
if (!Program.hasOwnProperty('__deep_log')) {
Program.__deep_log = new _deepLog2.default();
}
return Program.__deep_log;
}
/**
* @returns {String}
* @private
*/
get _homeDir() {
if (_os2.default.homedir) {
return _os2.default.homedir();
}
return process.env.HOME || _path2.default.sep;
}
/**
* @param {String} inputPath
* @returns {String}
*/
normalizeInputPath(inputPath) {
// set current working directory if empty or no path provided
inputPath = inputPath || process.cwd();
// case some unresolved bash pwd
inputPath = inputPath.replace(/(`\s*pwd\s*`|\$\(\s*pwd\s*\))/ig, process.cwd());
// case tilda used (both ~ and ~/xxx cases)
if (/^~(?:(?:\/|\\).+)?$/i.test(inputPath)) {
inputPath = _path2.default.join(this._homeDir, inputPath && inputPath.length >= 2 ? inputPath.substr(2) : '');
}
// case relative path provided
// check for windows full path like c:/xxx to avoid transformation
if (!/^(?:\/|\\)/i.test(inputPath) && !(/^win/.test(process.platform) && /^[a-z]:(?:\/|\\)/i.test(inputPath))) {
inputPath = _path2.default.join(process.cwd(), inputPath);
}
return _path2.default.resolve(inputPath);
}
}
exports.Program = Program;