cli-engine
Version:
Generic CLI Framework
265 lines (225 loc) • 8.7 kB
JavaScript
'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);
}
};