transformSlot.ts 5.6 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1 2 3 4
import {
  AttributeNode,
  createCompilerError,
  createCompoundExpression,
fxy060608's avatar
fxy060608 已提交
5
  createSimpleExpression,
fxy060608's avatar
fxy060608 已提交
6 7 8
  DirectiveNode,
  ErrorCodes,
  ExpressionNode,
fxy060608's avatar
fxy060608 已提交
9
  findProp,
10
  isStaticArgOf,
fxy060608's avatar
fxy060608 已提交
11 12 13 14 15
  isStaticExp,
  NodeTypes,
  SimpleExpressionNode,
  SlotOutletNode,
} from '@vue/compiler-core'
fxy060608's avatar
fxy060608 已提交
16 17
import { camelize } from '@vue/shared'
import { dynamicSlotName, SLOT_DEFAULT_NAME } from '@dcloudio/uni-shared'
fxy060608's avatar
fxy060608 已提交
18 19
import { RENDER_SLOT } from '../runtimeHelpers'
import { genExpr } from '../codegen'
fxy060608's avatar
fxy060608 已提交
20
import { isScopedSlotVFor, isVForScope, TransformContext } from '../transform'
fxy060608's avatar
fxy060608 已提交
21
import { processProps } from './transformElement'
fxy060608's avatar
fxy060608 已提交
22
import { removeAttribute, rewriteExpression } from './utils'
fxy060608's avatar
fxy060608 已提交
23 24 25 26
import {
  createAttributeNode,
  createBindDirectiveNode,
} from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
27
import { DYNAMIC_SLOT } from '..'
fxy060608's avatar
fxy060608 已提交
28 29

export function rewriteSlot(node: SlotOutletNode, context: TransformContext) {
fxy060608's avatar
fxy060608 已提交
30
  let slotName: string | ExpressionNode = `"${SLOT_DEFAULT_NAME}"`
fxy060608's avatar
fxy060608 已提交
31 32
  let hasOtherDir = false
  const nonNameProps: (AttributeNode | DirectiveNode)[] = []
fxy060608's avatar
fxy060608 已提交
33
  const { props } = node
fxy060608's avatar
fxy060608 已提交
34 35 36 37
  // 默认插槽强制设置name
  if (!findProp(node, 'name')) {
    props.unshift(createAttributeNode('name', 'default'))
  }
fxy060608's avatar
fxy060608 已提交
38 39
  for (let i = 0; i < props.length; i++) {
    const p = props[i]
fxy060608's avatar
fxy060608 已提交
40 41 42
    if (p.type === NodeTypes.ATTRIBUTE) {
      if (p.value) {
        if (p.name === 'name') {
fxy060608's avatar
fxy060608 已提交
43
          p.value.content = dynamicSlotName(p.value.content)
fxy060608's avatar
fxy060608 已提交
44 45 46 47 48 49 50 51 52 53
          slotName = JSON.stringify(p.value.content)
        } else {
          p.name = camelize(p.name)
          nonNameProps.push(p)
        }
      }
    } else {
      if (p.name !== 'bind') {
        hasOtherDir = true
      }
54
      if (p.name === 'bind' && isStaticArgOf(p.arg, 'name')) {
fxy060608's avatar
fxy060608 已提交
55
        if (p.exp) {
56 57
          const slotKey = parseScopedSlotKey(context)
          // renderSlot 第三个参数已经传了 slotKey
fxy060608's avatar
fxy060608 已提交
58 59 60 61 62
          slotName = createCompoundExpression([
            context.helperString(DYNAMIC_SLOT) + '(',
            p.exp,
            ')',
          ])
63 64 65 66 67 68 69 70 71
          p.exp = rewriteExpression(
            createCompoundExpression([
              context.helperString(DYNAMIC_SLOT) + '(',
              p.exp,
              slotKey ? `+'-'+` + slotKey : '',
              ')',
            ]),
            context
          )
fxy060608's avatar
fxy060608 已提交
72
        }
fxy060608's avatar
fxy060608 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
      } else {
        if (p.name === 'bind' && p.arg && isStaticExp(p.arg)) {
          p.arg.content = camelize(p.arg.content)
        }
        nonNameProps.push(p)
      }
    }
  }
  if (hasOtherDir) {
    context.onError(
      createCompilerError(
        ErrorCodes.X_V_SLOT_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
        node.loc
      )
    )
  }
  if (nonNameProps.length > 0) {
    processProps(node, context, nonNameProps)
    const properties: string[] = []
    nonNameProps.forEach((prop) => {
      if (prop.type === NodeTypes.DIRECTIVE && prop.name === 'bind') {
        const property = transformProperty(prop, context)
        property && properties.push(property)
      }
    })
    if (properties.length) {
fxy060608's avatar
fxy060608 已提交
99 100
      transformScopedSlotName(node, context)
      const vForIndexAlias = parseVForIndexAlias(context)
fxy060608's avatar
fxy060608 已提交
101 102 103
      rewriteExpression(
        createCompoundExpression([
          context.helperString(RENDER_SLOT) + '(',
fxy060608's avatar
fxy060608 已提交
104
          slotName,
fxy060608's avatar
fxy060608 已提交
105 106
          ',',
          `{${properties.join(',')}}`,
fxy060608's avatar
fxy060608 已提交
107
          `${vForIndexAlias ? ',' + parseScopedSlotKey(context) : ''}`,
fxy060608's avatar
fxy060608 已提交
108 109 110 111
          ')',
        ]),
        context
      )
fxy060608's avatar
fxy060608 已提交
112 113 114 115 116 117 118 119 120 121
    } else {
      // 非作用域默认插槽直接移除命名
      if (slotName === `"${SLOT_DEFAULT_NAME}"`) {
        removeAttribute(node, 'name')
      }
    }
  } else {
    // 非作用域默认插槽直接移除命名
    if (slotName === `"${SLOT_DEFAULT_NAME}"`) {
      removeAttribute(node, 'name')
fxy060608's avatar
fxy060608 已提交
122 123 124 125
    }
  }
}

