The create command was mentioned in the previous article;
Let's take a look at the command add and invoke in this article. The reason why we put it together is that when add executes, it will also execute invoke
add
vue add vue-cli-plugin-xxx or vue add @vue/xxx
In this form, it is a plug-in that can be recognized inside vue-cli 3.0
Let's first look at the entrance
program .command('add <plugin> [pluginOptions]') .description('install a plugin and invoke its generator in an already created project') .option('--registry <url>', 'Use specified npm registry when installing dependencies (only for npm)') // Can set source .allowUnknownOption() .action((plugin) => { require('../lib/add')(plugin, minimist(process.argv.slice(3))) })
The entry is relatively simple. Let's take a look at the add.js file
async function add (pluginName, options = {}, context = process.cwd()) { // special internal "plugins" // Here, we do special processing for the two plug-ins @ vue/router and @ vue/vuex, directly from the CLI service drop-down module if (/^(@vue\/)?router$/.test(pluginName)) { return addRouter(context) } if (/^(@vue\/)?vuex$/.test(pluginName)) { return addVuex(context) } const packageName = resolvePluginId(pluginName) // Resolve plug-in name log() log(`📦 Installing ${chalk.cyan(packageName)}...`) log() const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : 'npm') // What is npm and yarn installed with await installPackage(context, packageManager, options.registry, packageName) // Start installing plug-ins log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`) log() const generatorPath = resolveModule(`${packageName}/generator`, context) // Analytic path // Start loading the generator under the plug-in if (generatorPath) { invoke(pluginName, options, context) } else { log(`Plugin ${packageName} does not have a generator to invoke`) } }
This side is also relatively simple and clear at a glance.
async function addRouter (context) { const inquirer = require('inquirer') const options = await inquirer.prompt([{ name: 'routerHistoryMode', type: 'confirm', message: `Use history mode for router? ${chalk.yellow(`(Requires proper server setup for index fallback in production)`)}` }]) invoke.runGenerator(context, { id: 'core:router', apply: loadModule('@vue/cli-service/generator/router', context), options }) } async function addVuex (context) { invoke.runGenerator(context, { id: 'core:vuex', apply: loadModule('@vue/cli-service/generator/vuex', context) }) }
These two are to add router and vuex separately
exports.resolvePluginId = id => { // already full id // e.g. vue-cli-plugin-foo, @vue/cli-plugin-foo, @bar/vue-cli-plugin-foo if (pluginRE.test(id)) { return id } // scoped short // e.g. @vue/foo, @bar/foo if (id.charAt(0) === '@') { const scopeMatch = id.match(scopeRE) if (scopeMatch) { const scope = scopeMatch[0] const shortId = id.replace(scopeRE, '') return `${scope}${scope === '@vue/' ? `` : `vue-`}cli-plugin-${shortId}` } } // default short // e.g. foo return `vue-cli-plugin-${id}` }
Resolve the shape of @ vue/xxx to Vue cli plugin XXX
The main process here is to install the plug-in and inject the invoke
invoke
Let's also take a look at the entrance
program .command('invoke <plugin> [pluginOptions]') .description('invoke the generator of a plugin in an already created project') .option('--registry <url>', 'Use specified npm registry when installing dependencies (only for npm)') .allowUnknownOption() .action((plugin) => { require('../lib/invoke')(plugin, minimist(process.argv.slice(3))) })
The code in add is the same as the entry call, both by calling invoke.js
invoke(pluginName, options, context)
So let's see how the internal implementation of invoke.js is implemented. It is mainly divided into the following steps
- Information validation
- Load plug-in information generator/prompts
- Run Generator
Information validation:
const pkg = getPkg(context) // package file // attempt to locate the plugin in package.json const findPlugin = deps => { if (!deps) return let name // official if (deps[(name = `@vue/cli-plugin-${pluginName}`)]) { return name } // full id, scoped short, or default short if (deps[(name = resolvePluginId(pluginName))]) { return name } } const id = findPlugin(pkg.devDependencies) || findPlugin(pkg.dependencies) // Looking for Vue cli plug-ins in devdpendencies and dependencies dependencies if (!id) { throw new Error( `Cannot resolve plugin ${chalk.yellow(pluginName)} from package.json. ` + `Did you forget to install it?` ) }
Verify whether the package.json file exists and whether the Vue cli plug-in is installed in the package file
Add plug-ins
const pluginGenerator = loadModule(`${id}/generator`, context) // Load the generator file under the plug-in if (!pluginGenerator) { throw new Error(`Plugin ${id} does not have a generator.`) } // resolve options if no command line options (other than --registry) are passed, // and the plugin contains a prompt module. // eslint-disable-next-line prefer-const let { registry, ...pluginOptions } = options if (!Object.keys(pluginOptions).length) { let pluginPrompts = loadModule(`${id}/prompts`, context) // Load the prompts under the plug-in, dialog if (pluginPrompts) { if (typeof pluginPrompts === 'function') { pluginPrompts = pluginPrompts(pkg) } if (typeof pluginPrompts.getPrompts === 'function') { pluginPrompts = pluginPrompts.getPrompts(pkg) } pluginOptions = await inquirer.prompt(pluginPrompts) } }
The above is the built-in code loaded with generator and prompts to run the plug-in
Generator
const generator = new Generator(context, { pkg, plugins: [plugin], files: await readFiles(context), completeCbs: createCompleteCbs, invoking: true })
This one is the same as the one in create
Last
router and vuex go directly to the Generator step, and the previous loading is omitted.