@antmove/alipay-wx
Version:
transform alipay miniprogram to wx miniprogram tool.
366 lines (339 loc) • 10.3 kB
JavaScript
const os = require('os')
const {
transformEs6,
processMixTemplate,
} = require('@antmove/utils')
const propsHandle = require('../props/index.js')
const proccessComponentProps = require('../component/props')
let { ref } = require('../config')
const indentWidthChar = ' '
let isAddWxs = false
let funName = ''
/**
* @special tags
*/
function createElement(tagName, children = []) {
return {
typeof: 'element',
key: null,
props: {},
type: tagName,
children,
}
}
function processSpecialTags(ast = {}) {
if (ast.type === 'picker' && ast.children[0].length > 1) {
ast.children[0] = [createElement('view', ast.children[0])]
return ast
}
}
function addWxKey(props) {
if (props['a:for']) {
if (!props['a:key']) {
props = Object.assign(props, { 'a:key': { type: 'unknown', value: ['{{index}}'] } })
}
}
return props
}
function transformTypeof(val) {
let str = ''
let params = ''
if (/typeof\s*\w+/.test(val)) {
val = val.replace(/typeof\s*\w+/, (a) => {
str = a.replace(/ +/, ' ')
params = str.split(' ')[1]
a = ` custom.isTypeof(${params}) `
return a
})
}
if (/typeof\s*\((.+?)\)/.test(val)) {
val = val.replace(/typeof\s*\((.+?)\)/, (a) => {
str = a.match(/\((.+?)\)/)[0]
params = str.slice(1, str.length - 1)
a = ` custom.isTypeof(${params}) `
return a
})
}
if (!funName.includes('isTypeof')) {
funName += '\n\t\tisTypeof: function(val) {\n\t\t\treturn typeof(val);\n\t\t},'
}
return val
}
function transformFun(type, val) {
val = val.replace(/{{(.*?)}}/, (a) => {
type = `is${type.charAt(0).toUpperCase()}${type.slice(1)}`
let params = a.match(/\w+.?/)[0]
params = params.slice(0, params.length - 1)
let str = a.slice(2, a.length - 2)
const reg = new RegExp(`${params}`)
str = str.replace(reg, '__item')
if (!funName.includes(type)) {
funName += `\n\t\t${type}: function(__item) {\n\t\t\treturn ${str};\n\t\t},`
}
a = `{{ custom.${type}(${params}) }}`
return a
})
return val
}
function transformToString(val) {
val = val.replace(/{{(.*\.toString\(\)).*}}/, (value) => {
const text = RegExp.$1.toString().split('.')[0]
const str = `custom._toString(${text})`
if (!funName.includes('_toString')) {
funName += '\n\t\t_toString: function(val) {\n\t\t\treturn val.toString();\n\t\t},'
}
const reg = new RegExp(text)
value = value.replace(/\.toString\(\)/, '')
value = value.replace(reg, str)
return value
})
return val
}
function appendWxs(val) {
const arr = ['typeof', 'some', 'every', 'forEach', 'reduce', 'filter', 'toString']
let type = ''
let value = ''
if (val) {
value = val.match(/{{(.*?)}}/)
}
if (value !== null) {
arr.some((item) => {
if (value[0] && value[0].includes(item)) {
type = item
return true
}
return false
})
switch (type) {
case 'typeof':
val = transformTypeof(val)
break
case 'toString':
val = transformToString(val)
break
case 'some':
val = transformFun(type, val)
break
case 'every':
val = transformFun(type, val)
break
case 'forEach':
val = transformFun(type, val)
break
case 'reduce':
val = transformFun(type, val)
break
case 'filter':
val = transformFun(type, val)
break
default:
break
}
if (type !== '') {
isAddWxs = true
}
}
return val
}
function transformStyle(value) {
value = value.trim()
const reg = /{\s?[a-zA-Z]+:.+}/
if (reg.test(value)) {
let val = value.slice(1, value.length - 1)
val = val.replace(/ +/g, '')
const comma = val.charAt(val.length - 2)
if (comma === ',') {
const index = val.lastIndexOf(comma)
val = `${val.slice(0, index)}}`
}
value = `{{ custom.transformStyle(${val}) }}`
isAddWxs = true
}
if (!funName.includes('transformStyle')) {
(
funName += '\n\t\ttransformStyle: function(value) {\n\t\t\tvalue = JSON.stringify(value);\n\t\t\tvalue = value.slice(1, value.length - 1);\n\t\t\tlet val =\'\';\n\t\t\tfor(var i = 0; i < value.length; i++){\n\t\t\t\tif (value[i] !== \'"\') {\n\t\t\t\t\tif (value.indexOf(\'transform\') === -1) {\n\t\t\t\t\t\tif(value[i] === \',\'){\n\t\t\t\t\t\t\tval += \';\';\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tval += \'\';\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif(value.charCodeAt(i) >= 65 && value.charCodeAt(i) <= 90) {\n\t\t\t\t\tval += \'-\' + value[i].toLowerCase();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tval += value[i];\n\t\t\t}\n\t\t\treturn val;\n\t\t},'
)
}
return value
}
function transformStringTemplate(prop) {
let value = prop.value.value[0].replace(/ /g, '')
const reg = /^{{`(.+)`}}$/
if (reg.test(value)) {
const arr = RegExp.$1.split('$')
let str = arr[1].trim().match(/\s*{.+}/)[0]
str = str.slice(1, str.length - 1)
arr[1] = `{{ ${str} }}`
value = arr.join('')
prop.value.value[0] = value
}
}
function transformSjs(ast) {
ast.type = 'wxs'
ast.props.module = ast.props.name
ast.props.src = ast.props.from
delete ast.props.name
delete ast.props.from
return ast
}
function transformRef(prop, filePath) {
prop.id = { type: 'unknown', value: prop.ref.value }
const filename = filePath.replace(/\.axml/, '.js')
const obj = { id: prop.ref.value[0], filename }
ref = Object.assign(ref, obj)
delete prop.ref
}
function transformInput(ast) {
if (ast.props[undefined]) {
ast.props.type = { type: 'unknown', value: ast.props[undefined].value }
delete ast.props[undefined]
}
}
module.exports = function axmlRender(ast = [], fileInfo) {
if (typeof ast === 'string') { return ast }
let wxsLabel = '<wxs module="custom">\n'
let wxsCode = '\tmodule.exports = {'
isAddWxs = false
let _code = ''
let indentWidth = ''
ast.forEach((tagAst) => {
_code += renderFn(tagAst, fileInfo)
})
if (isAddWxs) {
wxsCode += funName
wxsCode += '\n\t}'
wxsCode = transformEs6(wxsCode)
wxsLabel += wxsCode
wxsLabel += '\n</wxs>'
_code += wxsLabel
}
funName = ''
_code = _code.replace(/<text[^>]*\s*.*>\s*.+\s*<\/text>/g, (val) => {
val = val.replace(/\r|\n/g, '')
val = val.replace(/ +/g, ' ')
return val
})
return _code
function incIndent() {
indentWidth += indentWidthChar
}
function decIndent() {
indentWidth = indentWidth.slice(0, -1 * indentWidthChar.length)
}
function renderFn(_ast, _fileInfo) {
let { props } = _ast
proccessComponentProps(_ast, _fileInfo, axmlRender)
processSpecialTags(_ast)
_ast.value = appendWxs(_ast.value)
if (props && props.style) {
props.style.value[0] = transformStyle(props.style.value[0])
}
if (_ast.props) {
Object.values(_ast.props).forEach((obj) => {
obj.value[0] = appendWxs(obj.value[0])
})
}
if (_ast.type === 'import-sjs') {
transformSjs(_ast)
}
if (_ast.type === 'input') {
transformInput(_ast)
}
if (props && props.ref) {
transformRef(props, fileInfo.path)
}
if (_ast.type === 'textContent') {
// todo: fix comment parse bug
if (_ast.value.match(/-->/)) {
return ''
}
return `${_ast.value}`
}
let code = ''
const tagName = _ast.type
const children = _ast.children
appendCode(`<${tagName}`)
props = addWxKey(props)
props = props || {}
let attrCode = ''
Object.keys(props)
.forEach((prop) => {
const propInfo = propsHandle(prop, props[prop], ast)
transformStringTemplate(propInfo)
// a:for process
if (propInfo.key === 'wx:for-items' || propInfo.key === 'a:for-items') {
propInfo.key = 'a:for'
}
if (propInfo.value === null) {
// 无值属性
attrCode += ` ${propInfo.key}`
} else {
let value = propInfo.value.value[0] || ''
value = value.replace(/\.axml/g, '.wx')
.replace(/\.sjs/g, '.wxs')
/**
* support unknown type string
* */
if (propInfo.value && propInfo.value.type === 'unknown') {
let singleIndex = value.indexOf("'")
let doubleIndex = value.indexOf('"')
singleIndex = singleIndex > -1 ? singleIndex : 0
doubleIndex = doubleIndex > -1 ? doubleIndex : 0
if (singleIndex > doubleIndex) {
propInfo.value.type = 'double'
} else {
propInfo.value.type = 'single'
}
}
if (propInfo.value && propInfo.value.type === 'double') {
attrCode += ` ${propInfo.key}="${value}"`
} else if (value === '' || value === undefined || !value) {
attrCode += ` ${propInfo.key}`
} else {
attrCode += ` ${propInfo.key}='${value}'`
}
}
})
/**
* close element
*/
if (children === undefined) {
appendCode(`${attrCode}/>`)
// decIndent()
} else {
appendCode(`${attrCode}>`)
incIndent()
// children element
if (Array.isArray(children)) {
children.forEach((child) => {
if (Array.isArray(child)) {
child.forEach((subChild) => {
appendCode(renderFn(subChild, _fileInfo))
})
} else {
appendCode(renderFn(child, _fileInfo))
}
})
} else {
appendCode(children)
}
decIndent()
appendCode(`</${tagName}>`)
}
return code.replace(os.EOL + os.EOL, os.EOL)
function appendCode(appendChars) {
const isType = processMixTemplate('wx', _ast)
if (!isType) { return }
if (appendChars.trim().length === 0) {
return
}
if (appendChars.startsWith('<')) {
code += (appendChars.startsWith('</') ? os.EOL : '') + String(indentWidth) + appendChars
} else if (appendChars.endsWith('>')) {
code += appendChars + os.EOL
} else {
code += indentWidth + appendChars
}
}
}
}