fxy060608's avatar
fxy060608 已提交
126 127 128 129 130 131 132 133 134 135 136
function parseVForIndexAlias(context: TransformContext) {
  let { currentScope } = context
  while (currentScope) {
    if (isVForScope(currentScope) && !isScopedSlotVFor(currentScope)) {
      return currentScope.indexAlias
    }
    currentScope = currentScope.parent!
  }
}

function transformScopedSlotName(
fxy060608's avatar
fxy060608 已提交
137 138 139 140 141 142 143
  node: SlotOutletNode,
  context: TransformContext
) {
  if (!context.miniProgram.slot.dynamicSlotNames) {
    return
  }
  const slotKey = parseScopedSlotKey(context)
fxy060608's avatar
fxy060608 已提交
144 145 146
  if (!slotKey) {
    return
  }
fxy060608's avatar
fxy060608 已提交
147
  const nameProps = findProp(node, 'name')
fxy060608's avatar
fxy060608 已提交
148 149 150 151 152
  if (nameProps && nameProps.type === NodeTypes.ATTRIBUTE && nameProps.value) {
    const { props } = node
    props.splice(
      props.indexOf(nameProps),
      1,
153
      createScopedSlotDirectiveNode(nameProps.value.content, slotKey, context)
fxy060608's avatar
fxy060608 已提交
154
    )
fxy060608's avatar
fxy060608 已提交
155 156 157
  }
}

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
export interface NameScopedSlotDirectiveNode extends DirectiveNode {
  slotName: string
}

function createScopedSlotDirectiveNode(
  name: string,
  slotKey: string,
  context: TransformContext
): NameScopedSlotDirectiveNode {
  const dir = createBindDirectiveNode(
    'name',
    rewriteExpression(createSimpleExpression(`"${name}-"+` + slotKey), context)
      .content
  ) as NameScopedSlotDirectiveNode
  // 存储原始的 slot 名称
  dir.slotName = name
  return dir
}

fxy060608's avatar
fxy060608 已提交
177 178
function parseScopedSlotKey(context: TransformContext) {
  let { currentScope } = context
179
  const indexes: string[] = []
fxy060608's avatar
fxy060608 已提交
180 181
  while (currentScope) {
    if (isVForScope(currentScope)) {
182
      indexes.push(currentScope.indexAlias)
fxy060608's avatar
fxy060608 已提交
183 184 185
    }
    currentScope = currentScope.parent!
  }
186
  const inFor = !!indexes.length
fxy060608's avatar
fxy060608 已提交
187
  if (inFor) {
188
    return indexes.reverse().join(`+'-'+`)
fxy060608's avatar
fxy060608 已提交
189 190 191
  }
}

fxy060608's avatar
fxy060608 已提交
192 193 194 195 196 197 198 199 200 201 202 203 204
function transformProperty(dir: DirectiveNode, context: TransformContext) {
  if (!dir.arg || !dir.exp) {
    return
  }

  const isStaticArg =
    dir.arg.type === NodeTypes.SIMPLE_EXPRESSION && dir.arg.isStatic

  if (isStaticArg) {
    return `${(dir.arg as SimpleExpressionNode).content}:${genExpr(dir.exp)}`
  }
  return `[${genExpr(dir.arg)}||'']:${genExpr(dir.exp)}`
}