249 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /**
 | |
|  * @fileoverview 处理插件
 | |
|  */
 | |
| const path = require('path')
 | |
| const through2 = require('through2')
 | |
| 
 | |
| const config = require('./config')
 | |
| 
 | |
| config.plugins.sort((a, b) => {
 | |
|   // editable 置于最后面
 | |
|   if (a === 'editable') return 1
 | |
|   if (b === 'editable') return -1
 | |
|   // markdown 置于最前面
 | |
|   if (a === 'markdown') return -1
 | |
|   if (b === 'markdown') return 1
 | |
|   // 剩余任意顺序
 | |
|   return 0
 | |
| })
 | |
| 
 | |
| // 提取和替换标签名选择器(组件中仅支持 class 选择器)
 | |
| const tagSelector = {}
 | |
| let tagI = 0
 | |
| if (config.externStyle) {
 | |
|   config.externStyle = config.externStyle.replace(/[^,\s}]+(?=[^}]*{)/g, $ => {
 | |
|     if (!/[a-zA-Z_]/.test($[0])) return $
 | |
|     if (tagSelector[$]) return '.' + tagSelector[$]
 | |
|     tagSelector[$] = '_' + tagI++
 | |
|     return '.' + tagSelector[$]
 | |
|   })
 | |
| }
 | |
| 
 | |
