Vue Cli plug-in development details

The original address

What is the CLI Plugin

To quote an official quote:

CLI plug-in is an NPM package that adds additional features to @vue/ CLI projects. It should always include a Service plug-in as its main export, and optionally include a Generator and a Prompt file.

Usually we need to pull out some of our own configuration/common components for use by each project, and other projects created using cli can be automatically imported by using vue Add oView.

Vue-cli plug-in directory structure

The directory structure of a typical CLI plug-in looks like this:

├── Prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt prompt package.jsonCopy the code

If you need to choose from the command line whether to create some sample components while the plug-in is installed, the directory can be changed to:

. ├ ─ ─ the README. Md ├ ─ ─ the generator | - - - the template │ └ ─ ─ index. The js # generator ├ ─ ─ prompts. Js # command prompt to install ├ ─ ─ index. The js # service Plug-in └ ─ ─ package. JsonCopy the code

Third, GeneratorAPI

A CLI plug-in published as an NPM package can contain a generator. Js or generator/index.js file. The generator inside the plug-in will be invoked in two scenarios:

  • During the initial creation of a project, if the CLI plug-in is installed as part of the project creation preset.

  • The plug-in is installed after the project is created when it is called independently via Vue Invoke.

(1) of the Generator

  • topackage.jsonInject additional dependencies or fields, and add files to the project
  • useejsRender the file under Generator /template.

Example:

