const { parseExpression } = require('@babel/parser') const t = require('@babel/types') const ID = '_i' const ITERATOR1 = '$1' const ITERATOR2 = '$2' const ITERATOR3 = '$3' const SET_DATA = '_$s' const GET_DATA = '_$g' const SET_MP_CLASS = '_$smc' const GET_CHANGE_DATA = '_$gc' // wxs const C_IS = 'is' const C_SLOT_TARGET = 'st' const C_REF = 'ref' const C_NAME = 'name' const V_FOR = 'f' const V_IF = 'i' const V_ELSE_IF = 'e' // web components const elements = ['uni-view'] function isVar (str) { if (!str) { return false } const expr = parseExpression(str) if ( t.isStringLiteral(expr) || t.isNumericLiteral(expr) || t.isBooleanLiteral(expr) || t.isNullLiteral(expr) ) { return false } return true } function addRawAttr (el, name, value) { el.attrsMap[name] = value el.attrsList.push({ name, value }) } function updateEleId (el, it, state) { if (el.type !== 1) { return } const newId = getNewId(el.attrsMap[ID], it) addRawAttr(el, ID, newId) if (el.attrs) { const attr = el.attrs.find(attr => attr.name === ID) attr.value = newId } el.children.forEach(child => { if (!child.for) { // 忽略嵌套 for updateEleId(child, it) } else { child.$parentIterator3 = (child.$parentIterator3 ? (child.$parentIterator3 + '+') : '') + it child.forId = `${child.forId}+'-'+${it}` } }) el.ifConditions && el.ifConditions.forEach((con, index) => { index !== 0 && updateEleId(con.block, it, state) }) el.scopedSlots && Object.values(el.scopedSlots).forEach((slot, index) => { updateEleId(slot, it, state) }) } function getBindingAttr (el, name) { return getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name) } function getAndRemoveAttr (el, name) { let val if ((val = el.attrsMap[name]) != null) { const list = el.attrsList for (let i = 0, l = list.length; i < l; i++) { if (list[i].name === name) { list.splice(i, 1) break } } } delete el.attrsMap[name] return val } function updateForIterator (el, state) { if (!el.for) { return } // 简单处理,确保所有 for 循环,均包含 1,2,3 const forIteratorId = state.forIteratorId++ if (!el.iterator1) { el.iterator1 = ITERATOR1 + forIteratorId } if (!el.iterator2) { el.iterator2 = ITERATOR2 + forIteratorId } if (!el.iterator3) { el.iterator3 = ITERATOR3 + forIteratorId } } function updateForEleId (el, state) { updateForIterator(el, state) if (el.for) { const it = el.$parentIterator3 ? (el.$parentIterator3 + '+' + "'-'" + '+' + el.iterator3) : el.iterator3 updateEleId(el, it, state) } } function getNewId (id, it) { return Number.isInteger(id) ? `("${id}-"+${it})` : `(${id}+${it})` } function updateScopedSlotEleId (el, state) { // TODO 暂不考虑 scopedSlot 嵌套情况 if (el.slotScope) { const getNewId = function (id, it) { return Number.isInteger(id) ? `("${id}-"+${it})` : `(${id}+"-"+${it})` } const updateEleId = function (el) { if (el.type !== 1) { return } const it = '_si' const newId = getNewId(el.attrsMap[ID], it) if (el.forId) { el.forId = getNewId(el.forId, it) } addRawAttr(el, ID, newId) if (el.attrs) { const attr = el.attrs.find(attr => attr.name === ID) attr.value = newId } el.children.forEach(child => { if (!child.slotScope) { // 忽略嵌套 scopedSlot updateEleId(child, state) } }) } if (el.tag === 'template' && el.slotTarget) { // new v-slot el.children.forEach(child => { if (!child.slotScope) { // 忽略嵌套 scopedSlot updateEleId(child, state) } }) } else { // old slot-scope updateEleId(el) } } } function getForEl (el) { if (el.for) { return el } if (el.parent && el.parent.for && (el.parent.tag === 'template' || el.parent.tag === 'block')) { return el.parent } } function processForKey (el) { const forEl = getForEl(el) if (forEl && !el.key) { // 占位的 text 标签也无需添加 key if (!isVar(forEl.for)) { // return } const it = forEl.iterator3 if (forEl.tag === 'template' || forEl.tag === 'block') { if (forEl !== el) { const keyIndex = forEl.children.indexOf(el) el.key = `${forEl.forId}+'-${keyIndex}'+${it}` } else { // 当 template 下只有文本节点 if ( el.children && el.children.length && !el.children.find(child => child.type === 1) ) { el.children[0].parent = el if (!el.children.find(child => child.key)) { el.children[0].key = `${forEl.forId}+'-0'+${it}` } return true } } } else { el.key = `${forEl.forId}+'-'+${it}` } } } function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) } function traverseNode (el, parent, state, isScopedSlot) { state.transformNode(el, parent, state, isScopedSlot) el.children && el.children.forEach((child, index) => { state.childIndex = index traverseNode(child, el, state, isScopedSlot) }) el.ifConditions && el.ifConditions.forEach((con, index) => { if (index !== 0) { state.childIndex = index traverseNode(con.block, el, state, isScopedSlot) } }) el.scopedSlots && Object.values(el.scopedSlots).forEach((slot, index) => { state.childIndex = index slot.slotScope = `${slot.slotScope}, _svm, _si` if (slot.slotTargetDynamic && slot.slotTarget) { slot.slotTarget = state.createGenVar(slot.attrsMap[ID])(C_SLOT_TARGET, slot.slotTarget) } traverseNode(slot, el, state, true) }) } function addAttr (el, name, value, dynamic) { const attrs = dynamic ? (el.dynamicAttrs || (el.dynamicAttrs = [])) : (el.attrs || (el.attrs = [])) attrs.push({ name, value, dynamic }) el.plain = false } function removeRawAttr (el, name) { delete el.attrsMap[name] const index = el.attrsList.findIndex(attr => attr.name === name) index !== -1 && el.attrsList.splice(index, 1) } function removeRawBindingAttr (el, name) { removeRawAttr(el, ':' + name) removeRawAttr(el, 'v-bind:' + name) } function addHandler (el, name, value, important) { const events = el.events || (el.events = {}) const handlers = events[name] const newHandler = { value: value.trim(), dynamic: undefined } if (Array.isArray(handlers)) { important ? handlers.unshift(newHandler) : handlers.push(newHandler) } else if (handlers) { events[name] = important ? [newHandler, handlers] : [handlers, newHandler] } else { events[name] = newHandler } el.plain = false } module.exports = { C_IS, C_REF, C_NAME, V_FOR, V_IF, V_ELSE_IF, ID, SET_DATA, GET_DATA, SET_MP_CLASS, GET_CHANGE_DATA, elements, isVar, hasOwn, addAttr, addRawAttr, removeRawAttr, removeRawBindingAttr, getNewId, getForEl, addHandler, processForKey, updateForEleId, updateScopedSlotEleId, getBindingAttr, getAndRemoveAttr, traverseNode }