Skip to content

Commit

Permalink
feat: prompt to install known plugin when command is not found (twili…
Browse files Browse the repository at this point in the history
…o#191)

Added a known plugin-command list which enables installing a plugin when a command is attempted and the plugin is not yet installed.
  • Loading branch information
childish-sambino committed Jun 9, 2020
1 parent bd8f030 commit f64acf5
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 35 deletions.
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ test_script:
- .\bin\run --version
- .\bin\run --help
- .\bin\run plugins
- echo y | .\bin\run watch
- npm test
after_test:
- .\node_modules\.bin\nyc report --reporter text-lcov > coverage.lcov
Expand Down
102 changes: 67 additions & 35 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"mocha": "^7.1.1",
"nock": "^12.0.3",
"nyc": "^15.0.1",
"proxyquire": "^2.1.3",
"sinon": "^9.0.2",
"tmp": "^0.1.0"
},
Expand Down Expand Up @@ -89,6 +90,9 @@
],
"plugins:preinstall": [
"./src/hooks/plugin-install"
],
"command_not_found": [
"./src/hooks/command-not-found"
]
},
"macos": {
Expand Down
54 changes: 54 additions & 0 deletions src/hooks/command-not-found.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const Plugins = require('@oclif/plugin-plugins').default;
const { logger } = require('@twilio/cli-core').services.logging;

const PLUGIN_COMMANDS = {
'@twilio-labs/plugin-flex': ['flex'],
'@twilio-labs/plugin-rtc': ['rtc'],
'@twilio-labs/plugin-serverless': ['serverless'],
'@twilio-labs/plugin-token': ['token'],
'@twilio-labs/plugin-watch': ['watch'],
'@dabblelab/plugin-autopilot': ['autopilot']
};

const getSupportedPlugin = commandId => {
return Object.keys(PLUGIN_COMMANDS).find(plugin => {
return PLUGIN_COMMANDS[plugin].find(command => commandId === command || commandId.startsWith(command + ':'));
});
};

const isPluginInstalled = (config, pluginName) => {
return config.plugins.find(p => p.name === pluginName);
};

const installPlugin = async (config, pluginName) => {
const plugins = new Plugins(config);

await config.runHook('plugins:preinstall', { plugin: { name: pluginName } });

return plugins.install(pluginName);
};

module.exports = async function (options) {
const pluginName = getSupportedPlugin(options.id);

if (pluginName && !isPluginInstalled(options.config, pluginName)) {
logger.warn(`Plugin ${pluginName} not installed for command "${options.id}"`);

const prompt = require('inquirer').prompt;
const response = await prompt([{
type: 'confirm',
name: 'install',
message: 'Would you like to install the plugin?',
default: false
}]);
if (response.install) {
logger.warn(`Installing plugin ${pluginName} ...`);

await installPlugin(options.config, pluginName);

logger.warn(`Installed plugin ${pluginName}"!`);
logger.warn('Please try running the command again');
this.exit(0);
}
}
};
51 changes: 51 additions & 0 deletions test/hooks/command-not-found.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const sinon = require('sinon');
const inquirer = require('inquirer');
const proxyquire = require('proxyquire');
const { expect, test } = require('@twilio/cli-test');

function PluginsMock() {
this.install = sinon.stub();
}

const hookFunc = proxyquire('../../src/hooks/command-not-found', {
'@oclif/plugin-plugins': { default: PluginsMock }
});

const config = {
plugins: [{ name: '@twilio-labs/plugin-watch' }],
runHook: sinon.stub()
};

describe('hooks', () => {
describe('command-not-found', () => {
before(() => {
inquirer._prompt = inquirer.prompt;
inquirer.prompt = sinon.stub().resolves({ install: true });
});

after(() => {
inquirer.prompt = inquirer._prompt;
delete inquirer._prompt;
});

test.stderr().it('warns and installs non-installed plugin topics', async ctx => {
ctx.exit = sinon.stub().resolves(1);
await hookFunc.call(ctx, { id: 'serverless', config });
expect(ctx.stderr).to.contain('not installed');
});

test.stderr().it('warns and install non-installed plugins commands', async ctx => {
ctx.exit = sinon.stub().resolves(1);
await hookFunc.call(ctx, { id: 'serverless:init', config });
expect(ctx.stderr).to.contain('not installed');
});

/* eslint-disable no-unused-expressions */
test.stderr().it('does nothing for installed plugins or unknown commands', async ctx => {
await hookFunc.call(ctx, { id: 'watch', config });
expect(ctx.stderr).to.be.empty;
await hookFunc.call(ctx, { id: 'serverless-schmerverless', config });
expect(ctx.stderr).to.be.empty;
});
});
});

0 comments on commit f64acf5

Please sign in to comment.