You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
7.6 KiB
268 lines
7.6 KiB
const t = require('@babel/types')
|
|
|
|
const {
|
|
METHOD_CREATE_ELEMENT,
|
|
METHOD_TO_STRING,
|
|
METHOD_RENDER_LIST,
|
|
METHOD_BUILT_IN,
|
|
METHOD_RESOLVE_FILTER,
|
|
IDENTIFIER_FILTER,
|
|
IDENTIFIER_METHOD,
|
|
IDENTIFIER_GLOBAL
|
|
} = require('../../constants')
|
|
|
|
const {
|
|
getTagName
|
|
} = require('../../h5')
|
|
|
|
const {
|
|
hasOwn,
|
|
hyphenate,
|
|
traverseFilter,
|
|
getComponentName
|
|
} = require('../../util')
|
|
|
|
const traverseData = require('./data')
|
|
const traverseRenderList = require('./render-list')
|
|
|
|
const getMemberExpr = require('./member-expr')
|
|
|
|
function addStaticClass (path, staticClass) {
|
|
const dataPath = path.get('arguments.1')
|
|
if (dataPath && dataPath.isObjectExpression()) {
|
|
const staticClassProperty = dataPath.node.properties.find(property => property.key.name === 'staticClass')
|
|
if (staticClassProperty) { // update
|
|
staticClassProperty.value.value = staticClassProperty.value.value + ' ' + staticClass
|
|
} else { // add
|
|
dataPath.node.properties.push(
|
|
t.objectProperty(t.identifier('staticClass'), t.stringLiteral(staticClass))
|
|
)
|
|
}
|
|
} else { // {staticClass:'data-v-aaa'}
|
|
const args = path.node.arguments
|
|
args.splice(1, 0, t.objectExpression(
|
|
[
|
|
t.objectProperty(t.identifier('staticClass'), t.stringLiteral(staticClass))
|
|
]
|
|
))
|
|
}
|
|
}
|
|
|
|
function addVueId (path, state) {
|
|
// const platformName = state.options.platform.name
|
|
// if ( // 暂不对 mp-weixin,app-plus 增加 vueId
|
|
// platformName === 'mp-weixin' ||
|
|
// platformName === 'app-plus'
|
|
// ) {
|
|
// return
|
|
// }
|
|
if (!hasOwn(state.options, '$vueId')) {
|
|
state.options.$vueId = 1
|
|
}
|
|
const hashId = state.options.hashId
|
|
const vueId = (hashId ? (hashId + '-') : '') + (state.options.$vueId++)
|
|
|
|
let value
|
|
|
|
if (state.scoped.length) {
|
|
const scopeds = state.scoped
|
|
const len = scopeds.length
|
|
if (len > 1) { // v-for 嵌套,forIndex 不允许重复
|
|
const forIndexSet = new Set()
|
|
for (let i = 0; i < len; i++) {
|
|
const scoped = scopeds[i]
|
|
forIndexSet.add(scoped.forIndex)
|
|
if (forIndexSet.size !== i + 1) {
|
|
state.errors.add(`v-for 嵌套时,索引名称 ${scoped.forIndex} 不允许重复`)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
for (let i = len - 1; i >= 0; i--) {
|
|
const scoped = scopeds[i]
|
|
if (!value) {
|
|
value = t.binaryExpression('+', t.stringLiteral(vueId + '-'), t.identifier(scoped.forIndex))
|
|
} else {
|
|
value = t.binaryExpression('+',
|
|
t.binaryExpression('+', value, t.stringLiteral('-')),
|
|
t.identifier(scoped.forIndex)
|
|
)
|
|
}
|
|
}
|
|
} else {
|
|
value = t.stringLiteral(vueId)
|
|
}
|
|
|
|
const objectProperty = t.objectProperty(
|
|
t.stringLiteral('vue-id'),
|
|
value
|
|
)
|
|
|
|
const dataPath = path.get('arguments.1')
|
|
if (dataPath && dataPath.isObjectExpression()) {
|
|
const attrsProperty = dataPath.node.properties.find(property => property.key.name === 'attrs')
|
|
if (attrsProperty) {
|
|
attrsProperty.value.properties.unshift(objectProperty)
|
|
} else {
|
|
dataPath.node.properties.push(
|
|
t.objectProperty(t.identifier('attrs'), t.objectExpression([
|
|
objectProperty
|
|
]))
|
|
)
|
|
}
|
|
} else { // {attrs:{'vue-id':'2'}}
|
|
const args = path.node.arguments
|
|
args.splice(1, 0, t.objectExpression(
|
|
[
|
|
t.objectProperty(t.identifier('attrs'), t.objectExpression([
|
|
objectProperty
|
|
]))
|
|
]
|
|
))
|
|
}
|
|
}
|
|
|
|
function checkUsingGlobalComponents (name, globalUsingComponents, state) {
|
|
if (globalUsingComponents && globalUsingComponents[name]) {
|
|
if (!state.options.usingGlobalComponents) {
|
|
state.options.usingGlobalComponents = Object.create(null)
|
|
}
|
|
state.options.usingGlobalComponents[name] = globalUsingComponents[name]
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
noScope: false,
|
|
MemberExpression (path) {
|
|
if ( // t.m(123)
|
|
t.isIdentifier(path.node.object) &&
|
|
this.options.filterModules.includes(path.node.object.name)
|
|
) {
|
|
path.skip()
|
|
}
|
|
},
|
|
CallExpression (path) {
|
|
const callee = path.node.callee
|
|
if (traverseFilter(callee, this)) {
|
|
return path.skip()
|
|
} else if (t.isIdentifier(callee)) {
|
|
const methodName = callee.name
|
|
switch (methodName) {
|
|
case METHOD_CREATE_ELEMENT:
|
|
{
|
|
const tagNode = path.node.arguments[0]
|
|
if (t.isStringLiteral(tagNode)) {
|
|
// 需要把标签增加到 class 样式中
|
|
const tagName = getTagName(tagNode.value)
|
|
if (tagName !== tagNode.value) {
|
|
addStaticClass(path, '_' + tagNode.value)
|
|
}
|
|
tagNode.value = getComponentName(hyphenate(tagName))
|
|
|
|
// 组件增加 vueId
|
|
if (this.options.platform.isComponent(tagNode.value)) {
|
|
addVueId(path, this)
|
|
}
|
|
|
|
// 查找全局组件
|
|
checkUsingGlobalComponents(
|
|
tagNode.value,
|
|
this.options.globalUsingComponents,
|
|
this
|
|
)
|
|
}
|
|
if (this.options.scopeId) {
|
|
addStaticClass(path, this.options.scopeId)
|
|
}
|
|
|
|
const dataPath = path.get('arguments.1')
|
|
dataPath && dataPath.isObjectExpression() && traverseData(dataPath, this, tagNode.value)
|
|
}
|
|
break
|
|
case METHOD_TO_STRING:
|
|
{
|
|
const stringNodes = path.node.arguments[0]
|
|
stringNodes.$toString = true
|
|
path.replaceWith(stringNodes)
|
|
}
|
|
break
|
|
case METHOD_RENDER_LIST:
|
|
traverseRenderList(path, this)
|
|
path.skip()
|
|
break
|
|
default:
|
|
// TODO 检测是否是 filterModules
|
|
if (!METHOD_BUILT_IN.includes(methodName)) {
|
|
if (
|
|
path.findParent(
|
|
path =>
|
|
path.isObjectProperty() && ['on', 'nativeOn'].includes(path.node.key.name)
|
|
)
|
|
) {
|
|
// event
|
|
return path.skip()
|
|
}
|
|
|
|
path.replaceWith(
|
|
getMemberExpr(
|
|
path,
|
|
methodName === METHOD_RESOLVE_FILTER
|
|
? IDENTIFIER_FILTER
|
|
: IDENTIFIER_METHOD,
|
|
path.node,
|
|
this
|
|
)
|
|
)
|
|
}
|
|
break
|
|
}
|
|
} else if (
|
|
t.isCallExpression(callee) &&
|
|
t.isIdentifier(callee.callee) &&
|
|
callee.callee.name === METHOD_RESOLVE_FILTER
|
|
) {
|
|
// multi filter
|
|
path.replaceWith(getMemberExpr(path, IDENTIFIER_FILTER, path.node, this))
|
|
} else if (
|
|
t.isMemberExpression(callee) // message.split('').reverse().join('')
|
|
) {
|
|
// Object.assign...
|
|
path.replaceWith(getMemberExpr(path, IDENTIFIER_GLOBAL, path.node, this))
|
|
}
|
|
},
|
|
TemplateLiteral (path) {
|
|
const nodes = []
|
|
const expressions = path.get('expressions')
|
|
|
|
let index = 0
|
|
for (const elem of path.node.quasis) {
|
|
if (elem.value.cooked) {
|
|
nodes.push(t.stringLiteral(elem.value.cooked))
|
|
}
|
|
|
|
if (index < expressions.length) {
|
|
const expr = expressions[index++]
|
|
const node = expr.node
|
|
if (!t.isStringLiteral(node, {
|
|
value: ''
|
|
})) {
|
|
nodes.push(node)
|
|
}
|
|
}
|
|
}
|
|
|
|
// since `+` is left-to-right associative
|
|
// ensure the first node is a string if first/second isn't
|
|
const considerSecondNode = !t.isStringLiteral(nodes[1])
|
|
if (!t.isStringLiteral(nodes[0]) && considerSecondNode) {
|
|
nodes.unshift(t.stringLiteral(''))
|
|
}
|
|
let root = nodes[0]
|
|
|
|
for (let i = 1; i < nodes.length; i++) {
|
|
root = t.binaryExpression('+', root, nodes[i])
|
|
}
|
|
|
|
path.replaceWith(root)
|
|
}
|
|
}
|
|
|