invoke.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. const inquirer = require('inquirer')
  2. const {
  3. chalk,
  4. log,
  5. error,
  6. logWithSpinner,
  7. stopSpinner,
  8. resolvePluginId,
  9. loadModule
  10. } = require('@vue/cli-shared-utils')
  11. const Generator = require('./Generator')
  12. const confirmIfGitDirty = require('./util/confirmIfGitDirty')
  13. const readFiles = require('./util/readFiles')
  14. const getPkg = require('./util/getPkg')
  15. const getChangedFiles = require('./util/getChangedFiles')
  16. const PackageManager = require('./util/ProjectPackageManager')
  17. async function invoke (pluginName, options = {}, context = process.cwd()) {
  18. if (!(await confirmIfGitDirty(context))) {
  19. return
  20. }
  21. delete options._
  22. const pkg = getPkg(context)
  23. // attempt to locate the plugin in package.json
  24. const findPlugin = deps => {
  25. if (!deps) return
  26. let name
  27. // official
  28. if (deps[(name = `@vue/cli-plugin-${pluginName}`)]) {
  29. return name
  30. }
  31. // full id, scoped short, or default short
  32. if (deps[(name = resolvePluginId(pluginName))]) {
  33. return name
  34. }
  35. }
  36. const id = findPlugin(pkg.devDependencies) || findPlugin(pkg.dependencies)
  37. if (!id) {
  38. throw new Error(
  39. `Cannot resolve plugin ${chalk.yellow(pluginName)} from package.json. ` +
  40. `Did you forget to install it?`
  41. )
  42. }
  43. const pluginGenerator = loadModule(`${id}/generator`, context)
  44. if (!pluginGenerator) {
  45. throw new Error(`Plugin ${id} does not have a generator.`)
  46. }
  47. // resolve options if no command line options (other than --registry) are passed,
  48. // and the plugin contains a prompt module.
  49. // eslint-disable-next-line prefer-const
  50. let { registry, $inlineOptions, ...pluginOptions } = options
  51. if ($inlineOptions) {
  52. try {
  53. pluginOptions = JSON.parse($inlineOptions)
  54. } catch (e) {
  55. throw new Error(`Couldn't parse inline options JSON: ${e.message}`)
  56. }
  57. } else if (!Object.keys(pluginOptions).length) {
  58. let pluginPrompts = loadModule(`${id}/prompts`, context)
  59. if (pluginPrompts) {
  60. if (typeof pluginPrompts === 'function') {
  61. pluginPrompts = pluginPrompts(pkg)
  62. }
  63. if (typeof pluginPrompts.getPrompts === 'function') {
  64. pluginPrompts = pluginPrompts.getPrompts(pkg)
  65. }
  66. pluginOptions = await inquirer.prompt(pluginPrompts)
  67. }
  68. }
  69. const plugin = {
  70. id,
  71. apply: pluginGenerator,
  72. options: {
  73. registry,
  74. ...pluginOptions
  75. }
  76. }
  77. await runGenerator(context, plugin, pkg)
  78. }
  79. async function runGenerator (context, plugin, pkg = getPkg(context)) {
  80. const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
  81. const afterInvokeCbs = []
  82. const afterAnyInvokeCbs = []
  83. const generator = new Generator(context, {
  84. pkg,
  85. plugins: [plugin],
  86. files: await readFiles(context),
  87. afterInvokeCbs,
  88. afterAnyInvokeCbs,
  89. invoking: true
  90. })
  91. log()
  92. log(`🚀 Invoking generator for ${plugin.id}...`)
  93. await generator.generate({
  94. extractConfigFiles: true,
  95. checkExisting: true
  96. })
  97. const newDeps = generator.pkg.dependencies
  98. const newDevDeps = generator.pkg.devDependencies
  99. const depsChanged =
  100. JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) ||
  101. JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies)
  102. if (!isTestOrDebug && depsChanged) {
  103. log(`📦 Installing additional dependencies...`)
  104. log()
  105. const pm = new PackageManager({ context })
  106. await pm.install()
  107. }
  108. if (afterInvokeCbs.length || afterAnyInvokeCbs.length) {
  109. logWithSpinner('⚓', `Running completion hooks...`)
  110. for (const cb of afterInvokeCbs) {
  111. await cb()
  112. }
  113. for (const cb of afterAnyInvokeCbs) {
  114. await cb()
  115. }
  116. stopSpinner()
  117. log()
  118. }
  119. log(`${chalk.green('✔')} Successfully invoked generator for plugin: ${chalk.cyan(plugin.id)}`)
  120. const changedFiles = getChangedFiles(context)
  121. if (changedFiles.length) {
  122. log(` The following files have been updated / added:\n`)
  123. log(chalk.red(changedFiles.map(line => ` ${line}`).join('\n')))
  124. log()
  125. log(
  126. ` You should review these changes with ${chalk.cyan(
  127. 'git diff'
  128. )} and commit them.`
  129. )
  130. log()
  131. }
  132. generator.printExitLogs()
  133. }
  134. module.exports = (...args) => {
  135. return invoke(...args).catch(err => {
  136. error(err)
  137. if (!process.env.VUE_CLI_TEST) {
  138. process.exit(1)
  139. }
  140. })
  141. }
  142. module.exports.runGenerator = runGenerator