codegen.ts 5.0 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
import { hyphenate } from '@vue/shared'
fxy060608's avatar
fxy060608 已提交
2
import { formatMiniProgramEvent } from '@dcloudio/uni-cli-shared'
fxy060608's avatar
fxy060608 已提交
3 4 5 6
import {
  AttributeNode,
  DirectiveNode,
  ElementNode,
fxy060608's avatar
fxy060608 已提交
7
  ElementTypes,
fxy060608's avatar
fxy060608 已提交
8
  ExpressionNode,
fxy060608's avatar
fxy060608 已提交
9
  findProp,
fxy060608's avatar
fxy060608 已提交
10 11 12 13 14 15 16
  NodeTypes,
  RootNode,
  SimpleExpressionNode,
  TemplateChildNode,
  TextNode,
} from '@vue/compiler-core'
import { TemplateCodegenOptions } from '../options'
fxy060608's avatar
fxy060608 已提交
17
import { genExpr } from '../codegen'
fxy060608's avatar
fxy060608 已提交
18 19
import { isForElementNode, VForOptions } from '../transforms/vFor'
import { IfElementNode, isIfElementNode } from '../transforms/vIf'
fxy060608's avatar
fxy060608 已提交
20 21
interface TemplateCodegenContext {
  code: string
fxy060608's avatar
fxy060608 已提交
22
  directive: string
fxy060608's avatar
fxy060608 已提交
23 24 25 26 27
  push(code: string): void
}

export function generate(
  { children }: RootNode,
fxy060608's avatar
fxy060608 已提交
28
  { emitFile, filename, directive }: TemplateCodegenOptions
fxy060608's avatar
fxy060608 已提交
29 30 31
) {
  const context: TemplateCodegenContext = {
    code: '',
fxy060608's avatar
fxy060608 已提交
32
    directive,
fxy060608's avatar
fxy060608 已提交
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    push(code) {
      context.code += code
    },
  }
  children.forEach((node) => {
    genNode(node, context)
  })
  emitFile({ type: 'asset', fileName: filename, source: context.code })
}

export function genNode(
  node: TemplateChildNode,
  context: TemplateCodegenContext
) {
  switch (node.type) {
fxy060608's avatar
fxy060608 已提交
48 49 50 51
    case NodeTypes.IF:
      return node.branches.forEach((node) => {
        genElement(node as unknown as IfElementNode, context)
      })
fxy060608's avatar
fxy060608 已提交
52 53 54 55 56 57 58 59 60 61 62 63 64 65
    case NodeTypes.TEXT:
      return genText(node, context)
    case NodeTypes.INTERPOLATION:
      return genExpression(node.content, context)
    case NodeTypes.ELEMENT:
      return genElement(node, context)
  }
}

function genText(node: TextNode, { push }: TemplateCodegenContext) {
  push(node.content)
}

function genExpression(node: ExpressionNode, { push }: TemplateCodegenContext) {
fxy060608's avatar
fxy060608 已提交
66
  push(`{{${genExpr(node)}}}`)
fxy060608's avatar
fxy060608 已提交
67 68
}

fxy060608's avatar
fxy060608 已提交
69 70
function genVIf(exp: string, { push, directive }: TemplateCodegenContext) {
  push(` ${directive}if="{{${exp}}}"`)
fxy060608's avatar
fxy060608 已提交
71
}
fxy060608's avatar
fxy060608 已提交
72 73
function genVElseIf(exp: string, { push, directive }: TemplateCodegenContext) {
  push(` ${directive}elif="{{${exp}}}"`)
fxy060608's avatar
fxy060608 已提交
74
}
fxy060608's avatar
fxy060608 已提交
75 76
function genVElse({ push, directive }: TemplateCodegenContext) {
  push(` ${directive}else`)
fxy060608's avatar
fxy060608 已提交
77 78 79
}

