vue-cli is a new CLI tool created by the Vue project that allows rapid development of Vue.js projects. It has a ton of promise and looks like it is going to seriously change how developers create, update, and maintain their Vue projects.

One great feature of vue-cli is how easy it is to extend with plugins. In fact, all of the out-of-the-box features are implemented as core plugins that get distributed with every install.

Making a vue-cli plugin is pretty straightforward, but good documentation is lacking right now. This isn’t very surprising considering that (as of writing) vue-cli 3.0 hasn’t even been released. Despite that, the core plugins provide a lot of guidance on how we can go about creating our own plugins.

Anatomy of a plugin

All vue-cli plugin names must be preceded by vue-cli-plugin. For example, if you create a plugin called widget-indexer the plugin/project must be called vue-cli-plugin-widget-indexer. It seems a bit long, but this prefix is needed to link into the plugin internals of vue-cli.

There are three basic parts to a vue-cli plugin index.js, prompts.js, and generator.js.

  • index.js : this is the entry point for your plugin. This file must export a function that accepts an instance of PluginAPI and options from the vue.config.js file.
  • prompts.js : this file defines the questions that will be asked of the user when someone vue invokes this plugin.
  • generator.js : this file defines how the plugin will modify the project files, add options, create new files, etc.

Entry Point

The index.js file can be pretty barebones. You will likely just define the name of the command, structure of the options, and include some pre-defined help text.

module.exports = (api, configOptions) => {
  const defaults = { widgetType: 'thing' }

  api.registerCommand('widget-indexer', {
    description: 'Indexes your widgets',
    usage: 'vue-cli-service widget-indexer [options]',
    options: {
      '--widgetType': 'The S3 bucket name, eg: my-site-bucket (default: ${defaults.typeOfWidget})'
    }
  }, (commandArgs) => {
    let options = Object.assign(defaults, commandArgs)
    console.log(`Indexing widgets of type: ${options.widgetType}`)
  })
}

In this code snippet, note the api.registerCommand call. It takes the name of the vue-cli command you are defining, an object that defines how it should be used, and a function that is executed when the command is called.

User Prompts

vue-cli makes asking questions of your users really easy (and nice looking) through the use of inquirer.js. This library allows you to ask a pretty sophisticated set of questions to a user, but it’s best to keep it to a single-level question tree and as simple as possible.

The prompts.js file should export an array of inquirer.js question objects which will be asked in order.

module.exports = [
  {
    name: 'widgetType',
    type: 'input',
    message: 'What kind of widgets to index?',
    default: 'thing'
  },
  {
    name: 'secondQuestion',
    type: 'confirm',
    message: 'Do you want to see the third question?',
    default: false
  },
  {
    name: 'thirdQuestion',
    type: 'input',
    message: 'This is the third question: ',
    when: answers => answers.secondQuestion === true,
    validate: input => input !== '' ? true : 'An answer is required.'
  }
]

This example defines three questions with the third question being required and dependent on the answer to the second question.

Generating Files

Now that you’ve got some input from the user, you can use that to customize how the plugin behaves in a project. For this example, I will modify package.json and add a new command to vue-cli.

module.exports = (api, options) => {
  api.extendPackage({
    scripts: {
      deploy: `vue-cli-service widget-indexer --widgetType=${options.widgetType}`
    }
  })
}

The option widgetType will get passed in from the answers to the questions in prompts.js.

Putting it All Together

So, the flow of functions and options when you run vue invoke widget-indexer is:

  1. prompts.js runs and collects information from the user. That information is wrapped into an options object with the question names as keys and the answers as values.
  2. generator.js receives the PluginAPI instance and options from the prompts. You can do a ton of things in the generator, including copy file templates, interpolate the options into files, and extend package.json.
  3. With package.json appropriately modified, the new vue-cli command is available. Run it with vue widget-indexer.

I strongly recommend that anyone interested in making a plugin for vue-cli check out the core plugins source code and take a look at my own vue-cli-plugin-s3-deploy plugin to see how it’s done.