【vue-cli3源码解析】04_vue add命令

前言

packages/@vue/cli/bin/vue.js中可以得知,执行add <plugin> [pluginOptions]该命令,会执行../lib/add文件。

主函数

../lib/add.js文件中,主要导出了add函数:

module.exports = (...args) => {
  return add(...args).catch(err => {
    error(err)
    if (!process.env.VUE_CLI_TEST) {
      process.exit(1)
    }
  })
}

接下来开始分析add.js

判断是否有未commit的情况

if (!(await confirmIfGitDirty(context))) {
    return
  }

confirmIfGitDirty这个方法中,会判断用户是否修改了代码但是未commit的情况,提醒用户是否要提交。

根据版本号处理router和vuex

const servicePkg = loadModule('@vue/cli-service/package.json', context)
if (servicePkg && semver.satisfies(servicePkg.version, '3.x')) {
// special internal "plugins"
if (/^(@vue\/)?router$/.test(pluginToAdd)) {
  return addRouter(context)
}
if (/^(@vue\/)?vuex$/.test(pluginToAdd)) {
  return addVuex(context)
}
}

使用loadModule获得当前的package.json从而获得vue的版本。再判断是安装router或者vuex模块,就不会通过包管理器安装,而是调用addRouter或者addVuex。以addRouter为例子:

async function addRouter (context) {
  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
  })
}

循环用户使用vue-router是使用hash模式还是history模式,用户选择完之后,调用@vue/cli-service/generator/router,里面调用了@vue/cli-plugin-router/generator。这个文件就是vue-router插件(插件的开发流程详见上一文)。

解析安装的插件名

const pluginRe = /^(@?[^@]+)(?:@(.+))?$/
  const [
    // eslint-disable-next-line
    _skip,
    pluginName,
    pluginVersion
  ] = pluginToAdd.match(pluginRe)
  const packageName = resolvePluginId(pluginName)

  log()
  log(`📦  Installing ${chalk.cyan(packageName)}...`)
  log()

安装

const pm = new PackageManager({ context })

  if (pluginVersion) {
    // 是否指定了版本
    await pm.add(`${packageName}@${pluginVersion}`)
  } else if (isOfficialPlugin(packageName)) {
    // 是否是官方插件
    const { latestMinor } = await getVersions()
    await pm.add(`${packageName}@~${latestMinor}`)
  } else {
    await pm.add(packageName, { tilde: true })
  }

  log(`${chalk.green('✔')}  Successfully installed plugin: ${chalk.cyan(packageName)}`)
  log()

获得插件的generator.js路径

const generatorPath = resolveModule(`${packageName}/generator`, context)
  if (generatorPath) {
    invoke(pluginName, options, context)
  } else {
    log(`Plugin ${packageName} does not have a generator to invoke`)
  }

如果插件的genrator.js或者genrator/index.js文件路径之后,调用invoke方法。

invoke方法

async function invoke (pluginName, options = {}, context = process.cwd()) {
  if (!(await confirmIfGitDirty(context))) {
    return
  }

  delete options._
  const pkg = getPkg(context)

  // 判断插件是否安装
  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)
  if (!id) {
    throw new Error(
      `Cannot resolve plugin ${chalk.yellow(pluginName)} from package.json. ` +
        `Did you forget to install it?`
    )
  }

 // 判断插件内部是否有generator方法
  const pluginGenerator = loadModule(`${id}/generator`, context)
  if (!pluginGenerator) {
    throw new Error(`Plugin ${id} does not have a generator.`)
  }

  // 判断插件是否含有 prompt。如果有则调用 inquirer.prompt 获取插件的 option,并传给其 generator
  let { registry, $inlineOptions, ...pluginOptions } = options
  if ($inlineOptions) {
    try {
      pluginOptions = JSON.parse($inlineOptions)
    } catch (e) {
      throw new Error(`Couldn't parse inline options JSON: ${e.message}`)
    }
  } else if (!Object.keys(pluginOptions).length) {
    let pluginPrompts = loadModule(`${id}/prompts`, context)
    if (pluginPrompts) {
      const prompt = inquirer.createPromptModule()

      if (typeof pluginPrompts === 'function') {
        pluginPrompts = pluginPrompts(pkg, prompt)
      }
      if (typeof pluginPrompts.getPrompts === 'function') {
        pluginPrompts = pluginPrompts.getPrompts(pkg, prompt)
      }
      pluginOptions = await prompt(pluginPrompts)
    }
  }

  const plugin = {
    id,
    apply: pluginGenerator,
    options: {
      registry,
      ...pluginOptions
    }
  }

  await runGenerator(context, plugin, pkg)
}

runGenerator方法中调用了generator.generate,跟vue create中的类似,就不赘述了。

总结

add <plugin> [pluginOptions]命名调用了一个add的主函数,在该函数执行的过程中会执行如下流程:

  • 先判断用户当前代码是否有git且是否存在未提交代码的情况;
  • 获得当前的vue cli的版本号以及是否安装的是router或者vuex,是的话就不会通过包管理器安装,而是调用addRouter或者addVuex,去调用插件的generator进行处理;
  • 解析插件的名;
  • 调用包管理器安装插件;
  • 获得插件的generator文件的路径;
  • 最后调用invoke方法,该函数的流程跟vue create中插件的安装流程差不多。

由此可见:vue add 相当于只是 vue create 中的一部分,vue create 包含了插件的安装以及调用,vue add 命令只是将此功能分离了出来。

参考

add 命令


如有错误,欢迎指出,感谢~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值