const t = require('@babel/types') const traverse = require('@babel/traverse').default const { VAR_ROOT, IDENTIFIER_METHOD, IDENTIFIER_FILTER, IDENTIFIER_GLOBAL, METHOD_RENDER_LIST } = require('../../constants') function isMatch (name, forItem, forIndex) { return name === forItem || name === forIndex } function findScoped (path, test, state) { if (!path) { return state } const scoped = state.scoped.find(scoped => { const { forItem, forIndex, path: listPath } = scoped const funPath = path.findParent(path => path.isFunctionExpression() && t.isCallExpression(path.parentPath) && path.parentPath.node.callee.name === METHOD_RENDER_LIST ) if (funPath && funPath.parentPath === listPath) { // TODO 为兼容历史结构仅在当前 list 父级存在 v-if 返回 const parent = listPath.findParent(path => path.isFunctionExpression() || path.isConditionalExpression()) if (parent && parent.isConditionalExpression()) { return true } } else { return false } let match = false path.traverse({ noScope: true, Identifier (path) { if (!match && path.key !== 'key' && (path.key !== 'property' || path.parent.computed)) { match = isMatch(path.node.name, forItem, forIndex) if (match) { path.stop() } } } }) if (!match && test) { traverse(t.arrayExpression([test]), { noScope: true, Identifier (path) { if (!match && path.key !== 'key' && (path.key !== 'property' || path.parent.computed)) { const node = path.node match = isMatch(node.name, forItem, forIndex) || scoped.declarationArray.find(({ declarations }) => declarations.find(({ id }) => id === node)) if (match) { path.stop() } } } }) } return match }) if (!scoped && state.scoped.length > 1) { return state.scoped[1] // 取父 } return scoped || state } function findTest (path, state) { let tests if (path) { while (path.parentPath && path.key !== 'body') { if (path.key === 'consequent' || path.key === 'alternate') { const testOrig = path.container.test let test = t.arrayExpression([t.cloneDeep(testOrig)]) traverse(test, { noScope: true, MemberExpression (memberExpressionPath) { const names = state.scoped.map(scoped => scoped.forItem) const node = memberExpressionPath.node const objectName = node.object.name const property = node.property const propertyName = property.name if (objectName === VAR_ROOT || (names.includes(objectName) && (propertyName === IDENTIFIER_METHOD || propertyName === IDENTIFIER_FILTER || propertyName === IDENTIFIER_GLOBAL))) { const array = [] let tempPath = memberExpressionPath while (tempPath.parentPath) { const key = tempPath.key array.unshift(typeof key === 'number' ? `[${key}]` : `.${key}`) tempPath = tempPath.parentPath } memberExpressionPath.replaceWith(path.parentPath.get('test' + array.join('')).node.property) } } }) test = test.elements[0] if (path.key === 'alternate') { test = t.unaryExpression('!', test) } tests = tests ? t.logicalExpression('&&', test, tests) : test } path = path.parentPath } } return tests } module.exports = function getMemberExpr (path, name, init, state, variableDeclaration = true) { const test = findTest(path, state) const scoped = findScoped(path, test, state) if (!variableDeclaration) { scoped.declarationArray.push(t.expressionStatement(init)) return } const identifier = t.identifier(name) scoped.propertyArray.push(t.objectProperty(identifier, identifier)) scoped.declarationArray.push( t.variableDeclaration('var', [t.variableDeclarator(identifier, test ? t.conditionalExpression(test, init, t.nullLiteral()) : init)]) ) state.identifierArray.push(identifier) const contextIdentifier = t.identifier(scoped.context) contextIdentifier.$mpProcessed = true return t.memberExpression(contextIdentifier, identifier) }