| module.exports = {
 | |
|   /**
 | |
|    * @description 构建插件
 | |
|    * @param {string} platform 使用平台
 | |
|    */
 | |
|   build (platform) {
 | |
|     const builds = {} // 构建模块
 | |
|     let pluginImports = '' // 插件引入
 | |
|     let plugins = '' // 插件列表
 | |
|     let voidTags = '' // 增加的自闭合标签
 | |
|     let wxml = '' // 要引入到 node.wxml 中的内容
 | |
|     let js = '' // 要引入到 node.js 中的内容
 | |
|     let wxss = config.externStyle // 要引入到 node.wxss 中的内容
 | |
|     const json = {} // 要引入到 node.json 中的内容
 | |
| 
 | |
|     // 收集插件中要写入模板文件的内容
 | |
|     for (let i = 0; i < config.plugins.length; i++) {
 | |
|       const plugin = config.plugins[i]
 | |
|       let build = {}
 | |
|       try {
 | |
|         // 专用 build
 | |
|         if (platform === 'uni-app') {
 | |
|           build = require(`../plugins/${plugin}/uni-app/build.js`)
 | |
|         } else {
 | |
|           build = require(`../plugins/${plugin}/miniprogram/build.js`)
 | |
|         }
 | |
|       } catch (e) { }
 | |
|       try {
 | |
|         // 通用 build
 | |
|         build = Object.assign(require(`../plugins/${plugin}/build.js`), build)
 | |
|       } catch (e) { }
 | |
|       // 可以在当前平台使用
 | |
|       if (!build.platform || build.platform.includes(platform)) {
 | |
|         builds[plugin] = build
 | |
|         if (platform === 'uni-app') {
 | |
|           plugins += plugin.replace(/-([a-z])/g, ($, $1) => $1.toUpperCase()) + ','
 | |
|           pluginImports += `import ${plugin.replace(/-([a-z])/g, ($, $1) => $1.toUpperCase())} from './${plugin}/${build.main ? build.main : 'index.js'}'\n`
 | |
|         } else {
 | |
|           plugins += `require('./${plugin}/${build.main ? build.main : 'index.js'}'),`
 | |
|         }
 | |
|         if (build.template) {
 | |
|           wxml += build.template.replace('wx:if', 'wx:elif').replace('v-if', 'v-else-if')
 | |
|         }
 | |
|         if (build.methods) {
 | |
|           for (const method in build.methods) {
 | |
|             js += build.methods[method].toString() + ','
 | |
|           }
 | |
|         }
 | |
|         if (build.usingComponents) {
 | |
|           Object.assign(json, build.usingComponents)
 | |
|         }
 | |
|         if (build.style) {
 | |
|           wxss += build.style
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // 加入其他自定义标签
 | |
|     for (const item of config.customElements) {
 | |
|       if (platform === 'uni-app') {
 | |
|         if (item.platforms) {
 | |
|           wxml += '<!-- #ifdef ' + item.platforms.join(' || ').toUpperCase() + ' -->'
 | |
|         }
 | |
|         voidTags += item.name + ','
 | |
|         wxml += '<' + item.name + ' v-else-if="n.name==\'' + item.name + '\'" :class="n.attrs.class" :style="n.attrs.style"'
 | |
|         if (item.attrs) {
 | |
|           for (const attr of item.attrs) {
 | |
|             wxml += ' :' + attr + '="n.attrs'
 | |
|             if (attr.includes('-')) {
 | |
|               wxml += '[\'' + attr + '\']"'
 | |
|             } else {
 | |
|               wxml += '.' + attr + '"'
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         wxml += ' />'
 | |
|         if (item.platforms) {
 | |
|           wxml += '<!-- #endif -->'
 | |
|         }
 | |
|       } else if (!item.platforms || item.platforms.join(',').toLowerCase().includes(platform)) {
 | |
|         voidTags += item.name + ','
 | |
|         wxml += '<' + item.name + ' wx:elif="{{n.name==\'' + item.name + '\'}}" class="{{n.attrs.class}}" style="{{n.attrs.style}}"'
 | |
|         if (item.attrs) {
 | |
|           for (const attr of item.attrs) {
 | |
|             wxml += ' ' + attr + '="{{n.attrs'
 | |
|             if (attr.includes('-')) {
 | |
|               wxml += '[\'' + attr + '\']}}"'
 | |
|             } else {
 | |
|               wxml += '.' + attr + '}}"'
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|         wxml += ' />'
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return through2.obj(function (file, _, callback) {
 | |
|       if (file.isBuffer()) {
 | |
|         // src 目录
 | |
|         if (file.base.includes('src')) {
 | |
|           let content = file.contents.toString()
 | |
|           if (file.basename === 'index.js' || file.basename === 'mp-html.vue') {
 | |
|             // 注册插件列表
 | |
|             if (platform === 'uni-app') {
 | |
|               content = content.replace(/const\s*plugins\s*=\s*\[\]/, `${pluginImports}const plugins=[${plugins}]`)
 | |
|             } else {
 | |
|               content = content.replace(/plugins\s*=\s*\[\]/, `plugins=[${plugins}]`)
 | |
|             }
 | |
|           } else if (file.basename === 'parser.js') {
 | |
|             // 设置标签名选择器
 | |
|             content = content.replace(/tagSelector\s*=\s*{}/, `tagSelector=${JSON.stringify(tagSelector)}`)
 | |
|             // 设置自闭合标签
 | |
|               .replace(/voidTags\s*:\s*makeMap\('/, 'voidTags: makeMap(\'' + voidTags)
 | |
|           } else if (file.basename === 'node.wxml') {
 | |
|             // 引入模板
 | |
|             content = content.replace(/<!--\s*insert\s*-->/, wxml)
 | |
|           } else if (file.basename === 'node.js') {
 | |
|             // 引入方法
 | |
|             content = content.replace(/methods\s*:\s*{/, 'methods:{' + js)
 | |
|           } else if (file.basename === 'node.wxss') {
 | |
|             // 引入样式
 | |
|             content = wxss + content
 | |
|           } else if (file.basename === 'node.json') {
 | |
|             // 引入组件声明
 | |
|             const comps = JSON.stringify(json).slice(1, -1)
 | |
|             if (comps) {
 | |
|               content = content.replace(/"usingComponents"\s*:\s*{/, '"usingComponents":{' + comps + ',')
 | |
|             }
 | |
|           } else if (file.basename === 'node.vue') {
 | |
|             // 引入 vue
 | |
|             content = content.replace(/<!--\s*insert\s*-->/, wxml)
 | |
|               .replace(/methods\s*:\s*{/, 'methods:{' + js)
 | |
|               .replace('<style>', '<style>' + wxss.replace(/\.[a-zA-Z_][^)}]*?[{,]/g, '/deep/ $&')).replace(/,url/g, ', url')
 | |
|             let importComp = ''
 | |
|             let comps = ''
 | |
|             for (let item in json) {
 | |
|               const val = json[item]
 | |
|               // 插件无法通过这种方式引入
 | |
|               if (val.includes('plugin://')) continue
 | |
|               item = item.replace(/-([a-z])/g, (_, $1) => $1.toUpperCase())
 | |
|               importComp += 'import ' + item + " from '" + val + "'\n"
 | |
|               comps += item + ',\n'
 | |
|             }
 | |
|             content = content.replace('<script>', '<script>\n' + importComp)
 | |
|               .replace(/components\s*:\s*{/, 'components: {\n' + comps)
 | |
|           } else if (file.basename === 'local.html' && wxss) {
 | |
|             // 引入样式
 | |
|             content = '<style>' + wxss + '</style>' + content
 | |
|           }
 | |
|           file.contents = Buffer.from(content)
 | |
|           for (const item in builds) {
 | |
|             if (builds[item].handler) {
 | |
|               builds[item].handler(file, platform)
 | |
|             }
 | |
|           }
 | |
|         } else {
 | |
|           // plugins 目录
 | |
|           const name = file.relative.split(path.sep)[0]
 | |
|           const build = builds[name]
 | |
|           // 本平台不支持使用
 | |
|           if (!build || file.extname === '.md' || file.basename === 'build.js') {
 | |
|             callback()
 | |
|             return
 | |
|           }
 | |
|           // import
 | |
|           if (build.import) {
 | |
|             if (typeof build.import === 'string') {
 | |
|               if (file.relative.includes(build.import)) {
 | |
|                 file.import = true
 | |
|               }
 | |
|             } else {
 | |
|               for (let i = 0; i < build.import.length; i++) {
 | |
|                 if (file.relative.includes(build.import[i])) {
 | |
|                   file.import = true
 | |
|                   break
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|           if (build.handler) {
 | |
|             build.handler(file, platform)
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       this.push(file)
 | |
|       callback()
 | |
|     })
 | |
|   },
 | |
|   /**
 | |
|    * @description 引入样式文件到 node.wxss 中
 | |
|    */
 | |
|   importCss () {
 | |
|     let css = ''
 | |
|     return through2.obj(function (file, _, callback) {
 | |
|       if (file.isBuffer()) {
 | |
|         let content = file.contents.toString()
 | |
|         // 要被引入的文件
 | |
|         if (file.import) {
 | |
|           css += content
 | |
|           callback()
 | |
|           return
 | |
|         }
 | |
|         // 引入到对应位置
 | |
|         if (file.basename === 'node.wxss') {
 | |
|           content = css + content
 | |
|         } else if (file.basename === 'node.vue') {
 | |
|           content = content.replace('<style>', '<style>' + css.replace(/\.[a-z_][^)}]+?[{,]/g, '/deep/ $&')).replace(/,url/g, ', url')
 | |
|         } else if (file.basename === 'local.html' && css) {
 | |
|           content = '<style>' + css + '</style>' + content
 | |
|         }
 | |
|         file.contents = Buffer.from(content)
 | |
|       }
 | |
|       this.push(file)
 | |
|       callback()
 | |
|     })
 | |
|   }
 | |
| }
 |