module.exports = (api, options, rootOptions) = > {
  // Modify the fields in 'package.json'
  api.extendPackage({
    scripts: {
      test: "vue-cli-service test"}});// Copy and render all files in './template 'with ejS
  api.render("./template");

  if (options.foo) {
    // Conditionally generate files}};Copy the code

(2) GeneratorAPI parameter description

A generator/index should export a function that takes three arguments:

  1. An instance of GeneratorAPI:

  2. Generator option for this plug-in. Prompts create a conversation parameter using Prompts. Js, or preset is loaded from a save in ~/.vuerc. For example, if you save ~/. Vuerc like this:

    {
      "presets": {
        "foo": {
          "plugins": {
            "@vue/cli-plugin-foo": { "option": "bar" }
          }
        }
      }
    }
    Copy the code

    If the user creates a project using Preset Foo, the generator for @vue/cli-plugin-foo will receive {option: ‘bar’} as the second parameter.

    For a third party plug-in, this option will be resolved in the command line arguments from the conversation or when the user executes Vue Invoke (see the conversation for third party plug-ins).

  3. The entire preset (presets.foo) will be passed in as the third argument.

(3) Common APIS of Generator

See here for details of all apis, and here are some commonly used apis

onCreateComplete

  • The ginseng

    • {function}The callback function
  • Usage: Callback function after plug-in file is written to disk, often used for custom plug-in Usage after installing dependencies. For example, import oView from “oView” in SRC /main.js; Vue.use(oView)

api.onCreateComplete(() = > {
  let oview = `\nimport oView from 'oview'; \n\nVue.use(oView); `;

  const fs = require("fs");
  const mainPath = api.resolve("./src/main.js");
  // Get the content
  let contentMain = fs.readFileSync(mainPath, {
    encoding: "utf-8"
  });
  if (contentMain.indexOf("oview") = = = -1) {
    const lines = contentMain.split(/\r? \n/g).reverse();
    / / into the import
    const lastImportIndex = lines.findIndex(line= > line.match(/^import/));
    lines[lastImportIndex] += oview;
    // Modify the application
    contentMain = lines.reverse().join("\n");
    fs.writeFileSync(mainPath, contentMain, {
      encoding: "utf-8"}); }});Copy the code

extendPackage

  • The ginseng

    • {object | () => object}
  • Usage instructions: Add dependencies in package.json. Nested fields are deeply merged unless {merge: false} is passed. It also resolves dependency conflicts between plug-ins. Tool configuration fields can be extracted into separate files before files are written to disk.

render

  • The ginseng

    • {string | object | FileMiddleware}– It could be one of the following three
      • Relative path to a folder: for example./template;
      • {sourceTemplate: targetFile}The object hash of the map;
      • Custom file middleware functions
    • {object} [additionalData]– Other data available to the template
    • {object} [ejsOptions]– ejs Specifies additional parameters
  • Usage: Use EJS to render files into the project structure

resolve

  • The ginseng

    • {string} _path– Relative to the project root directory directory
  • The return value

    • {string}– Absolute file path
  • Get the absolute path of a file/folder

hasPlugin

Determine if a plug-in already exists.

  • The ginseng

    • {string} id– the plug-in id, can be omitted (@ vue / | vue – | @ the scope/vue) – cli – plugin – prefix
    • {string} version– Version range. This value is optional
  • The return value

    • {boolean}

cliVersion

Use the @vue/ CLI version of this plug-in

cliServiceVersion

Use the @vue/ CLI-service version of this plug-in.

4. Service plug-in

registerCommand

  • Effect: Registers a file similar to thevue-cli-service [name]The command
  • The arguments:
    • Command name (name),
    • Optional parameter (OPTS)
{
  description: string,
  usage: string,
  options: { [string]: string }
}
Copy the code
  • Callback function (FN)

Vue-cli-service serve is an example

module.exports = (api, options) = > {
api.registerCommand('serve', {
    description: 'start development server'.usage: 'vue-cli-service serve [options] [entry]'.options: {
      '--open': `open browser on server start`.'--copy': `copy url to clipboard on server start`.'--mode': `specify env mode (default: development)`.'--host': `specify host (default: ${defaults.host}) `.'--port': `specify port (default: ${defaults.port}) `.'--https': `use https (default: ${defaults.https}) `.'--public': `specify the public network URL for the HMR client`.'--skip-plugins': `comma-separated list of plugin names to skip for this run`}},async function serve (args) {}Copy the code

chainWebpack

What is chainWebpack? Please see here

Webpack – chain website

  • Action: Changes the WebPack configuration in a chained manner
  • Input parameter: callback function
// An example: modify the default index.html file
api.chainWebpack(function(config) {
  config.plugin("html").tap(args= > {
    args[0].template = "/Users/username/proj/app/templates/index.html";
    return args;
  });
});
Copy the code

configureWebpack

ConfigureWebpack There are two ways to modify the Webpack configuration. Introduction to the solid Web Pack for Vue

  • ConfigureWebpack forObjectType, yesMerge configuration into Webpack
api.configureWebpack: {
    plugins: [
      new MyAwesomeWebpackPlugin()
    ]
}

Copy the code
  • ConfigureWebpack forfunctionType, yesModify the WebPack configuration directly
api.configureWebpack: config= > {
    if (process.env.NODE_ENV === 'production') {
      // Modify the configuration for the production environment...
    } else {
      // Modify the configuration for the development environment...}}Copy the code

resolveWebpackConfig

To get the modified WebPack configuration, we usually need to get the modified Webpack value after using api.configureWebPack or api.chainWebPack

api.chainWebpack(webpackConfig= > {
  / /...
});
// Get the modified Webpack value
const webpackConfig = api.resolveWebpackConfig();
Copy the code

resolveChainableWebpackConfig

Returns a chain chainWebpack configuration, resolveWebpackConfig is returned directly webpack configuration, but a chainWebpack resolveChainableWebpackConfig returns

version

Cli @vue/ CLI-service version in use.

getCwd

Current working directory

resolve

  • Input parameter: directory relative to the root directory
  • Return value: Absolute path

hasPlugin

  • Effect: Checks whether a plug-in already exists for the project
  • Input parameter: plug-in ID, which can be ignored@vue/|vue-|@scope/vueThe prefix.
  • Reference: Boolean

Prompts

When you create a new project or add a new plug-in to an existing project, you need to be prompted to handle the user’s choices. All prompt logic is stored in the prompts. Js file.

Prompt in vue is the Inquirer used;

When the user initializes the plug-in through a call, vue Invoke is used during the invocation if the plug-in prompts. Js is contained in the root directory of the plug-in. The file should be exported to a series of issues that will be handled by Inquirer.

The prompt. Js export can be done in two ways:

  • Export an array of questions
  • Export a function that handles the problem

The result of the prompt. Js export will become the second argument to generator/index.jsoptions.

Before introducing the above two, a brief introduction to the use of Inquirer

Inquirer.js

A question is an object:

{
  type: "input|number|confirm|list|rawlist|expand|checkbox|password|editor".// The default value is input
  name: String,// This is the id of the problem. You can use options.example to get the value of the problem
  message:String | Function,// A problem to print. If defined as a function, the first argument will be the current inquirer session answer. The default is name (followed by a colon)
  default:String | Number | Boolean | Array | Function,// If nothing is entered, the default value is used, or the function that returns the default value. If defined as a function, the first argument will be the current inquirer session answer,
  choices:Array|Function,/ / (Array | Function) Choices Array or Function returns an Array of Choices. If defined as a function, the first argument will be the current inquirer session answer. Array values can be Simple numbers, Strings, or Objects containing the name (displayed in the list), value (stored in the name described above, and short (displayed after selection) attributesValidate: Function,// Verify that the input value meets the requirements
  filter:Function,// Takes user input and returns the filtered values to be used internally in the program. The filtered values are returned to 'Answers', asking if the user is correct
  when:Boolean|Function,// Accept the current user's answer hash and should return true or false depending on whether the question should be asked
  pageSize:Number,/ / when using the list, rawList, expand, there may be a paging checkbox
  prefix:String,// Change the default prefix message
  suffix:String// Change the default postfix message

}
Copy the code

Export an array of questions

module.exports = [
  {
    type: "input".name: "locale".message: "The locale of project localization.".validate: input= >!!!!! input,default: "en"
  }
  // ...
];
Copy the code

Export the handling function

// The input parameter is package.json
module.exports = pkg= > {
  const prompts = [
    {
      type: "input".name: "locale".message: "The locale of project localization.".validate: input= >!!!!! input,default: "en"}];// Dynamically add problems
  if ("@vue/cli-plugin-eslint" in (pkg.devDependencies || {})) {
    prompts.push({
      type: "confirm".name: "useESLintPluginVueI18n".message: "Use ESLint plugin for Vue I18n ?"
    });
  }

  return prompts;
};
Copy the code

Vue-cli-plugin-oview is an example

What does this instance do?

Install the mobile terminal chart library Oview using vuE-CLI plug-in, and add a broken line pattern example. The source address

  1. Initialize the NPM
npm init
# Then type vue-cli-plugin-oview
Copy the code
  1. Creating index.js requires no command registration, so you just need to write the default export.

    module.exports = (api, opts) = > {};
    Copy the code
  2. Write the generator to generate the template

  • Creating a Generator directory
  • Create a new index.js file
  • Creating a template directory

The file directory is:

├ ─ the generator │ ├ ─ index. Js │ └ ─ template │ └ ─ SRC │ ├ ─ components │ │ └ ─ Line. The vue │ └ ─ mock │ └ ─ data. Js ├ ─ index, js ├ ─ package. Json ├ ─ prompts. Js └ ─ the README, mdCopy the code

The Chinese side of the template directory is the sample template, which the CLI will render using EJS.

  1. Add user validation We may need user validation to install instances,
// prompts.js
module.exports = [
  {
    name: "example".type: "confirm".message: "Do you want to add the sample component to the project Components directory?".default: false}];Copy the code
  1. Add dependencies, declared in main.js
// generator/index.js
module.exports = (api, options, rootOptions) = > {
  // Add dependencies to package.json
  api.extendPackage({
    dependencies: {
      oview: "^ 1.1.2." "}});// When the file is written to disk, read main.js and write import oView from "oView" in main.js; Vue.use(oView)
  api.onCreateComplete(() = > {
    let oview = `\nimport oView from 'oview'; \n\nVue.use(oView); `;

    const fs = require("fs");
    const mainPath = api.resolve("./src/main.js");
    // Get the content
    let contentMain = fs.readFileSync(mainPath, {
      encoding: "utf-8"
    });
    if (contentMain.indexOf("oview") = = = -1) {
      const lines = contentMain.split(/\r? \n/g).reverse();
      / / into the import
      const lastImportIndex = lines.findIndex(line= > line.match(/^import/));
      lines[lastImportIndex] += oview;
      // Modify the application
      contentMain = lines.reverse().join("\n");
      fs.writeFileSync(mainPath, contentMain, {
        encoding: "utf-8"}); }});// the parameters passed by prompt
  if (options.example) {
    // Render the template under template
    api.render("./template", {... options }); }};Copy the code
  1. Create a new instance project to test the plug-in
vue create demo
# Change the following paths to your own
npm install --save-dev C:\Users\mrgao\Desktop\demo\vue-cli-plugin-oview
You can see this dependency in package.json
vue invoke vue-cli-plugin-oview
# Now you can see a command that prompts you whether to add a file or not, and if everything is OK, then you can find oView dependencies in package.json and oview in main.js
Copy the code
  1. Published to the NPM
1NPM config set registry HTTPS://registry.npmjs.org/

2To login to NPM, the user name, password, and email address must match NPM loginUsername: Your NPM usernamePassword:
Email: (thisIS your email address3NPM publish + ngx-xxx@0.01.Login HTTPS://www.npmjs.com/ you can see your own published projects


Copy the code

Give it a thumbs up! The original address

Example :oview plug-in

  1. vue-cli-plugin-oview

If you find this article useful, welcome to Star: mrgaogang.github. IO