filesbox/miniprogram/FilesBox/node_modules/mp-html/tools/plugin.js
2023-09-21 10:23:31 +08:00

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