1
0

plugin.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /**
  2. * @fileoverview 处理插件
  3. */
  4. const path = require('path')
  5. const through2 = require('through2')
  6. const config = require('./config')
  7. config.plugins.sort((a, b) => {
  8. // editable 置于最后面
  9. if (a === 'editable') return 1
  10. if (b === 'editable') return -1
  11. // markdown 置于最前面
  12. if (a === 'markdown') return -1
  13. if (b === 'markdown') return 1
  14. // 剩余任意顺序
  15. return 0
  16. })
  17. // 提取和替换标签名选择器(组件中仅支持 class 选择器)
  18. const tagSelector = {}
  19. let tagI = 0
  20. if (config.externStyle) {
  21. config.externStyle = config.externStyle.replace(/[^,\s}]+(?=[^}]*{)/g, $ => {
  22. if (!/[a-zA-Z_]/.test($[0])) return $
  23. if (tagSelector[$]) return '.' + tagSelector[$]
  24. tagSelector[$] = '_' + tagI++
  25. return '.' + tagSelector[$]
  26. })
  27. }
  28. module.exports = {
  29. /**
  30. * @description 构建插件
  31. * @param {string} platform 使用平台
  32. */
  33. build (platform) {
  34. const builds = {} // 构建模块
  35. let plugins = '' // 插件列表
  36. let voidTags = '' // 增加的自闭合标签
  37. let wxml = '' // 要引入到 node.wxml 中的内容
  38. let js = '' // 要引入到 node.js 中的内容
  39. let wxss = config.externStyle // 要引入到 node.wxss 中的内容
  40. const json = {} // 要引入到 node.json 中的内容
  41. // 收集插件中要写入模板文件的内容
  42. for (let i = 0; i < config.plugins.length; i++) {
  43. const plugin = config.plugins[i]
  44. let build = {}
  45. try {
  46. // 专用 build
  47. if (platform === 'uni-app') {
  48. build = require(`../plugins/${plugin}/uni-app/build.js`)
  49. } else {
  50. build = require(`../plugins/${plugin}/miniprogram/build.js`)
  51. }
  52. } catch (e) { }
  53. try {
  54. // 通用 build
  55. build = Object.assign(require(`../plugins/${plugin}/build.js`), build)
  56. } catch (e) { }
  57. // 可以在当前平台使用
  58. if (!build.platform || build.platform.includes(platform)) {
  59. builds[plugin] = build
  60. plugins += `require('./${plugin}/${build.main ? build.main : 'index.js'}'),`
  61. if (build.template) {
  62. wxml += build.template.replace('wx:if', 'wx:elif').replace('v-if', 'v-else-if')
  63. }
  64. if (build.methods) {
  65. for (const method in build.methods) {
  66. js += build.methods[method].toString() + ','
  67. }
  68. }
  69. if (build.usingComponents) {
  70. Object.assign(json, build.usingComponents)
  71. }
  72. if (build.style) {
  73. wxss += build.style
  74. }
  75. }
  76. }
  77. // 加入其他自定义标签
  78. for (const item of config.customElements) {
  79. if (platform === 'uni-app') {
  80. if (item.platforms) {
  81. wxml += '<!-- #ifdef ' + item.platforms.join(' || ').toUpperCase() + ' -->'
  82. }
  83. voidTags += item.name + ','
  84. wxml += '<' + item.name + ' v-else-if="n.name==\'' + item.name + '\'" :class="n.attrs.class" :style="n.attrs.style"'
  85. if (item.attrs) {
  86. for (const attr of item.attrs) {
  87. wxml += ' :' + attr + '="n.attrs'
  88. if (attr.includes('-')) {
  89. wxml += '[\'' + attr + '\']"'
  90. } else {
  91. wxml += '.' + attr + '"'
  92. }
  93. }
  94. }
  95. wxml += ' />'
  96. if (item.platforms) {
  97. wxml += '<!-- #endif -->'
  98. }
  99. } else if (!item.platforms || item.platforms.join(',').toLowerCase().includes(platform)) {
  100. voidTags += item.name + ','
  101. wxml += '<' + item.name + ' wx:elif="{{n.name==\'' + item.name + '\'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}"'
  102. if (item.attrs) {
  103. for (const attr of item.attrs) {
  104. wxml += ' ' + attr + '="{{n.attrs'
  105. if (attr.includes('-')) {
  106. wxml += '[\'' + attr + '\']}}"'
  107. } else {
  108. wxml += '.' + attr + '}}"'
  109. }
  110. }
  111. }
  112. wxml += ' />'
  113. }
  114. }
  115. return through2.obj(function (file, _, callback) {
  116. if (file.isBuffer()) {
  117. // src 目录
  118. if (file.base.includes('src')) {
  119. let content = file.contents.toString()
  120. if (file.basename === 'index.js' || file.basename === 'mp-html.vue') {
  121. // 注册插件列表
  122. content = content.replace(/plugins\s*=\s*\[\]/, `plugins=[${plugins}]`)
  123. } else if (file.basename === 'parser.js') {
  124. // 设置标签名选择器
  125. content = content.replace(/tagSelector\s*=\s*{}/, `tagSelector=${JSON.stringify(tagSelector)}`)
  126. // 设置自闭合标签
  127. .replace(/voidTags\s*:\s*makeMap\('/, 'voidTags: makeMap(\'' + voidTags)
  128. } else if (file.basename === 'node.wxml') {
  129. // 引入模板
  130. content = content.replace(/<!--\s*insert\s*-->/, wxml)
  131. } else if (file.basename === 'node.js') {
  132. // 引入方法
  133. content = content.replace(/methods\s*:\s*{/, 'methods:{' + js)
  134. } else if (file.basename === 'node.wxss') {
  135. // 引入样式
  136. content = wxss + content
  137. } else if (file.basename === 'node.json') {
  138. // 引入组件声明
  139. const comps = JSON.stringify(json).slice(1, -1)
  140. if (comps) {
  141. content = content.replace(/"usingComponents"\s*:\s*{/, '"usingComponents":{' + comps + ',')
  142. }
  143. } else if (file.basename === 'node.vue') {
  144. // 引入 vue
  145. content = content.replace(/<!--\s*insert\s*-->/, wxml)
  146. .replace(/methods\s*:\s*{/, 'methods:{' + js)
  147. .replace('<style>', '<style>' + wxss.replace(/\.[a-zA-Z_][\s\S]*?[{,]/g, '/deep/ $&'))
  148. let importComp = ''
  149. let comps = ''
  150. for (let item in json) {
  151. const val = json[item]
  152. // 插件无法通过这种方式引入
  153. if (val.includes('plugin://')) continue
  154. item = item.replace(/-([a-z])/g, (_, $1) => $1.toUpperCase())
  155. importComp += 'import ' + item + " from '" + val + "'\n"
  156. comps += item + ',\n'
  157. }
  158. content = content.replace('<script>', '<script>\n' + importComp)
  159. .replace(/components\s*:\s*{/, 'components: {\n' + comps)
  160. } else if (file.basename === 'local.html' && wxss) {
  161. // 引入样式
  162. content = '<style>' + wxss + '</style>' + content
  163. }
  164. file.contents = Buffer.from(content)
  165. for (const item in builds) {
  166. if (builds[item].handler) {
  167. builds[item].handler(file, platform)
  168. }
  169. }
  170. } else {
  171. // plugins 目录
  172. const name = file.relative.split(path.sep)[0]
  173. const build = builds[name]
  174. // 本平台不支持使用
  175. if (!build || file.extname === '.md' || file.basename === 'build.js') {
  176. callback()
  177. return
  178. }
  179. // import
  180. if (build.import) {
  181. if (typeof build.import === 'string') {
  182. if (file.relative.includes(build.import)) {
  183. file.import = true
  184. }
  185. } else {
  186. for (let i = 0; i < build.import.length; i++) {
  187. if (file.relative.includes(build.import[i])) {
  188. file.import = true
  189. break
  190. }
  191. }
  192. }
  193. }
  194. if (build.handler) {
  195. build.handler(file, platform)
  196. }
  197. }
  198. }
  199. this.push(file)
  200. callback()
  201. })
  202. },
  203. /**
  204. * @description 引入样式文件到 node.wxss 中
  205. */
  206. importCss () {
  207. let css = ''
  208. return through2.obj(function (file, _, callback) {
  209. if (file.isBuffer()) {
  210. let content = file.contents.toString()
  211. // 要被引入的文件
  212. if (file.import) {
  213. css += content
  214. callback()
  215. return
  216. }
  217. // 引入到对应位置
  218. if (file.basename === 'node.wxss') {
  219. content = css + content
  220. } else if (file.basename === 'node.vue') {
  221. content = content.replace('<style>', '<style>' + css.replace(/\.[a-z_][\s\S]+?[{,]/g, '/deep/ $&'))
  222. } else if (file.basename === 'local.html' && css) {
  223. content = '<style>' + css + '</style>' + content
  224. }
  225. file.contents = Buffer.from(content)
  226. }
  227. this.push(file)
  228. callback()
  229. })
  230. }
  231. }