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()
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |