2019-09-23 00:55:18 +00:00
|
|
|
import {
|
|
|
|
createStructuralDirectiveTransform,
|
2020-02-14 20:57:14 +00:00
|
|
|
TransformContext,
|
|
|
|
traverseNode
|
2019-09-23 00:55:18 +00:00
|
|
|
} from '../transform'
|
2019-09-17 23:08:47 +00:00
|
|
|
import {
|
|
|
|
NodeTypes,
|
|
|
|
ElementTypes,
|
|
|
|
ElementNode,
|
|
|
|
DirectiveNode,
|
2019-09-27 15:42:02 +00:00
|
|
|
IfBranchNode,
|
2019-10-01 16:25:13 +00:00
|
|
|
SimpleExpressionNode,
|
|
|
|
createCallExpression,
|
|
|
|
createConditionalExpression,
|
|
|
|
createSimpleExpression,
|
|
|
|
createObjectProperty,
|
2019-10-06 02:47:20 +00:00
|
|
|
createObjectExpression,
|
|
|
|
IfConditionalExpression,
|
|
|
|
BlockCodegenNode,
|
2020-02-11 23:12:56 +00:00
|
|
|
IfNode,
|
|
|
|
createVNodeCall
|
2019-09-17 23:08:47 +00:00
|
|
|
} from '../ast'
|
|
|
|
import { createCompilerError, ErrorCodes } from '../errors'
|
2019-09-24 00:45:40 +00:00
|
|
|
import { processExpression } from './transformExpression'
|
2019-10-01 16:25:13 +00:00
|
|
|
import {
|
|
|
|
CREATE_BLOCK,
|
|
|
|
FRAGMENT,
|
2020-02-11 23:12:56 +00:00
|
|
|
CREATE_COMMENT,
|
|
|
|
OPEN_BLOCK
|
2019-10-05 21:18:25 +00:00
|
|
|
} from '../runtimeHelpers'
|
2019-10-03 16:03:14 +00:00
|
|
|
import { injectProp } from '../utils'
|
2019-09-17 23:08:47 +00:00
|
|
|
|
2019-09-21 21:42:12 +00:00
|
|
|
export const transformIf = createStructuralDirectiveTransform(
|
2019-09-17 23:08:47 +00:00
|
|
|
/^(if|else|else-if)$/,
|
|
|
|
(node, dir, context) => {
|
2020-02-06 02:04:40 +00:00
|
|
|
return processIf(node, dir, context, (ifNode, branch, isRoot) => {
|
2019-10-01 16:25:13 +00:00
|
|
|
// Exit callback. Complete the codegenNode when all children have been
|
|
|
|
// transformed.
|
|
|
|
return () => {
|
2020-02-03 20:51:41 +00:00
|
|
|
if (isRoot) {
|
2020-02-11 23:12:56 +00:00
|
|
|
ifNode.codegenNode = createCodegenNodeForBranch(
|
|
|
|
branch,
|
|
|
|
0,
|
|
|
|
context
|
|
|
|
) as IfConditionalExpression
|
2020-02-03 20:51:41 +00:00
|
|
|
} else {
|
2019-10-01 16:25:13 +00:00
|
|
|
// attach this branch's codegen node to the v-if root.
|
2020-02-03 20:51:41 +00:00
|
|
|
let parentCondition = ifNode.codegenNode!
|
2020-01-31 14:43:34 +00:00
|
|
|
while (
|
|
|
|
parentCondition.alternate.type ===
|
|
|
|
NodeTypes.JS_CONDITIONAL_EXPRESSION
|
|
|
|
) {
|
|
|
|
parentCondition = parentCondition.alternate
|
2019-10-01 16:25:13 +00:00
|
|
|
}
|
2020-01-31 14:43:34 +00:00
|
|
|
parentCondition.alternate = createCodegenNodeForBranch(
|
|
|
|
branch,
|
2020-02-03 20:51:41 +00:00
|
|
|
ifNode.branches.length - 1,
|
2020-01-31 14:43:34 +00:00
|
|
|
context
|
|
|
|
)
|
2019-09-17 23:08:47 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-03 20:51:41 +00:00
|
|
|
})
|
2019-09-17 23:08:47 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-02-03 22:47:06 +00:00
|
|
|
// target-agnostic transform used for both Client and SSR
|
2020-02-06 02:04:40 +00:00
|
|
|
export function processIf(
|
2020-02-03 20:51:41 +00:00
|
|
|
node: ElementNode,
|
|
|
|
dir: DirectiveNode,
|
|
|
|
context: TransformContext,
|
|
|
|
processCodegen?: (
|
|
|
|
node: IfNode,
|
|
|
|
branch: IfBranchNode,
|
|
|
|
isRoot: boolean
|
2020-02-03 22:47:06 +00:00
|
|
|
) => (() => void) | undefined
|
|
|
|
) {
|
2020-02-03 20:51:41 +00: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_V_IF_NO_EXPRESSION, dir.loc)
|
|
|
|
)
|
|
|
|
dir.exp = createSimpleExpression(`true`, false, loc)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
|
|
|
|
// dir.exp can only be simple expression because vIf transform is applied
|
|
|
|
// before expression transform.
|
|
|
|
dir.exp = processExpression(dir.exp as SimpleExpressionNode, context)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir.name === 'if') {
|
|
|
|
const branch = createIfBranch(node, dir)
|
|
|
|
const ifNode: IfNode = {
|
|
|
|
type: NodeTypes.IF,
|
|
|
|
loc: node.loc,
|
|
|
|
branches: [branch]
|
|
|
|
}
|
|
|
|
context.replaceNode(ifNode)
|
|
|
|
if (processCodegen) {
|
|
|
|
return processCodegen(ifNode, branch, true)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// locate the adjacent v-if
|
|
|
|
const siblings = context.parent!.children
|
|
|
|
const comments = []
|
|
|
|
let i = siblings.indexOf(node)
|
|
|
|
while (i-- >= -1) {
|
|
|
|
const sibling = siblings[i]
|
|
|
|
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
|
|
|
|
context.removeNode(sibling)
|
|
|
|
comments.unshift(sibling)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if (sibling && sibling.type === NodeTypes.IF) {
|
|
|
|
// move the node to the if node's branches
|
|
|
|
context.removeNode()
|
|
|
|
const branch = createIfBranch(node, dir)
|
|
|
|
if (__DEV__ && comments.length) {
|
|
|
|
branch.children = [...comments, ...branch.children]
|
|
|
|
}
|
|
|
|
sibling.branches.push(branch)
|
|
|
|
const onExit = processCodegen && processCodegen(sibling, branch, false)
|
|
|
|
// since the branch was removed, it will not be traversed.
|
|
|
|
// make sure to traverse here.
|
2020-02-14 20:57:14 +00:00
|
|
|
traverseNode(branch, context)
|
2020-02-03 20:51:41 +00:00
|
|
|
// call on exit
|
|
|
|
if (onExit) onExit()
|
|
|
|
// make sure to reset currentNode after traversal to indicate this
|
|
|
|
// node has been removed.
|
|
|
|
context.currentNode = null
|
|
|
|
} else {
|
|
|
|
context.onError(
|
|
|
|
createCompilerError(ErrorCodes.X_V_ELSE_NO_ADJACENT_IF, node.loc)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 23:17:45 +00:00
|
|
|
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
|
2019-09-17 23:08:47 +00:00
|
|
|
return {
|
|
|
|
type: NodeTypes.IF_BRANCH,
|
|
|
|
loc: node.loc,
|
|
|
|
condition: dir.name === 'else' ? undefined : dir.exp,
|
2019-09-25 23:17:45 +00:00
|
|
|
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
|
2019-09-17 23:08:47 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-01 16:25:13 +00:00
|
|
|
|
|
|
|
function createCodegenNodeForBranch(
|
2019-10-01 19:04:58 +00:00
|
|
|
branch: IfBranchNode,
|
2019-10-01 16:25:13 +00:00
|
|
|
index: number,
|
|
|
|
context: TransformContext
|
2019-10-06 02:47:20 +00:00
|
|
|
): IfConditionalExpression | BlockCodegenNode {
|
2019-10-01 19:04:58 +00:00
|
|
|
if (branch.condition) {
|
2019-10-01 16:25:13 +00:00
|
|
|
return createConditionalExpression(
|
2019-10-01 19:04:58 +00:00
|
|
|
branch.condition,
|
2019-10-01 20:48:20 +00:00
|
|
|
createChildrenCodegenNode(branch, index, context),
|
2019-10-25 01:19:02 +00:00
|
|
|
// make sure to pass in asBlock: true so that the comment node call
|
|
|
|
// closes the current block.
|
|
|
|
createCallExpression(context.helper(CREATE_COMMENT), [
|
|
|
|
__DEV__ ? '"v-if"' : '""',
|
|
|
|
'true'
|
|
|
|
])
|
2019-10-06 02:47:20 +00:00
|
|
|
) as IfConditionalExpression
|
2019-10-01 16:25:13 +00:00
|
|
|
} else {
|
2020-02-11 23:12:56 +00:00
|
|
|
return createChildrenCodegenNode(branch, index, context)
|
2019-10-01 16:25:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function createChildrenCodegenNode(
|
2019-10-01 19:04:58 +00:00
|
|
|
branch: IfBranchNode,
|
2019-10-01 16:25:13 +00:00
|
|
|
index: number,
|
2019-10-03 16:03:14 +00:00
|
|
|
context: TransformContext
|
2020-02-11 23:12:56 +00:00
|
|
|
): BlockCodegenNode {
|
2019-10-03 16:03:14 +00:00
|
|
|
const { helper } = context
|
|
|
|
const keyProperty = createObjectProperty(
|
|
|
|
`key`,
|
|
|
|
createSimpleExpression(index + '', false)
|
|
|
|
)
|
2019-10-01 20:48:20 +00:00
|
|
|
const { children } = branch
|
2020-02-03 20:51:41 +00:00
|
|
|
const firstChild = children[0]
|
2019-10-01 20:48:20 +00:00
|
|
|
const needFragmentWrapper =
|
2020-02-03 20:51:41 +00:00
|
|
|
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
|
2019-10-01 20:48:20 +00:00
|
|
|
if (needFragmentWrapper) {
|
2020-02-03 20:51:41 +00:00
|
|
|
if (children.length === 1 && firstChild.type === NodeTypes.FOR) {
|
2019-10-02 03:53:52 +00:00
|
|
|
// optimize away nested fragments when child is a ForNode
|
2020-02-11 23:12:56 +00:00
|
|
|
const vnodeCall = firstChild.codegenNode!
|
|
|
|
injectProp(vnodeCall, keyProperty, context)
|
|
|
|
return vnodeCall
|
|
|
|
} else {
|
|
|
|
return createVNodeCall(
|
|
|
|
context,
|
|
|
|
helper(FRAGMENT),
|
|
|
|
createObjectExpression([keyProperty]),
|
|
|
|
children,
|
|
|
|
undefined,
|
|
|
|
undefined,
|
|
|
|
undefined,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
branch.loc
|
|
|
|
)
|
2019-10-01 20:48:20 +00:00
|
|
|
}
|
2019-10-01 16:25:13 +00:00
|
|
|
} else {
|
2020-02-11 23:12:56 +00:00
|
|
|
const vnodeCall = (firstChild as ElementNode)
|
|
|
|
.codegenNode as BlockCodegenNode
|
2019-10-03 16:03:14 +00:00
|
|
|
// Change createVNode to createBlock.
|
2020-02-11 23:12:56 +00:00
|
|
|
if (vnodeCall.type === NodeTypes.VNODE_CALL) {
|
|
|
|
vnodeCall.isBlock = true
|
|
|
|
helper(OPEN_BLOCK)
|
|
|
|
helper(CREATE_BLOCK)
|
2019-10-01 16:25:13 +00:00
|
|
|
}
|
2019-10-03 16:03:14 +00:00
|
|
|
// inject branch key
|
2019-10-08 14:50:00 +00:00
|
|
|
injectProp(vnodeCall, keyProperty, context)
|
2020-02-11 23:12:56 +00:00
|
|
|
return vnodeCall
|
2019-10-01 16:25:13 +00:00
|
|
|
}
|
|
|
|
}
|