UNPKG

craydent-cli

Version:

Node module to manage command line execution and arguments

456 lines (453 loc) 18.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const craydent_error_1 = require("craydent.error"); const craydent_isarray_1 = require("craydent.isarray"); const craydent_isasync_1 = require("craydent.isasync"); const craydent_isfunction_1 = require("craydent.isfunction"); const craydent_isgenerator_1 = require("craydent.isgenerator"); const craydent_isnull_1 = require("craydent.isnull"); const craydent_isobject_1 = require("craydent.isobject"); const craydent_logit_1 = require("craydent.logit"); const craydent_parseadvanced_1 = require("craydent.parseadvanced"); const craydent_parseboolean_1 = require("craydent.parseboolean"); const craydent_strip_1 = require("craydent.strip"); const craydent_syncroit_1 = require("craydent.syncroit"); const craydent_tryeval_1 = require("craydent.tryeval"); const _getFuncName_1 = require("../protected/_getFuncName"); const craydent_include_1 = require("craydent.include"); const syncro = craydent_syncroit_1.default; function _cli_exec(command, options, callback) { let child = (0, craydent_include_1.default)('child_process'); if ((0, craydent_isfunction_1.default)(options)) { callback = options; options = undefined; } return new Promise(function (res, rej) { try { if (!command) { res(false); } options = options || {}; options.silent = !!options.silent; let output = ''; const cprocess = child.exec(command, { env: process.env, maxBuffer: 20 * 1024 * 1024 }, function (err) { const fin = !err || options.alwaysResolve ? res : rej; if (options.outputOnly) { if (callback) { callback.call(cprocess, output); } return fin(output); } if (callback) { const code = err ? err.code : 0; callback.call(cprocess, code, output); return !code ? fin(undefined) : fin({ code, output }); } /* istanbul ignore next */ let code = err ? err.code : 0; fin({ code, output }); }); cprocess.stdout.on('data', function (data) { output += data; if (!options.silent) { process.stdout.write(data); } }); cprocess.stderr.on('data', function (data) { output += data; if (!options.silent) { process.stdout.write(data); } }); } catch (e) /* istanbul ignore next */ { craydent_error_1.default && (0, craydent_error_1.default)('CLI.exec', e); } }); } function add(opt, self) { try { opt.command = opt.command || "*"; let processedOptions = processOptions(opt, self), popt = processedOptions.options, options, value = processedOptions.value; // = self.Options; options = self.Commands[opt.command] = self.Commands[opt.command] || []; self._commandIndex.push(opt.command); if (!(0, craydent_isnull_1.default)(self._potentialCommand) && self._potentialCommand == opt.command) { self.CommandName = self._potentialCommand; self._potentialCommand = null; } if (self.CommandName == opt.command) { /* istanbul ignore else */ if (self.Arguments[0] == self.CommandName && !self._commandRemoved) { self._commandRemoved = true; self.Arguments.splice(0, 1); } } // let val = !self.UsingLabels && self.Arguments[options.length], v; let v; for (let i = 0, len = popt.length; i < len /* && !isNull(value) */; i++) { if (!(0, craydent_isnull_1.default)(v)) { self[popt[i]._property] = v; continue; } // if (isNull(val)) { v = !(0, craydent_isnull_1.default)(value) ? value : opt.default; // } switch (popt[i].type.toLowerCase()) { case "number": v = Number(v); v = isNaN(v) ? Number(popt[i].default) : v; if (isNaN(v)) { v = undefined; } break; case "array": case "object": v = (0, craydent_tryeval_1.default)(v, craydent_parseadvanced_1.default) || v; break; case "bool": case "boolean": const tmp = (0, craydent_parseboolean_1.default)(v); v = (0, craydent_isnull_1.default)(tmp) ? v : tmp; break; } self[popt[i]._property] = v; } if (!opt.command || opt.command == '*') { self.Options.push(opt); } options.push(opt); } catch (e) /* istanbul ignore next */ { (0, craydent_error_1.default)('CLI.add', e); } return self; } function processOptions(option, self) { /* istanbul ignore if */ if (!(0, craydent_isobject_1.default)(option)) { throw `Error: Option [${JSON.stringify(option)}] must be an object. Option will be ignored.`; } // if (!option.option) { return { options: [option] }; } let o = option.option.split(','); let options = [], value; for (let i = 0, len = o.length; i < len; i++) { let prop = (0, craydent_strip_1.default)(o[i], '-'); // if (prop != "name" && self[prop]) { value.value = self[prop]; } if (self[prop]) { value = self[prop]; } options.push({ option: o[i], type: option.type || 'string', description: option.description, default: option.default, command: option.command, required: option.required, _property: prop }); } return { options, value }; } function renderOptions(options, extratabs) { extratabs = extratabs || ""; let content = "", nlinetab = "\n\t"; for (let i = 0, len = options.length; i < len; i++) { /* istanbul ignore next */ let option = options[i], optnames = (option.option || "").split(","), sep = ""; if (optnames.length > 1) { optnames.sort(function (a, b) { return a.length - b.length; }); sep = ","; } content += nlinetab + extratabs + optnames[0] + sep; content += `\t\t${option.type ? `(${option.type})` : "(string)"} ${option.description}${option.required ? "(required)" : ""}`; for (let j = 1, jlen = optnames.length; j < jlen; j++) { /* istanbul ignore else */ if (j + 1 == jlen) { sep = ""; } content += nlinetab + extratabs + optnames[j] + sep; } } return content; } ; function validate(self) { /* istanbul ignore next */ self.CommandName = self.CommandName || '*'; /* istanbul ignore next */ let options = self.Commands[self.CommandName] || []; for (let i = 0, len = options.length; i < len; i++) { const option = options[i], copt = (0, craydent_strip_1.default)(option.option.split(',')[0], '-'); /* istanbul ignore else */ if (self[copt] === undefined) { /* istanbul ignore next */ self[copt] = self.UsingLabels && self.Arguments[i] || option.default; } /* istanbul ignore else */ if (option.required && (0, craydent_isnull_1.default)(self[copt])) { throw `Option ${option.option} is required.`; } else if (option.type && !(0, craydent_isnull_1.default)(self[copt])) { let type = option.type.toLowerCase(); switch (type) { case "number": self[copt] = isNaN(Number(self[copt])) ? self[copt] : Number(self[copt]); break; case "array": case "object": self[copt] = (0, craydent_tryeval_1.default)(self[copt], craydent_parseadvanced_1.default) || self[copt]; break; case "bool": case "boolean": type = 'boolean'; const tmp = (0, craydent_parseboolean_1.default)(self[copt]); self[copt] = (0, craydent_isnull_1.default)(tmp) ? self[copt] : tmp; break; } if ((0, _getFuncName_1.default)(self[copt].constructor).toLowerCase() != type) { throw `Option ${option.option} must be a ${option.type}.`; } } } } class CLI { constructor(params) { /*|{ "info": "CLI parser for arguments and simplem method to execute shell commands", "category": "CLI", "parameters":[], "overloads":[ {"parameters":[ {"options": "(CLIOption[]) Array of options having properties option(required:command option ex: -c), type(data type returned using typeof, ex:string), description, required(default:false)."}]}], "url": "http://www.craydent.com/library/1.9.3/docs#CLI", "returnType": "(CLI)" }|*/ params = params || {}; let args = process.argv, self = this, cself = self, sindex = 2; this._commandIndex = []; this._potentialCommand = args[sindex]; this._commandRemoved = false; this.Interpreter = args[0]; this.ScriptPath = args[1]; this.ScriptName = this.ScriptPath.substring(this.ScriptPath.lastIndexOf('/') + 1); this.Name = params.name || ""; this.Info = params.info || ""; this.Synopsis = params.synopsis || ""; this.Copyright = params.copyright || ""; this.OptionsDescription = params.optionsDescription || ""; this.Description = params.description || ""; this.UsingLabels = true; this.CommandName = ""; this.Commands = params.commands || { /*command:[options]*/}; this.Commands['*'] = self.Commands['*'] || []; this.Options = [ /*{ option: "-c", type:"string", description:"", default:"", command:"", required:false }*/]; this.Arguments = []; this.Notes = params.notes || ""; this.isMan = false; this.isHelp = false; this.waitForPending = []; const command = args[2]; if (!~self._commandIndex.indexOf('*') && command && command[0] == "-") { self.CommandName = "*"; } else if (command == 'man' || command == 'help') { self.CommandName = command; self.isHelp = command == 'help'; // requesting help self.isMan = command == 'man'; // requesting man sindex++; } for (let i = sindex, len = args.length; i < len; i++) { let arg = args[i]; /* istanbul ignore else */ if (arg[0] != '-') { // no label self.UsingLabels = false; cself[arg] = arg; self.Arguments.push(arg); } else if (arg.startsWith('--')) { // this is a multi char label // check if next arg is an option. if it is then this is a flag if (!args[i + 1] || args[i + 1][0] == '-') { cself[(0, craydent_strip_1.default)(arg, '-')] = true; continue; } i++; // add the value of the option as a property cself[(0, craydent_strip_1.default)(arg, '-')] = args[i]; self.Arguments.push(arg); self.Arguments.push(args[i]); } else if (arg.startsWith('-')) { // this is a single char label const opts = (0, craydent_strip_1.default)(arg, '-'); if (opts.length == 1) { // check if next arg is an option. if it is then this is a flag if (!args[i + 1] || args[i + 1][0] == '-') { cself[opts] = true; continue; } i++; cself[opts] = args[i]; self.Arguments.push(arg); self.Arguments.push(args[i]); continue; } for (var j = 0, jlen = opts.length; j < jlen; j++) { cself[opts[j]] = true; } } } if (params.options) { for (var i = 0, len = params.options.length; i < len; i++) { this.add(params.options[i]); } } } isValid() { try { this.validate(); return true; } catch (e) { (0, craydent_logit_1.default)(e); return false; } } validate() { validate(this); } add(opt) { add(opt, this); } option(opt) { add(opt, this); } command(cmd, opts) { try { /* istanbul ignore next */ opts = opts || []; let args = process.argv; if ((0, craydent_isnull_1.default)(cmd)) { throw "Command name must be provided. This operation will be ignored."; } let cindexCMD = cmd.split(/\s/)[0]; this._commandIndex.push(cindexCMD); /* istanbul ignore else */ if (args[2] == cindexCMD) { this.CommandName = cindexCMD; } /* istanbul ignore else */ if (!(0, craydent_isarray_1.default)(opts)) { opts = [opts]; } for (let i = 0, len = opts.length; i < len; i++) { var opt = opts[i]; /* istanbul ignore next */ if (!(0, craydent_isobject_1.default)(opt)) { (0, craydent_error_1.default)('CLI.command', new Error(`Option [${JSON.stringify(opt)}] must be an object. Option will be ignored.`)); continue; } opt.command = cmd; add(opt, this); } /* istanbul ignore next */ this.Commands[cmd] = this.Commands[cmd] || []; return this; } catch (e) { craydent_error_1.default && (0, craydent_error_1.default)('CLI.command', e); throw e; } } action(name, cb) { let args = process.argv, self = this; /* istanbul ignore else */ if ((0, craydent_isfunction_1.default)(name) || (0, craydent_isgenerator_1.default)(name) || (0, craydent_isasync_1.default)(name)) { cb = name; name = self._commandIndex[self._commandIndex.length - 1]; } /* istanbul ignore else */ if (self.CommandName == name) { if ((0, craydent_isgenerator_1.default)(cb)) { eval('self.waitForPending.push(syncro(function*(){ return yield* cb.call(self,args[2]); }));'); } else if ((0, craydent_isasync_1.default)(cb)) { eval('self.waitForPending.push((async function (){ return await cb.call(self,args[2]); })());'); } else { cb.call(self, name); } } return self; } ; renderMan() { try { let nlinetab = "\n\t", dline = "\n\n"; let commands = "", self = this; for (var prop in self.Commands) { /* istanbul ignore if */ if (!self.Commands.hasOwnProperty(prop)) { continue; } if (prop == '*') { continue; } if (!commands) { commands = `ADDITIONAL COMMANDS${nlinetab}`; } commands += `${prop}${renderOptions(self.Commands[prop], '\t')}\n${nlinetab}`; } commands += "\n"; let content = `NAME${nlinetab}${self.Name}${(self.Info ? " -- " + self.Info : "")}${dline}`; content += `SYNOPSIS${nlinetab}${self.Synopsis}${dline}`; content += `DESCRIPTION${nlinetab}${self.Description}${dline}`; content += `OPTIONS${renderOptions(self.Options)}${dline}`; content += commands; content += `NOTES${nlinetab}${self.Notes}${dline}`; return content; } catch (e) /* istanbul ignore next */ { (0, craydent_error_1.default)('CLI.renderMan', e); return ""; } } renderHelp() { try { let nlinetab = "\n\t", dline = "\n\n", self = this; let commands = ""; let hasOptions = !!self.Options.length; for (let prop in self.Commands) { /* istanbul ignore if */ if (!self.Commands.hasOwnProperty(prop)) { continue; } if (prop == '*') { continue; } commands += `${prop}${renderOptions(self.Commands[prop], '\t')}\n${nlinetab}`; hasOptions = hasOptions || !!self.Commands[prop].length; } let content = `Description: ${self.Synopsis}${dline}`; content += `Usage: ${self.ScriptName}${(commands && " [command]")}${(hasOptions ? " [options]" : "")}${nlinetab}${commands}\n`; content += `Options:${renderOptions(self.Options)}${dline}`; return content; } catch (e) /* istanbul ignore next */ { (0, craydent_error_1.default)('CLI.renderMan', e); return ""; } } static exec(command, options, callback) { return _cli_exec(command, options, callback); } exec(command, options, callback) { return CLI.exec(command, options, callback); } } exports.default = CLI;