2019-09-23 08:55:18 +08:00
|
|
|
import {
|
|
|
|
createStructuralDirectiveTransform,
|
2019-10-02 00:25:13 +08:00
|
|
|
traverseChildren,
|
|
|
|
TransformContext
|
2019-09-23 08:55:18 +08:00
|
|
|
} from '../transform'
|
2019-09-18 07:08:47 +08:00
|
|
|
import {
|
|
|
|
NodeTypes,
|
|
|
|
ElementTypes,
|
|
|
|
ElementNode,
|
|
|
|
DirectiveNode,
|
2019-09-27 23:42:02 +08:00
|
|
|
IfBranchNode,
|
2019-10-02 00:25:13 +08:00
|
|
|
SimpleExpressionNode,
|
|
|
|
createSequenceExpression,
|
|
|
|
createCallExpression,
|
|
|
|
createConditionalExpression,
|
|
|
|
ConditionalExpression,
|
|
|
|
CallExpression,
|
|
|
|
createSimpleExpression,
|
|
|
|
JSChildNode,
|
|
|
|
ObjectExpression,
|
|
|
|
createObjectProperty,
|
2019-10-02 03:04:58 +08:00
|
|
|
Property,
|
2019-10-02 11:19:48 +08:00
|
|
|
ExpressionNode
|
2019-09-18 07:08:47 +08:00
|
|
|
} from '../ast'
|
|
|
|
import { createCompilerError, ErrorCodes } from '../errors'
|
2019-09-24 08:45:40 +08:00
|
|
|
import { processExpression } from './transformExpression'
|
2019-10-02 00:25:13 +08:00
|
|
|
import {
|
|
|
|
OPEN_BLOCK,
|
|
|
|
CREATE_BLOCK,
|
|
|
|
EMPTY,
|
|
|
|
FRAGMENT,
|
2019-10-02 03:04:58 +08:00
|
|
|
APPLY_DIRECTIVES,
|
|
|
|
MERGE_PROPS
|
2019-10-02 00:25:13 +08:00
|
|
|
} from '../runtimeConstants'
|
|
|
|
import { isString } from '@vue/shared'
|
2019-09-18 07:08:47 +08:00
|
|
|
|
2019-09-22 05:42:12 +08:00
|
|
|
export const transformIf = createStructuralDirectiveTransform(
|
2019-09-18 07:08:47 +08:00
|
|
|
/^(if|else|else-if)$/,
|
|
|
|
(node, dir, context) => {
|
2019-10-02 00:25:13 +08:00
|
|
|
if (
|
|
|
|
dir.name !== 'else' &&
|
|
|
|
(!dir.exp || !(dir.exp as SimpleExpressionNode).content.trim())
|
|
|
|
) {
|
|
|
|
const loc = dir.exp ? dir.exp.loc : node.loc
|
|
|
|
context.onError(createCompilerError(ErrorCodes.X_IF_NO_EXPRESSION, loc))
|
|
|
|
dir.exp = createSimpleExpression(`true`, false, loc)
|
|
|
|
}
|
|
|
|
|
2019-09-24 01:29:41 +08:00
|
|
|
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
|
2019-09-27 23:42:02 +08:00
|
|
|
// dir.exp can only be simple expression because vIf transform is applied
|
|
|
|
// before expression transform.
|
2019-10-02 00:25:13 +08:00
|
|
|
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
|
2019-09-24 01:25:18 +08:00
|
|
|
}
|
2019-10-02 00:25:13 +08:00
|
|
|
|
2019-09-18 07:08:47 +08:00
|
|
|
if (dir.name === 'if') {
|
2019-10-02 03:04:58 +08:00
|
|
|
const branch = createIfBranch(node, dir)
|
2019-10-02 00:25:13 +08:00
|
|
|
const codegenNode = createSequenceExpression([
|
|
|
|
createCallExpression(context.helper(OPEN_BLOCK))
|
|
|
|
])
|
2019-09-18 07:08:47 +08:00
|
|
|
context.replaceNode({
|
|
|
|
type: NodeTypes.IF,
|
|
|
|
loc: node.loc,
|
2019-10-02 03:04:58 +08:00
|
|
|
branches: [branch],
|
2019-10-02 00:25:13 +08:00
|
|
|
codegenNode
|
2019-09-18 07:08:47 +08:00
|
|
|
})
|
2019-10-02 00:25:13 +08:00
|
|
|
|
|
|
|
// Exit callback. Complete the codegenNode when all children have been
|
|
|
|
// transformed.
|
|
|
|
return () => {
|
|
|
|
codegenNode.expressions.push(
|
2019-10-02 04:48:20 +08:00
|
|
|
createCodegenNodeForBranch(branch, 0, context)
|
2019-10-02 00:25:13 +08:00
|
|
|
)
|
|
|
|
}
|
2019-09-18 07:08:47 +08:00
|
|
|
} else {
|
|
|
|
// locate the adjacent v-if
|
2019-10-01 02:51:55 +08:00
|
|
|
const siblings = context.parent!.children
|
2019-09-20 03:41:17 +08:00
|
|
|
const comments = []
|
2019-09-23 14:52:54 +08:00
|
|
|
let i = siblings.indexOf(node as any)
|
2019-09-20 03:41:17 +08:00
|
|
|
while (i-- >= -1) {
|
2019-09-18 07:08:47 +08:00
|
|
|
const sibling = siblings[i]
|
2019-09-20 03:41:17 +08:00
|
|
|
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
|
|
|
|
context.removeNode(sibling)
|
|
|
|
comments.unshift(sibling)
|
2019-09-18 07:08:47 +08:00
|
|
|
continue
|
|
|
|
}
|
2019-09-20 03:41:17 +08:00
|
|
|
if (sibling && sibling.type === NodeTypes.IF) {
|
2019-09-18 07:08:47 +08:00
|
|
|
// move the node to the if node's branches
|
|
|
|
context.removeNode()
|
2019-09-26 07:17:45 +08:00
|
|
|
const branch = createIfBranch(node, dir)
|
2019-09-20 03:41:17 +08:00
|
|
|
if (__DEV__ && comments.length) {
|
|
|
|
branch.children = [...comments, ...branch.children]
|
|
|
|
}
|
|
|
|
sibling.branches.push(branch)
|
2019-09-23 08:55:18 +08:00
|
|
|
// since the branch was removed, it will not be traversed.
|
|
|
|
// make sure to traverse here.
|
|
|
|
traverseChildren(branch, context)
|
2019-10-02 00:25:13 +08:00
|
|
|
// attach this branch's codegen node to the v-if root.
|
|
|
|
let parentCondition = sibling.codegenNode
|
|
|
|
.expressions[1] as ConditionalExpression
|
|
|
|
while (true) {
|
|
|
|
if (
|
|
|
|
parentCondition.alternate.type ===
|
|
|
|
NodeTypes.JS_CONDITIONAL_EXPRESSION
|
|
|
|
) {
|
|
|
|
parentCondition = parentCondition.alternate
|
|
|
|
} else {
|
|
|
|
parentCondition.alternate = createCodegenNodeForBranch(
|
2019-10-02 03:04:58 +08:00
|
|
|
branch,
|
2019-10-02 00:25:13 +08:00
|
|
|
sibling.branches.length - 1,
|
|
|
|
context
|
|
|
|
)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 07:08:47 +08:00
|
|
|
} else {
|
2019-09-22 05:42:12 +08:00
|
|
|
context.onError(
|
2019-10-03 05:18:11 +08:00
|
|
|
createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, node.loc)
|
2019-09-18 07:08:47 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2019-09-26 07:17:45 +08:00
|
|
|
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
2019-09-18 07:08:47 +08:00
|
|
|
return {
|
|
|
|
type: NodeTypes.IF_BRANCH,
|
|
|
|
loc: node.loc,
|
|
|
|
condition: dir.name === 'else' ? undefined : dir.exp,
|
2019-09-26 07:17:45 +08:00
|
|
|
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
|
2019-09-18 07:08:47 +08:00
|
|
|
}
|
|
|
|
}
|
2019-10-02 00:25:13 +08:00
|
|
|
|
|
|
|
function createCodegenNodeForBranch(
|
2019-10-02 03:04:58 +08:00
|
|
|
branch: IfBranchNode,
|
2019-10-02 00:25:13 +08:00
|
|
|
index: number,
|
|
|
|
context: TransformContext
|
|
|
|
): ConditionalExpression | CallExpression {
|
2019-10-02 03:04:58 +08:00
|
|
|
if (branch.condition) {
|
2019-10-02 00:25:13 +08:00
|
|
|
return createConditionalExpression(
|
2019-10-02 03:04:58 +08:00
|
|
|
branch.condition,
|
2019-10-02 04:48:20 +08:00
|
|
|
createChildrenCodegenNode(branch, index, context),
|
2019-10-02 00:25:13 +08:00
|
|
|
createCallExpression(context.helper(CREATE_BLOCK), [
|
|
|
|
context.helper(EMPTY)
|
|
|
|
])
|
|
|
|
)
|
|
|
|
} else {
|
2019-10-02 04:48:20 +08:00
|
|
|
return createChildrenCodegenNode(branch, index, context)
|
2019-10-02 00:25:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function createChildrenCodegenNode(
|
2019-10-02 03:04:58 +08:00
|
|
|
branch: IfBranchNode,
|
2019-10-02 00:25:13 +08:00
|
|
|
index: number,
|
|
|
|
{ helper }: TransformContext
|
|
|
|
): CallExpression {
|
|
|
|
const keyExp = `{ key: ${index} }`
|
2019-10-02 04:48:20 +08:00
|
|
|
const { children } = branch
|
|
|
|
const child = children[0]
|
|
|
|
const needFragmentWrapper =
|
2019-10-02 11:53:52 +08:00
|
|
|
children.length !== 1 ||
|
|
|
|
child.type !== NodeTypes.ELEMENT ||
|
|
|
|
child.tagType === ElementTypes.SLOT
|
2019-10-02 04:48:20 +08:00
|
|
|
if (needFragmentWrapper) {
|
2019-10-02 11:19:48 +08:00
|
|
|
const blockArgs: CallExpression['arguments'] = [
|
|
|
|
helper(FRAGMENT),
|
|
|
|
keyExp,
|
|
|
|
children
|
|
|
|
]
|
2019-10-02 11:53:52 +08:00
|
|
|
if (children.length === 1) {
|
|
|
|
// optimize away nested fragments when child is a ForNode
|
|
|
|
if (child.type === NodeTypes.FOR) {
|
2019-10-02 22:47:01 +08:00
|
|
|
const forBlockArgs = (child.codegenNode
|
|
|
|
.expressions[1] as CallExpression).arguments
|
2019-10-02 11:53:52 +08:00
|
|
|
// directly use the for block's children and patchFlag
|
|
|
|
blockArgs[2] = forBlockArgs[2]
|
|
|
|
blockArgs[3] = forBlockArgs[3]
|
|
|
|
} else if (
|
|
|
|
child.type === NodeTypes.ELEMENT &&
|
|
|
|
child.tagType === ElementTypes.SLOT
|
|
|
|
) {
|
|
|
|
// <template v-if="..."><slot/></template>
|
|
|
|
// since slot always returns array, use it directly as the fragment children.
|
|
|
|
blockArgs[2] = child.codegenNode!
|
|
|
|
}
|
2019-10-02 04:48:20 +08:00
|
|
|
}
|
2019-10-02 11:19:48 +08:00
|
|
|
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
2019-10-02 00:25:13 +08:00
|
|
|
} else {
|
2019-10-02 04:48:20 +08:00
|
|
|
const childCodegen = (child as ElementNode).codegenNode!
|
|
|
|
let vnodeCall = childCodegen
|
|
|
|
if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) {
|
|
|
|
vnodeCall = vnodeCall.arguments[0] as CallExpression
|
2019-10-02 00:25:13 +08:00
|
|
|
}
|
|
|
|
// change child to a block
|
2019-10-02 04:48:20 +08:00
|
|
|
vnodeCall.callee = helper(CREATE_BLOCK)
|
2019-10-02 00:25:13 +08:00
|
|
|
// branch key
|
2019-10-02 04:48:20 +08:00
|
|
|
const existingProps = vnodeCall.arguments[1]
|
2019-10-02 00:25:13 +08:00
|
|
|
if (!existingProps || existingProps === `null`) {
|
2019-10-02 04:48:20 +08:00
|
|
|
vnodeCall.arguments[1] = keyExp
|
2019-10-02 00:25:13 +08:00
|
|
|
} else {
|
|
|
|
// inject branch key if not already have a key
|
2019-10-02 03:04:58 +08:00
|
|
|
const props = existingProps as
|
|
|
|
| CallExpression
|
|
|
|
| ObjectExpression
|
|
|
|
| ExpressionNode
|
2019-10-02 00:25:13 +08:00
|
|
|
if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
|
|
|
|
// merged props... add ours
|
|
|
|
// only inject key to object literal if it's the first argument so that
|
|
|
|
// if doesn't override user provided keys
|
|
|
|
const first = props.arguments[0] as string | JSChildNode
|
|
|
|
if (!isString(first) && first.type === NodeTypes.JS_OBJECT_EXPRESSION) {
|
|
|
|
first.properties.unshift(createKeyProperty(index))
|
|
|
|
} else {
|
|
|
|
props.arguments.unshift(keyExp)
|
|
|
|
}
|
2019-10-02 03:04:58 +08:00
|
|
|
} else if (props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
|
2019-10-02 00:25:13 +08:00
|
|
|
props.properties.unshift(createKeyProperty(index))
|
2019-10-02 03:04:58 +08:00
|
|
|
} else {
|
|
|
|
// single v-bind with expression
|
2019-10-02 04:48:20 +08:00
|
|
|
vnodeCall.arguments[1] = createCallExpression(helper(MERGE_PROPS), [
|
2019-10-02 03:04:58 +08:00
|
|
|
keyExp,
|
|
|
|
props
|
|
|
|
])
|
2019-10-02 00:25:13 +08:00
|
|
|
}
|
|
|
|
}
|
2019-10-02 04:48:20 +08:00
|
|
|
return childCodegen
|
2019-10-02 00:25:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function createKeyProperty(index: number): Property {
|
2019-10-03 11:10:41 +08:00
|
|
|
return createObjectProperty(`key`, createSimpleExpression(index + '', false))
|
2019-10-02 00:25:13 +08:00
|
|
|
}
|