UNPKG

cli-engine

Version:
265 lines (225 loc) 8.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _output = require('cli-engine-command/lib/output'); var _output2 = _interopRequireDefault(_output); var _stream = require('cli-engine-command/lib/output/stream'); var _moment = require('moment'); var _moment2 = _interopRequireDefault(_moment); require('cli-engine-config'); var _fsExtra = require('fs-extra'); var _fsExtra2 = _interopRequireDefault(_fsExtra); var _plugins = require('./plugins'); var _plugins2 = _interopRequireDefault(_plugins); var _legacy = require('./plugins/legacy'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } exports.default = class { constructor({ config, out }) { this.compaddArgs = []; this.compaddFlags = []; this.argsSetterFns = []; this.flagsSetterFns = []; this.cmdsWithDesc = []; this.cmdsWithFlags = []; this.config = config; this.out = out; } get completionsCachePath() { return _path2.default.join(this.config.cacheDir, 'completions'); } get acLogfile() { return _path2.default.join(this.config.cacheDir, 'autocomplete.log'); } writeLogFile(msg) { (0, _stream.logToFile)(`[${(0, _moment2.default)().format()}] ${msg}`, this.acLogfile); } async createCaches() { await _fsExtra2.default.ensureDir(this.completionsCachePath); await this._createCommandsCache(); await this._createCommandFuncsCache(); } async _createCommandsCache() { try { const plugins = await new _plugins2.default(this.out).list(); await Promise.all(plugins.map(async p => { const hydrated = await p.pluginPath.require(); const cmds = hydrated.commands || []; return cmds.map(c => { try { if (c.hidden || !c.topic) return; const Command = typeof c === 'function' ? c : (0, _legacy.convertFromV5)(c); const publicFlags = Object.keys(Command.flags || {}).filter(flag => !Command.flags[flag].hidden).map(flag => `--${flag}`).join(' '); const flags = publicFlags.length ? ` ${publicFlags}` : ''; const namespace = p.namespace ? `${p.namespace}:` : ''; const id = Command.command ? `${Command.topic}:${Command.command}` : Command.topic; this.cmdsWithFlags.push(`${namespace}${id}${flags}`); } catch (err) { this.out.debug(`Error creating autocomplete a command in ${p.name}, moving on...`); this.out.debug(err.message); this.writeLogFile(err.message); } }); })); const commands = this.cmdsWithFlags.join('\n'); _fsExtra2.default.writeFileSync(_path2.default.join(this.completionsCachePath, 'commands'), commands); } catch (e) { this.out.debug('Error creating autocomplete commands cache'); this.out.debug(e.message); this.writeLogFile(e.message); } } async _createCommandFuncsCache() { try { const plugins = await new _plugins2.default(this.out).list(); // for every plugin await Promise.all(plugins.map(async p => { // re-hydrate const hydrated = await p.pluginPath.require(); const commands = hydrated.commands || []; // for every command in plugin return commands.map(c => { try { if (c.hidden || !c.topic) return; // TODO: fix here // convertFromV5 pukes here w/o topic // but we lose this cmd const cmd = typeof c === 'function' ? c : (0, _legacy.convertFromV5)(c); const namespace = p.namespace || ''; // create completion setters this._addArgsSetterFn(this._genCmdArgSetter(cmd, namespace)); this._addFlagsSetterFn(this._genCmdFlagSetter(cmd, namespace)); this._addCmdWithDesc(this._genCmdWithDescription(cmd, namespace)); } catch (err) { this.out.debug(`Error creating azsh autocomplete command in ${p.name}, moving on...`); this.out.debug(err.message); this.writeLogFile(err.message); } }); })); // write setups and functions to cache this._writeShellSetupsToCache(); this._writeFunctionsToCache(); } catch (e) { this.out.debug('Error creating zsh autocomplete functions cache'); this.out.debug(e.message); this.writeLogFile(e.message); } } _addArgsSetterFn(fn) { if (fn) this.argsSetterFns.push(fn); } _addFlagsSetterFn(fn) { if (fn) this.flagsSetterFns.push(fn); } _addCmdWithDesc(cmd) { if (cmd) this.cmdsWithDesc.push(cmd); } _addCompaddArg(arg) { if (this.compaddArgs.find(a => a === arg)) return; this.compaddArgs.push(arg); } _addCompaddFlag(flag) { if (this.compaddFlags.find(f => f === flag)) return; this.compaddFlags.push(flag); } _genCmdArgSetter(Command, namespace) { const id = this._genCmdID(Command, namespace); const argscompletions = (Command.args || []).map(arg => { if (arg.completion && !arg.hidden) return arg; }).filter(arg => arg).map((arg, i) => { // make flow happy here // even though arg exists const name = arg ? arg.name : ''; const optionalPosition = i === 0 ? '$1' : ''; const optionalColon = arg && arg.required ? '' : ':'; this._addCompaddArg(name); return `"${optionalPosition}${optionalColon}: :_compadd_arg_${name}"`; }).join('\n'); if (argscompletions) { return `_set_${id.replace(/:/g, '_')}_args () { _args=(${argscompletions}) } `; } } _genCmdFlagSetter(Command, namespace) { const id = this._genCmdID(Command, namespace); const flagscompletions = Object.keys(Command.flags || {}).filter(flag => !Command.flags[flag].hidden).map(flag => { const f = Command.flags[flag]; const name = f.parse ? `${flag}=-` : flag; let cachecompl; if (f.completion) { this._addCompaddFlag(flag); cachecompl = `: :_compadd_flag_${flag}`; } let completion = `--${name}[${f.parse ? '' : '(bool) '}${f.description}]${cachecompl || ''}`; return `"${completion}"`; }).join('\n'); if (flagscompletions) { return `_set_${id.replace(/:/g, '_')}_flags () { _flags=( ${flagscompletions} ) } `; } } _genCmdWithDescription(Command, namespace) { const description = Command.description ? `:"${Command.description}"` : ''; return `"${this._genCmdID(Command, namespace).replace(/:/g, '\\:')}"${description}`; } _genCmdID(Command, namespace) { const ns = namespace ? `${namespace}:` : ''; const id = Command.command ? `${ns}${Command.topic}:${Command.command}` : `${ns}${Command.topic}`; return id; } _genAllCmdsListSetter() { return ` _set_all_commands_list () { _all_commands_list=( ${this.cmdsWithDesc.join('\n')} ) } `; } _genCompaddArgs() { const args = this.compaddArgs; return args.map(arg => { return `_compadd_arg_${arg} () { compadd $(echo $(${this.config.bin} autocomplete:values --cmd=$_command_id --resource=${arg} --arg)) }`; }); } _genCompaddFlags() { const flags = this.compaddFlags; return flags.map(flag => { return `_compadd_flag_${flag} () { compadd $(echo $(${this.config.bin} autocomplete:values --cmd=$_command_id --resource=${flag})) }`; }); } _writeShellSetupsToCache() { const zshSetup = `HEROKU_AC_COMMANDS_PATH=${_path2.default.join(this.completionsCachePath, 'commands')}; HEROKU_ZSH_AC_SETTERS_PATH=\${HEROKU_AC_COMMANDS_PATH}_functions && test -f $HEROKU_ZSH_AC_SETTERS_PATH && source $HEROKU_ZSH_AC_SETTERS_PATH; fpath=( ${_path2.default.join(__dirname, '..', 'autocomplete', 'zsh')} $fpath ); autoload -Uz compinit; compinit; `; const bashSetup = `HEROKU_AC_COMMANDS_PATH=${_path2.default.join(this.completionsCachePath, 'commands')}; HEROKU_BASH_AC_PATH=${_path2.default.join(__dirname, '..', 'autocomplete', 'bash', 'heroku.bash')} test -f $HEROKU_BASH_AC_PATH && source $HEROKU_BASH_AC_PATH; `; _fsExtra2.default.writeFileSync(_path2.default.join(this.completionsCachePath, 'zsh_setup'), zshSetup); _fsExtra2.default.writeFileSync(_path2.default.join(this.completionsCachePath, 'bash_setup'), bashSetup); } _writeFunctionsToCache() { const completions = [].concat(this.argsSetterFns).concat(this.flagsSetterFns).concat(this._genAllCmdsListSetter()).concat(this._genCompaddArgs()).concat(this._genCompaddFlags()).join('\n'); _fsExtra2.default.writeFileSync(_path2.default.join(this.completionsCachePath, 'commands_functions'), completions); } };