function genVFor(
fxy060608's avatar
fxy060608 已提交
80 81
  { sourceAlias, valueAlias, keyAlias }: VForOptions,
  node: ElementNode,
fxy060608's avatar
fxy060608 已提交
82
  { push, directive }: TemplateCodegenContext
fxy060608's avatar
fxy060608 已提交
83
) {
fxy060608's avatar
fxy060608 已提交
84
  push(` ${directive}for="{{${sourceAlias}}}"`)
fxy060608's avatar
fxy060608 已提交
85
  if (valueAlias) {
fxy060608's avatar
fxy060608 已提交
86
    push(` ${directive}for-item="${valueAlias}"`)
fxy060608's avatar
fxy060608 已提交
87
  }
fxy060608's avatar
fxy060608 已提交
88 89 90
  const keyProp = findProp(node, 'key', true)
  if (keyProp) {
    const key = ((keyProp as DirectiveNode).exp as SimpleExpressionNode).content
fxy060608's avatar
fxy060608 已提交
91
    push(` ${directive}key="${key.includes('.') ? key.split('.')[1] : key}"`)
fxy060608's avatar
fxy060608 已提交
92
    node.props.splice(node.props.indexOf(keyProp), 1)
fxy060608's avatar
fxy060608 已提交
93 94
  }
}
fxy060608's avatar
fxy060608 已提交
95 96 97
const tagMap: Record<string, string> = {
  template: 'block',
}
fxy060608's avatar
fxy060608 已提交
98
export function genElement(node: ElementNode, context: TemplateCodegenContext) {
fxy060608's avatar
fxy060608 已提交
99
  const { children, isSelfClosing, props } = node
fxy060608's avatar
fxy060608 已提交
100 101 102 103
  let tag = tagMap[node.tag] || node.tag
  if (node.tagType === ElementTypes.COMPONENT) {
    tag = hyphenate(tag)
  }
fxy060608's avatar
fxy060608 已提交
104 105
  const { push } = context
  push(`<${tag}`)
fxy060608's avatar
fxy060608 已提交
106 107 108 109 110 111 112
  if (isIfElementNode(node)) {
    const { name, condition } = node.vIf
    if (name === 'if') {
      genVIf(condition!, context)
    } else if (name === 'else-if') {
      genVElseIf(condition!, context)
    } else if (name === 'else') {
fxy060608's avatar
fxy060608 已提交
113 114 115
      genVElse(context)
    }
  }
fxy060608's avatar
fxy060608 已提交
116
  if (isForElementNode(node)) {
fxy060608's avatar
fxy060608 已提交
117
    genVFor(node.vFor, node, context)
fxy060608's avatar
fxy060608 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  }
  if (props.length) {
    genElementProps(props, context)
  }

  if (isSelfClosing) {
    push(`/>`)
  } else {
    push(`>`)
    children.forEach((node) => {
      genNode(node, context)
    })
    push(`</${tag}>`)
  }
}

export function genElementProps(
  props: Array<AttributeNode | DirectiveNode>,
  context: TemplateCodegenContext
) {
  const { push } = context
  props.forEach((prop) => {
    if (prop.type === NodeTypes.ATTRIBUTE) {
fxy060608's avatar
fxy060608 已提交
141 142 143 144 145 146
      const { value } = prop
      if (value) {
        context.push(` ${prop.name}="${value.content}"`)
      } else {
        context.push(` ${prop.name}`)
      }
fxy060608's avatar
fxy060608 已提交
147 148
    } else {
      const { name } = prop
fxy060608's avatar
fxy060608 已提交
149 150 151 152
      push(` `)
      if (name === 'on') {
        genOn(prop, context)
      } else {
fxy060608's avatar
fxy060608 已提交
153 154 155 156 157
        genDirectiveNode(prop, context)
      }
    }
  })
}
fxy060608's avatar
fxy060608 已提交
158 159 160 161 162 163 164 165 166 167 168
function genOn(prop: DirectiveNode, { push }: TemplateCodegenContext) {
  const arg = (prop.arg as SimpleExpressionNode).content
  const exp = (prop.exp as SimpleExpressionNode).content
  const modifiers = prop.modifiers
  push(
    `${formatMiniProgramEvent(arg, {
      isCatch: modifiers.includes('stop') || modifiers.includes('prevent'),
      isCapture: modifiers.includes('capture'),
    })}="{{${exp}}}"`
  )
}
fxy060608's avatar
fxy060608 已提交
169 170 171 172 173

function genDirectiveNode(
  prop: DirectiveNode,
  { push }: TemplateCodegenContext
) {
fxy060608's avatar
fxy060608 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
  if (prop.name === 'slot') {
    if (prop.arg) {
      push(`slot="${(prop.arg as SimpleExpressionNode).content}"`)
    }
  } else if (prop.name === 'model') {
    // TODO
  } else if (prop.name === 'show') {
    push(`hidden="{{!${(prop.exp as SimpleExpressionNode).content}}}"`)
  } else if (prop.arg && prop.exp) {
    const arg = (prop.arg as SimpleExpressionNode).content
    const exp = (prop.exp as SimpleExpressionNode).content
    push(`${arg}="{{${exp}}}"`)
  } else {
    throw new Error(`unknown directive` + JSON.stringify(prop))
  }
fxy060608's avatar
fxy060608 已提交
189
}