plugin-new.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. const fs = require('fs-extra')
  2. // eslint-disable-next-line import/no-extraneous-dependencies
  3. const path = require('path')
  4. const inquirer = require('inquirer')
  5. const ejs = require('ejs')
  6. const chalk = require('chalk')
  7. const yaml = require('js-yaml')
  8. const dirTree = require('directory-tree')
  9. const validatePName = require('validate-npm-package-name')
  10. const semver = require('semver')
  11. // const came = require('./lib/util')
  12. const came = str => `${str}`.replace(/-\D/g, match => match.charAt(1).toUpperCase())
  13. const questions = []
  14. questions.push({
  15. type: 'input',
  16. name: 'name',
  17. message: '请输入插件名, 多个单词以 "-" 连接, 如: user-permission\n',
  18. validate(value) {
  19. const done = this.async()
  20. setTimeout(() => {
  21. if (!value) {
  22. done('不可为空')
  23. return
  24. }
  25. const regex = /^[a-z][a-z0-9]*(-[a-z][a-z0-9]*)*/
  26. if (!regex.test(value)) {
  27. done('检测到格式错误, 多个单词以 "-" 连接, 如: user-permission')
  28. return
  29. }
  30. const filePath = path.resolve(__dirname, `../src/plugin/${value}`)
  31. if (fs.existsSync(filePath)) {
  32. done('项目中已存在该插件, 请更换其他插件名')
  33. return
  34. }
  35. const v = validatePName(`lc-plugin-${value}`)
  36. if (!v.validForNewPackages) {
  37. done(v.errors.join(', '))
  38. return
  39. }
  40. done(null, true)
  41. })
  42. },
  43. })
  44. questions.push({
  45. type: 'input',
  46. name: 'title',
  47. message: '请输入插件标题, 7个字内\n',
  48. default: '插件标题',
  49. })
  50. questions.push({
  51. type: 'input',
  52. name: 'version',
  53. message: '请输入版本号\n',
  54. default: '1.0.0',
  55. validate(value) {
  56. const done = this.async()
  57. if (!semver.valid(value)) {
  58. done('版本号不符合规范')
  59. return
  60. }
  61. done(null, true)
  62. },
  63. })
  64. questions.push({
  65. type: 'input',
  66. name: 'description',
  67. message: '简述插件处理的业务场景\n',
  68. default: '',
  69. })
  70. questions.push({
  71. type: 'input',
  72. name: 'author',
  73. message: '请输入作者\n',
  74. default: '',
  75. })
  76. const cachePath = path.resolve(__dirname, './.cache')
  77. const cachePluginPath = path.resolve(__dirname, './.cache/plugin')
  78. const pluginTmpPath = path.resolve(__dirname, './template/plugin')
  79. const pluginViewsPath = path.resolve(__dirname, './template/plugin/view')
  80. const pluginStrPos = __dirname.length + '/template/'.length
  81. const pluginsPath = path.resolve(__dirname, '../src/plugin')
  82. // 检测是否有插件文件夹
  83. if (!fs.existsSync(pluginsPath)) {
  84. fs.mkdirSync(pluginsPath)
  85. }
  86. inquirer
  87. .prompt(questions)
  88. .then(answers => {
  89. const result = answers
  90. result.camelCaseName = came(result.name)
  91. return result
  92. })
  93. .then(answers => {
  94. const config = { ...answers }
  95. // 创建缓存文件夹 .cache
  96. if (!fs.existsSync(cachePath)) {
  97. fs.mkdirSync(cachePath)
  98. }
  99. // 清空 plugin 文件夹
  100. if (fs.existsSync(cachePluginPath)) {
  101. fs.removeSync(cachePluginPath)
  102. }
  103. fs.mkdirSync(cachePluginPath)
  104. dirTree(pluginTmpPath, {}, item => {
  105. // 忽略隐藏文件
  106. if (item.extension === '' || item.name[0] === '.') {
  107. return
  108. }
  109. // 处理模板文件
  110. if (item.extension === '.ejs') {
  111. const template = fs.readFileSync(item.path, 'utf8')
  112. const fileConfig = { ...config }
  113. // 舞台 view 文件配置处理
  114. if (item.path.slice(pluginStrPos).split(path.sep)[1] === 'view' && item.name.slice(-8) === '.vue.ejs') {
  115. const viewConfig = {}
  116. viewConfig.icon = 'iconfont icon-demo'
  117. viewConfig.name = fileConfig.camelCaseName + item.name.slice(0, -8)
  118. viewConfig.route = path
  119. .join(config.name, path.relative(pluginViewsPath, item.path))
  120. .split(path.sep)
  121. .join('/')
  122. viewConfig.route = `/${viewConfig.route.slice(0, -8)}`
  123. viewConfig.order = null
  124. viewConfig.inNav = true
  125. viewConfig.title = '舞台页'
  126. viewConfig.type = 'view'
  127. viewConfig.auths = {
  128. role: null,
  129. permission: null,
  130. }
  131. viewConfig.needLogin = true
  132. fileConfig.configYml = yaml.safeDump(viewConfig)
  133. }
  134. const result = ejs.render(template, fileConfig)
  135. const targetPath1 = path.resolve(cachePluginPath, path.relative(pluginTmpPath, item.path).slice(0, -4))
  136. fs.outputFileSync(targetPath1, result)
  137. return
  138. }
  139. // 拷贝其他文件
  140. const targetPath1 = path.resolve(cachePluginPath, path.relative(pluginTmpPath, item.path))
  141. fs.copySync(item.path, targetPath1)
  142. })
  143. return config
  144. })
  145. .then(answers => {
  146. // 复制 .cache 到 plugin
  147. const sourcePath = path.resolve(__dirname, './.cache/plugin')
  148. const targetPath = path.resolve(__dirname, `../src/plugin/${answers.name}`)
  149. fs.copySync(sourcePath, targetPath)
  150. console.log(chalk.green(`创建插件 ${answers.name}: ${targetPath}`))
  151. // eslint-disable-next-line
  152. })
  153. .then(() => {
  154. // eslint-disable-next-line
  155. require('./plugin-get-config.js')
  156. // eslint-disable-next-line
  157. })
  158. .catch(err => {
  159. // eslint-disable-next-line
  160. console.log(chalk.red('创建插件失败'))
  161. console.error(err)
  162. process.exit(1)
  163. })