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