import { isString } from '@vue/shared'
import { ForParseResult } from './transforms/vFor'
import {
- CREATE_VNODE,
- WITH_DIRECTIVES,
RENDER_SLOT,
CREATE_SLOTS,
RENDER_LIST,
OPEN_BLOCK,
CREATE_BLOCK,
- FRAGMENT
+ FRAGMENT,
+ CREATE_VNODE,
+ WITH_DIRECTIVES
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
-import { ImportItem } from './transform'
+import { ImportItem, TransformContext } from './transform'
// Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific
FOR,
TEXT_CALL,
// codegen
+ VNODE_CALL,
JS_CALL_EXPRESSION,
JS_OBJECT_EXPRESSION,
JS_PROPERTY,
JS_ARRAY_EXPRESSION,
JS_FUNCTION_EXPRESSION,
- JS_SEQUENCE_EXPRESSION,
JS_CONDITIONAL_EXPRESSION,
JS_CACHE_EXPRESSION,
isSelfClosing: boolean
props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
- codegenNode:
- | CallExpression
- | SimpleExpressionNode
- | CacheExpression
- | SequenceExpression
- | undefined
}
export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT
codegenNode:
- | ElementCodegenNode
+ | VNodeCall
| SimpleExpressionNode // when hoisted
| CacheExpression // when cached by v-once
- | SequenceExpression // when turned into a block
| undefined
ssrCodegenNode?: TemplateLiteral
}
export interface ComponentNode extends BaseElementNode {
tagType: ElementTypes.COMPONENT
codegenNode:
- | ComponentCodegenNode
+ | VNodeCall
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: CallExpression
export interface SlotOutletNode extends BaseElementNode {
tagType: ElementTypes.SLOT
- codegenNode: SlotOutletCodegenNode | undefined | CacheExpression // when cached by v-once
+ codegenNode:
+ | RenderSlotCall
+ | CacheExpression // when cached by v-once
+ | undefined
ssrCodegenNode?: CallExpression
}
export interface TemplateNode extends BaseElementNode {
tagType: ElementTypes.TEMPLATE
// TemplateNode is a container type that always gets compiled away
+ codegenNode: undefined
}
export interface TextNode extends Node {
export interface IfNode extends Node {
type: NodeTypes.IF
branches: IfBranchNode[]
- codegenNode?: IfCodegenNode
+ codegenNode?: IfConditionalExpression
}
export interface IfBranchNode extends Node {
codegenNode: CallExpression | SimpleExpressionNode // when hoisted
}
+export type TemplateTextChildNode =
+ | TextNode
+ | InterpolationNode
+ | CompoundExpressionNode
+
+export interface VNodeCall extends Node {
+ type: NodeTypes.VNODE_CALL
+ tag: string | symbol | CallExpression
+ props: PropsExpression | undefined
+ children:
+ | TemplateChildNode[] // multiple children
+ | TemplateTextChildNode // single text child
+ | SlotsExpression // component slots
+ | ForRenderListExpression // v-for fragment call
+ | undefined
+ patchFlag: string | undefined
+ dynamicProps: string | undefined
+ directives: DirectiveArguments | undefined
+ isBlock: boolean
+ isForBlock: boolean
+}
+
// JS Node Types ---------------------------------------------------------------
// We also include a number of JavaScript AST nodes for code generation.
// Vue render function generation.
export type JSChildNode =
+ | VNodeCall
| CallExpression
| ObjectExpression
| ArrayExpression
| ExpressionNode
| FunctionExpression
| ConditionalExpression
- | SequenceExpression
| CacheExpression
| AssignmentExpression
isSlot: boolean
}
-export interface SequenceExpression extends Node {
- type: NodeTypes.JS_SEQUENCE_EXPRESSION
- expressions: JSChildNode[]
-}
-
export interface ConditionalExpression extends Node {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION
test: JSChildNode
// Codegen Node Types ----------------------------------------------------------
-// createVNode(...)
-export interface PlainElementCodegenNode extends CallExpression {
- callee: typeof CREATE_VNODE | typeof CREATE_BLOCK
- arguments: // tag, props, children, patchFlag, dynamicProps
- | [string | symbol]
- | [string | symbol, PropsExpression]
- | [string | symbol, 'null' | PropsExpression, TemplateChildNode[]]
- | [
- string | symbol,
- 'null' | PropsExpression,
- 'null' | TemplateChildNode[],
- string
- ]
- | [
- string | symbol,
- 'null' | PropsExpression,
- 'null' | TemplateChildNode[],
- string,
- string
- ]
+export interface DirectiveArguments extends ArrayExpression {
+ elements: DirectiveArgumentNode[]
}
-export type ElementCodegenNode =
- | PlainElementCodegenNode
- | CodegenNodeWithDirective<PlainElementCodegenNode>
+export interface DirectiveArgumentNode extends ArrayExpression {
+ elements: // dir, exp, arg, modifiers
+ | [string]
+ | [string, ExpressionNode]
+ | [string, ExpressionNode, ExpressionNode]
+ | [string, ExpressionNode, ExpressionNode, ObjectExpression]
+}
-// createVNode(...)
-export interface PlainComponentCodegenNode extends CallExpression {
- callee: typeof CREATE_VNODE | typeof CREATE_BLOCK
- arguments: // Comp, props, slots, patchFlag, dynamicProps
- | [string | symbol]
- | [string | symbol, PropsExpression]
- | [string | symbol, 'null' | PropsExpression, SlotsExpression]
- | [
- string | symbol,
- 'null' | PropsExpression,
- 'null' | SlotsExpression,
- string
- ]
+// renderSlot(...)
+export interface RenderSlotCall extends CallExpression {
+ callee: typeof RENDER_SLOT
+ arguments: // $slots, name, props, fallback
+ | [string, string | ExpressionNode]
+ | [string, string | ExpressionNode, PropsExpression]
| [
- string | symbol,
- 'null' | PropsExpression,
- 'null' | SlotsExpression,
string,
- string
+ string | ExpressionNode,
+ PropsExpression | '{}',
+ TemplateChildNode[]
]
}
-export type ComponentCodegenNode =
- | PlainComponentCodegenNode
- | CodegenNodeWithDirective<PlainComponentCodegenNode>
-
export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
// { foo: () => [...] }
value: SlotFunctionExpression
}
-// withDirectives(createVNode(...), [
-// [_directive_foo, someValue],
-// [_directive_bar, someValue, "arg", { mod: true }]
-// ])
-export interface CodegenNodeWithDirective<T extends CallExpression>
- extends CallExpression {
- callee: typeof WITH_DIRECTIVES
- arguments: [T, DirectiveArguments]
-}
-
-export interface DirectiveArguments extends ArrayExpression {
- elements: DirectiveArgumentNode[]
-}
-
-export interface DirectiveArgumentNode extends ArrayExpression {
- elements: // dir, exp, arg, modifiers
- | [string]
- | [string, ExpressionNode]
- | [string, ExpressionNode, ExpressionNode]
- | [string, ExpressionNode, ExpressionNode, ObjectExpression]
-}
-
-// renderSlot(...)
-export interface SlotOutletCodegenNode extends CallExpression {
- callee: typeof RENDER_SLOT
- arguments: // $slots, name, props, fallback
- | [string, string | ExpressionNode]
- | [string, string | ExpressionNode, PropsExpression]
- | [
- string,
- string | ExpressionNode,
- PropsExpression | '{}',
- TemplateChildNode[]
- ]
-}
-
-export type BlockCodegenNode =
- | ElementCodegenNode
- | ComponentCodegenNode
- | SlotOutletCodegenNode
-
-export interface IfCodegenNode extends SequenceExpression {
- expressions: [OpenBlockExpression, IfConditionalExpression]
-}
+export type BlockCodegenNode = VNodeCall | RenderSlotCall
export interface IfConditionalExpression extends ConditionalExpression {
consequent: BlockCodegenNode
alternate: BlockCodegenNode | IfConditionalExpression
}
-export interface ForCodegenNode extends SequenceExpression {
- expressions: [OpenBlockExpression, ForBlockCodegenNode]
-}
-
-export interface ForBlockCodegenNode extends CallExpression {
- callee: typeof CREATE_BLOCK
- arguments: [typeof FRAGMENT, 'null', ForRenderListExpression, string]
+export interface ForCodegenNode extends VNodeCall {
+ isBlock: true
+ tag: typeof FRAGMENT
+ props: undefined
+ children: ForRenderListExpression
+ patchFlag: string
+ isForBlock: true
}
export interface ForRenderListExpression extends CallExpression {
returns: BlockCodegenNode
}
-export interface OpenBlockExpression extends CallExpression {
- callee: typeof OPEN_BLOCK
- arguments: []
-}
-
// AST Utilities ---------------------------------------------------------------
// Some expressions, e.g. sequence and conditional expressions, are never
}
}
+export function createVNodeCall(
+ context: TransformContext,
+ tag: VNodeCall['tag'],
+ props?: VNodeCall['props'],
+ children?: VNodeCall['children'],
+ patchFlag?: VNodeCall['patchFlag'],
+ dynamicProps?: VNodeCall['dynamicProps'],
+ directives?: VNodeCall['directives'],
+ isBlock: VNodeCall['isBlock'] = false,
+ isForBlock: VNodeCall['isForBlock'] = false,
+ loc = locStub
+): VNodeCall {
+ if (isBlock) {
+ context.helper(OPEN_BLOCK)
+ context.helper(CREATE_BLOCK)
+ } else {
+ context.helper(CREATE_VNODE)
+ }
+ if (directives) {
+ context.helper(WITH_DIRECTIVES)
+ }
+
+ return {
+ type: NodeTypes.VNODE_CALL,
+ tag,
+ props,
+ children,
+ patchFlag,
+ dynamicProps,
+ directives,
+ isBlock,
+ isForBlock,
+ loc
+ }
+}
+
export function createArrayExpression(
elements: ArrayExpression['elements'],
loc: SourceLocation = locStub
}
}
-type InferCodegenNodeType<T> = T extends
- | typeof CREATE_VNODE
- | typeof CREATE_BLOCK
- ? PlainElementCodegenNode | PlainComponentCodegenNode
- : T extends typeof WITH_DIRECTIVES
- ?
- | CodegenNodeWithDirective<PlainElementCodegenNode>
- | CodegenNodeWithDirective<PlainComponentCodegenNode>
- : T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
+type InferCodegenNodeType<T> = T extends typeof RENDER_SLOT
+ ? RenderSlotCall
+ : CallExpression
export function createCallExpression<T extends CallExpression['callee']>(
callee: T,
}
}
-export function createSequenceExpression(
- expressions: SequenceExpression['expressions']
-): SequenceExpression {
- return {
- type: NodeTypes.JS_SEQUENCE_EXPRESSION,
- expressions,
- loc: locStub
- }
-}
-
export function createConditionalExpression(
test: ConditionalExpression['test'],
consequent: ConditionalExpression['consequent'],
CompoundExpressionNode,
SimpleExpressionNode,
FunctionExpression,
- SequenceExpression,
ConditionalExpression,
CacheExpression,
locStub,
TemplateLiteral,
IfStatement,
AssignmentExpression,
- ReturnStatement
+ ReturnStatement,
+ VNodeCall
} from './ast'
import { SourceMapGenerator, RawSourceMap } from 'source-map'
import {
CREATE_TEXT,
PUSH_SCOPE_ID,
POP_SCOPE_ID,
- WITH_SCOPE_ID
+ WITH_SCOPE_ID,
+ WITH_DIRECTIVES,
+ CREATE_BLOCK,
+ OPEN_BLOCK
} from './runtimeHelpers'
import { ImportItem } from './transform'
case NodeTypes.COMMENT:
genComment(node, context)
break
+ case NodeTypes.VNODE_CALL:
+ genVNodeCall(node, context)
+ break
+
case NodeTypes.JS_CALL_EXPRESSION:
genCallExpression(node, context)
break
case NodeTypes.JS_FUNCTION_EXPRESSION:
genFunctionExpression(node, context)
break
- case NodeTypes.JS_SEQUENCE_EXPRESSION:
- genSequenceExpression(node, context)
- break
case NodeTypes.JS_CONDITIONAL_EXPRESSION:
genConditionalExpression(node, context)
break
}
}
+function genVNodeCall(node: VNodeCall, context: CodegenContext) {
+ const { push, helper } = context
+ const {
+ tag,
+ props,
+ children,
+ patchFlag,
+ dynamicProps,
+ directives,
+ isBlock,
+ isForBlock
+ } = node
+ if (directives) {
+ push(helper(WITH_DIRECTIVES) + `(`)
+ }
+ if (isBlock) {
+ push(`(${helper(OPEN_BLOCK)}(${isForBlock ? `true` : ``}), `)
+ }
+ push(helper(isBlock ? CREATE_BLOCK : CREATE_VNODE) + `(`, node)
+ genNodeList(
+ genNullableArgs([tag, props, children, patchFlag, dynamicProps]),
+ context
+ )
+ push(`)`)
+ if (isBlock) {
+ push(`)`)
+ }
+ if (directives) {
+ push(`, `)
+ genNode(directives, context)
+ push(`)`)
+ }
+}
+
+function genNullableArgs(args: any[]): CallExpression['arguments'] {
+ let i = args.length
+ while (i--) {
+ if (args[i] != null) break
+ }
+ return args.slice(0, i + 1).map(arg => arg || `null`)
+}
+
// JavaScript
function genCallExpression(node: CallExpression, context: CodegenContext) {
const callee = isString(node.callee)
needNewline && deindent(true /* without newline */)
}
-function genSequenceExpression(
- node: SequenceExpression,
- context: CodegenContext
-) {
- context.push(`(`)
- genNodeList(node.expressions, context)
- context.push(`)`)
-}
-
function genCacheExpression(node: CacheExpression, context: CodegenContext) {
const { push, helper, indent, deindent, newline } = context
push(`_cache[${node.index}] || (`)
JSChildNode,
SimpleExpressionNode,
ElementTypes,
- ElementCodegenNode,
- ComponentCodegenNode,
- createCallExpression,
CacheExpression,
createCacheExpression,
- TemplateLiteral
+ TemplateLiteral,
+ createVNodeCall
} from './ast'
import {
isString,
TO_DISPLAY_STRING,
FRAGMENT,
helperNameMap,
- WITH_DIRECTIVES,
CREATE_BLOCK,
- CREATE_COMMENT
+ CREATE_COMMENT,
+ OPEN_BLOCK
} from './runtimeHelpers'
-import { isVSlot, createBlockExpression } from './utils'
+import { isVSlot } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
// There are two types of transforms:
if (isSingleElementRoot(root, child) && child.codegenNode) {
// single element root is never hoisted so codegenNode will never be
// SimpleExpressionNode
- const codegenNode = child.codegenNode as
- | ElementCodegenNode
- | ComponentCodegenNode
- | CacheExpression
- if (codegenNode.type !== NodeTypes.JS_CACHE_EXPRESSION) {
- if (codegenNode.callee === WITH_DIRECTIVES) {
- codegenNode.arguments[0].callee = helper(CREATE_BLOCK)
- } else {
- codegenNode.callee = helper(CREATE_BLOCK)
- }
- root.codegenNode = createBlockExpression(codegenNode, context)
- } else {
- root.codegenNode = codegenNode
+ const codegenNode = child.codegenNode
+ if (codegenNode.type === NodeTypes.VNODE_CALL) {
+ codegenNode.isBlock = true
+ helper(OPEN_BLOCK)
+ helper(CREATE_BLOCK)
}
+ root.codegenNode = codegenNode
} else {
// - single <slot/>, IfNode, ForNode: already blocks.
// - single text node: always patched.
}
} else if (children.length > 1) {
// root has multiple nodes - return a fragment block.
- root.codegenNode = createBlockExpression(
- createCallExpression(helper(CREATE_BLOCK), [
- helper(FRAGMENT),
- `null`,
- root.children,
- `${PatchFlags.STABLE_FRAGMENT} /* ${
- PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
- } */`
- ]),
- context
+ root.codegenNode = createVNodeCall(
+ context,
+ helper(FRAGMENT),
+ undefined,
+ root.children,
+ `${PatchFlags.STABLE_FRAGMENT} /* ${
+ PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
+ } */`,
+ undefined,
+ undefined,
+ true
)
} else {
// no children = noop. codegen will return null.
ComponentNode,
TemplateNode,
ElementNode,
- PlainElementCodegenNode,
- CodegenNodeWithDirective
+ VNodeCall
} from '../ast'
import { TransformContext } from '../transform'
-import { WITH_DIRECTIVES } from '../runtimeHelpers'
import { PatchFlags, isString, isSymbol } from '@vue/shared'
import { isSlotOutlet, findProp } from '../utils'
// node may contain dynamic children, but its props may be eligible for
// hoisting.
const codegenNode = child.codegenNode!
- if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) {
+ if (codegenNode.type === NodeTypes.VNODE_CALL) {
const flag = getPatchFlag(codegenNode)
if (
(!flag ||
!hasCachedProps(child)
) {
const props = getNodeProps(child)
- if (props && props !== `null`) {
- getVNodeCall(codegenNode).arguments[1] = context.hoist(props)
+ if (props) {
+ codegenNode.props = context.hoist(props)
}
}
}
return cached
}
const codegenNode = node.codegenNode!
- if (codegenNode.type !== NodeTypes.JS_CALL_EXPRESSION) {
+ if (codegenNode.type !== NodeTypes.VNODE_CALL) {
return false
}
const flag = getPatchFlag(codegenNode)
return false
}
}
+ // only svg/foeignObject could be block here, however if they are static
+ // then they don't need to be blocks since there will be no nested
+ // udpates.
+ if (codegenNode.isBlock) {
+ codegenNode.isBlock = false
+ }
resultCache.set(node, true)
return true
} else {
return false
}
const props = getNodeProps(node)
- if (
- props &&
- props !== 'null' &&
- props.type === NodeTypes.JS_OBJECT_EXPRESSION
- ) {
+ if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
const { properties } = props
for (let i = 0; i < properties.length; i++) {
if (properties[i].value.type === NodeTypes.JS_CACHE_EXPRESSION) {
function getNodeProps(node: PlainElementNode) {
const codegenNode = node.codegenNode!
- if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) {
- return getVNodeArgAt(
- codegenNode,
- 1
- ) as PlainElementCodegenNode['arguments'][1]
+ if (codegenNode.type === NodeTypes.VNODE_CALL) {
+ return codegenNode.props
}
}
-type NonCachedCodegenNode =
- | PlainElementCodegenNode
- | CodegenNodeWithDirective<PlainElementCodegenNode>
-
-function getVNodeArgAt(
- node: NonCachedCodegenNode,
- index: number
-): PlainElementCodegenNode['arguments'][number] {
- return getVNodeCall(node).arguments[index]
-}
-
-function getVNodeCall(node: NonCachedCodegenNode) {
- return node.callee === WITH_DIRECTIVES ? node.arguments[0] : node
-}
-
-function getPatchFlag(node: NonCachedCodegenNode): number | undefined {
- const flag = getVNodeArgAt(node, 3) as string
+function getPatchFlag(node: VNodeCall): number | undefined {
+ const flag = node.patchFlag
return flag ? parseInt(flag, 10) : undefined
}
createSimpleExpression,
createObjectExpression,
Property,
- createSequenceExpression,
- ComponentNode
+ ComponentNode,
+ VNodeCall,
+ TemplateTextChildNode,
+ DirectiveArguments,
+ createVNodeCall
} from '../ast'
import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
import {
- CREATE_VNODE,
- WITH_DIRECTIVES,
RESOLVE_DIRECTIVE,
RESOLVE_COMPONENT,
RESOLVE_DYNAMIC_COMPONENT,
MERGE_PROPS,
TO_HANDLERS,
PORTAL,
- KEEP_ALIVE,
- OPEN_BLOCK,
- CREATE_BLOCK
+ KEEP_ALIVE
} from '../runtimeHelpers'
import {
getInnerRange,
const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT
+ // The goal of the transform is to create a codegenNode implementing the
+ // VNodeCall interface.
+ const vnodeTag = isComponent
+ ? resolveComponentType(node as ComponentNode, context)
+ : `"${tag}"`
+
+ let vnodeProps: VNodeCall['props']
+ let vnodeChildren: VNodeCall['children']
+ let vnodePatchFlag: VNodeCall['patchFlag']
+ let patchFlag: number = 0
+ let vnodeDynamicProps: VNodeCall['dynamicProps']
+ let dynamicPropNames: string[] | undefined
+ let vnodeDirectives: VNodeCall['directives']
+
// <svg> and <foreignObject> must be forced into blocks so that block
// updates inside get proper isSVG flag at runtime. (#639, #643)
// This is technically web-specific, but splitting the logic out of core
let shouldUseBlock =
!isComponent && (tag === 'svg' || tag === 'foreignObject')
- const nodeType = isComponent
- ? resolveComponentType(node as ComponentNode, context)
- : `"${tag}"`
-
- const args: CallExpression['arguments'] = [nodeType]
-
- let hasProps = props.length > 0
- let patchFlag: number = 0
- let runtimeDirectives: DirectiveNode[] | undefined
- let dynamicPropNames: string[] | undefined
-
// props
- if (hasProps) {
+ if (props.length > 0) {
const propsBuildResult = buildProps(node, context)
+ vnodeProps = propsBuildResult.props
patchFlag = propsBuildResult.patchFlag
dynamicPropNames = propsBuildResult.dynamicPropNames
- runtimeDirectives = propsBuildResult.directives
- if (!propsBuildResult.props) {
- hasProps = false
- } else {
- args.push(propsBuildResult.props)
- }
+ const directives = propsBuildResult.directives
+ vnodeDirectives =
+ directives && directives.length
+ ? (createArrayExpression(
+ directives.map(dir => buildDirectiveArgs(dir, context))
+ ) as DirectiveArguments)
+ : undefined
}
// children
- const hasChildren = node.children.length > 0
- if (hasChildren) {
- if (!hasProps) {
- args.push(`null`)
- }
-
- if (nodeType === KEEP_ALIVE) {
+ if (node.children.length > 0) {
+ if (vnodeTag === KEEP_ALIVE) {
// Although a built-in component, we compile KeepAlive with raw children
// instead of slot functions so that it can be used inside Transition
// or other Transition-wrapping HOCs.
const shouldBuildAsSlots =
isComponent &&
// Portal is not a real component has dedicated handling in the renderer
- nodeType !== PORTAL &&
+ vnodeTag !== PORTAL &&
// explained above.
- nodeType !== KEEP_ALIVE
+ vnodeTag !== KEEP_ALIVE
if (shouldBuildAsSlots) {
const { slots, hasDynamicSlots } = buildSlots(node, context)
- args.push(slots)
+ vnodeChildren = slots
if (hasDynamicSlots) {
patchFlag |= PatchFlags.DYNAMIC_SLOTS
}
// pass directly if the only child is a text node
// (plain / interpolation / expression)
if (hasDynamicTextChild || type === NodeTypes.TEXT) {
- args.push(child)
+ vnodeChildren = child as TemplateTextChildNode
} else {
- args.push(node.children)
+ vnodeChildren = node.children
}
} else {
- args.push(node.children)
+ vnodeChildren = node.children
}
}
// patchFlag & dynamicPropNames
if (patchFlag !== 0) {
- if (!hasChildren) {
- if (!hasProps) {
- args.push(`null`)
- }
- args.push(`null`)
- }
if (__DEV__) {
const flagNames = Object.keys(PatchFlagNames)
.map(Number)
.filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n])
.join(`, `)
- args.push(patchFlag + ` /* ${flagNames} */`)
+ vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
} else {
- args.push(patchFlag + '')
+ vnodePatchFlag = String(patchFlag)
}
if (dynamicPropNames && dynamicPropNames.length) {
- args.push(stringifyDynamicPropNames(dynamicPropNames))
+ vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)
}
}
- const { loc } = node
- const vnode = shouldUseBlock
- ? createSequenceExpression([
- createCallExpression(context.helper(OPEN_BLOCK)),
- createCallExpression(context.helper(CREATE_BLOCK), args, loc)
- ])
- : createCallExpression(context.helper(CREATE_VNODE), args, loc)
- if (runtimeDirectives && runtimeDirectives.length) {
- node.codegenNode = createCallExpression(
- context.helper(WITH_DIRECTIVES),
- [
- vnode,
- createArrayExpression(
- runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
- loc
- )
- ],
- loc
- )
- } else {
- node.codegenNode = vnode
- }
+ node.codegenNode = createVNodeCall(
+ context,
+ vnodeTag,
+ vnodeProps,
+ vnodeChildren,
+ vnodePatchFlag,
+ vnodeDynamicProps,
+ vnodeDirectives,
+ shouldUseBlock,
+ false /* isForBlock */,
+ node.loc
+ )
}
}
createSimpleExpression,
SourceLocation,
SimpleExpressionNode,
- createSequenceExpression,
createCallExpression,
createFunctionExpression,
ElementTypes,
createObjectExpression,
createObjectProperty,
ForCodegenNode,
- ElementCodegenNode,
- SlotOutletCodegenNode,
+ RenderSlotCall,
SlotOutletNode,
ElementNode,
DirectiveNode,
ForNode,
- PlainElementNode
+ PlainElementNode,
+ createVNodeCall,
+ VNodeCall,
+ ForRenderListExpression,
+ BlockCodegenNode,
+ ForIteratorExpression
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import {
getInnerRange,
findProp,
- createBlockExpression,
isTemplateNode,
isSlotOutlet,
injectProp
RENDER_LIST,
OPEN_BLOCK,
CREATE_BLOCK,
- FRAGMENT,
- WITH_DIRECTIVES
+ FRAGMENT
} from '../runtimeHelpers'
import { processExpression } from './transformExpression'
import { PatchFlags, PatchFlagNames } from '@vue/shared'
// iterator on exit after all children have been traversed
const renderExp = createCallExpression(helper(RENDER_LIST), [
forNode.source
- ])
+ ]) as ForRenderListExpression
const keyProp = findProp(node, `key`)
const fragmentFlag = keyProp
? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT
- forNode.codegenNode = createSequenceExpression([
- // v-for fragment blocks disable tracking since they always diff their
- // children
- createCallExpression(helper(OPEN_BLOCK), [`true`]),
- createCallExpression(helper(CREATE_BLOCK), [
- helper(FRAGMENT),
- `null`,
- renderExp,
- `${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`
- ])
- ]) as ForCodegenNode
+ forNode.codegenNode = createVNodeCall(
+ context,
+ helper(FRAGMENT),
+ undefined,
+ renderExp,
+ `${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`,
+ undefined,
+ undefined,
+ true /* isBlock */,
+ true /* isForBlock */,
+ node.loc
+ ) as ForCodegenNode
return () => {
// finish the codegen now that all children have been traversed
- let childBlock
+ let childBlock: BlockCodegenNode
const isTemplate = isTemplateNode(node)
const { children } = forNode
const needFragmentWrapper =
: null
if (slotOutlet) {
// <slot v-for="..."> or <template v-for="..."><slot/></template>
- childBlock = slotOutlet.codegenNode as SlotOutletCodegenNode
+ childBlock = slotOutlet.codegenNode as RenderSlotCall
if (isTemplate && keyProperty) {
// <template v-for="..." :key="..."><slot/></template>
// we need to inject the key to the renderSlot() call.
} else if (needFragmentWrapper) {
// <template v-for="..."> with text or multi-elements
// should generate a fragment block for each loop
- childBlock = createBlockExpression(
- createCallExpression(helper(CREATE_BLOCK), [
- helper(FRAGMENT),
- keyProperty ? createObjectExpression([keyProperty]) : `null`,
- node.children,
- `${PatchFlags.STABLE_FRAGMENT} /* ${
- PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
- } */`
- ]),
- context
+ childBlock = createVNodeCall(
+ context,
+ helper(FRAGMENT),
+ keyProperty ? createObjectExpression([keyProperty]) : undefined,
+ node.children,
+ `${PatchFlags.STABLE_FRAGMENT} /* ${
+ PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
+ } */`,
+ undefined,
+ undefined,
+ true
)
} else {
// Normal element v-for. Directly use the child's codegenNode
- // arguments, but replace createVNode() with createBlock()
- let codegenNode = (children[0] as PlainElementNode)
- .codegenNode as ElementCodegenNode
- if (codegenNode.callee === WITH_DIRECTIVES) {
- codegenNode.arguments[0].callee = helper(CREATE_BLOCK)
- } else {
- codegenNode.callee = helper(CREATE_BLOCK)
- }
- childBlock = createBlockExpression(codegenNode, context)
+ // but mark it as a block.
+ childBlock = (children[0] as PlainElementNode)
+ .codegenNode as VNodeCall
+ childBlock.isBlock = true
+ helper(OPEN_BLOCK)
+ helper(CREATE_BLOCK)
}
- renderExp.arguments.push(
- createFunctionExpression(
- createForLoopParams(forNode.parseResult),
- childBlock,
- true /* force newline */
- )
- )
+ renderExp.arguments.push(createFunctionExpression(
+ createForLoopParams(forNode.parseResult),
+ childBlock,
+ true /* force newline */
+ ) as ForIteratorExpression)
}
})
}
DirectiveNode,
IfBranchNode,
SimpleExpressionNode,
- createSequenceExpression,
createCallExpression,
createConditionalExpression,
- ConditionalExpression,
- CallExpression,
createSimpleExpression,
createObjectProperty,
createObjectExpression,
- IfCodegenNode,
IfConditionalExpression,
BlockCodegenNode,
- SlotOutletCodegenNode,
- ElementCodegenNode,
- ComponentCodegenNode,
- IfNode
+ IfNode,
+ createVNodeCall
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression'
import {
- OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
- WITH_DIRECTIVES,
- CREATE_VNODE,
- CREATE_COMMENT
+ CREATE_COMMENT,
+ OPEN_BLOCK
} from '../runtimeHelpers'
import { injectProp } from '../utils'
// transformed.
return () => {
if (isRoot) {
- ifNode.codegenNode = createSequenceExpression([
- createCallExpression(context.helper(OPEN_BLOCK)),
- createCodegenNodeForBranch(branch, 0, context)
- ]) as IfCodegenNode
+ ifNode.codegenNode = createCodegenNodeForBranch(
+ branch,
+ 0,
+ context
+ ) as IfConditionalExpression
} else {
// attach this branch's codegen node to the v-if root.
let parentCondition = ifNode.codegenNode!
- .expressions[1] as ConditionalExpression
while (
parentCondition.alternate.type ===
NodeTypes.JS_CONDITIONAL_EXPRESSION
])
) as IfConditionalExpression
} else {
- return createChildrenCodegenNode(branch, index, context) as BlockCodegenNode
+ return createChildrenCodegenNode(branch, index, context)
}
}
branch: IfBranchNode,
index: number,
context: TransformContext
-): CallExpression {
+): BlockCodegenNode {
const { helper } = context
const keyProperty = createObjectProperty(
`key`,
const needFragmentWrapper =
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
if (needFragmentWrapper) {
- const blockArgs: CallExpression['arguments'] = [
- helper(FRAGMENT),
- createObjectExpression([keyProperty]),
- children
- ]
if (children.length === 1 && firstChild.type === NodeTypes.FOR) {
// optimize away nested fragments when child is a ForNode
- const forBlockArgs = firstChild.codegenNode!.expressions[1].arguments
- // directly use the for block's children and patchFlag
- blockArgs[2] = forBlockArgs[2]
- blockArgs[3] = forBlockArgs[3]
+ 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
+ )
}
- return createCallExpression(helper(CREATE_BLOCK), blockArgs)
} else {
- const childCodegen = (firstChild as ElementNode).codegenNode as
- | ElementCodegenNode
- | ComponentCodegenNode
- | SlotOutletCodegenNode
- let vnodeCall = childCodegen
- // Element with custom directives. Locate the actual createVNode() call.
- if (vnodeCall.callee === WITH_DIRECTIVES) {
- vnodeCall = vnodeCall.arguments[0]
- }
+ const vnodeCall = (firstChild as ElementNode)
+ .codegenNode as BlockCodegenNode
// Change createVNode to createBlock.
- if (vnodeCall.callee === CREATE_VNODE) {
- vnodeCall.callee = helper(CREATE_BLOCK)
+ if (vnodeCall.type === NodeTypes.VNODE_CALL) {
+ vnodeCall.isBlock = true
+ helper(OPEN_BLOCK)
+ helper(CREATE_BLOCK)
}
// inject branch key
injectProp(vnodeCall, keyProperty, context)
- return childCodegen
+ return vnodeCall
}
}
FunctionExpression,
CallExpression,
createCallExpression,
- createArrayExpression
+ createArrayExpression,
+ SlotsExpression
} from '../ast'
import { TransformContext, NodeTransform } from '../transform'
import { createCompilerError, ErrorCodes } from '../errors'
context: TransformContext,
buildSlotFn: SlotFnBuilder = buildClientSlotFn
): {
- slots: ObjectExpression | CallExpression
+ slots: SlotsExpression
hasDynamicSlots: boolean
} {
const { children, loc } = node
}
}
- let slots: ObjectExpression | CallExpression = createObjectExpression(
+ let slots = createObjectExpression(
slotsProperties.concat(
createObjectProperty(`_compiled`, createSimpleExpression(`true`, false))
),
loc
- )
+ ) as SlotsExpression
if (dynamicSlots.length) {
slots = createCallExpression(context.helper(CREATE_SLOTS), [
slots,
createArrayExpression(dynamicSlots)
- ])
+ ]) as SlotsExpression
}
return {
ElementNode,
NodeTypes,
CallExpression,
- SequenceExpression,
- createSequenceExpression,
createCallExpression,
DirectiveNode,
ElementTypes,
createObjectExpression,
SlotOutletNode,
TemplateNode,
- BlockCodegenNode,
- ElementCodegenNode,
- SlotOutletCodegenNode,
- ComponentCodegenNode,
+ RenderSlotCall,
ExpressionNode,
IfBranchNode,
TextNode,
- InterpolationNode
+ InterpolationNode,
+ VNodeCall
} from './ast'
import { parse } from 'acorn'
import { walk } from 'estree-walker'
import { TransformContext } from './transform'
import {
- OPEN_BLOCK,
MERGE_PROPS,
- RENDER_SLOT,
PORTAL,
SUSPENSE,
KEEP_ALIVE,
)
}
-export function createBlockExpression(
- blockExp: BlockCodegenNode,
- context: TransformContext
-): SequenceExpression {
- return createSequenceExpression([
- createCallExpression(context.helper(OPEN_BLOCK)),
- blockExp
- ])
-}
-
export function isText(
node: TemplateChildNode
): node is TextNode | InterpolationNode {
}
export function injectProp(
- node: ElementCodegenNode | ComponentCodegenNode | SlotOutletCodegenNode,
+ node: VNodeCall | RenderSlotCall,
prop: Property,
context: TransformContext
) {
let propsWithInjection: ObjectExpression | CallExpression
const props =
- node.callee === RENDER_SLOT ? node.arguments[2] : node.arguments[1]
+ node.type === NodeTypes.VNODE_CALL ? node.props : node.arguments[2]
if (props == null || isString(props)) {
propsWithInjection = createObjectExpression([prop])
} else if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
props
])
}
- if (node.callee === RENDER_SLOT) {
- node.arguments[2] = propsWithInjection
+ if (node.type === NodeTypes.VNODE_CALL) {
+ node.props = propsWithInjection
} else {
- node.arguments[1] = propsWithInjection
+ node.arguments[2] = propsWithInjection
}
}
asBlock: boolean = false
): VNode {
return asBlock
- ? createBlock(Comment, null, text)
+ ? (openBlock(), createBlock(Comment, null, text))
: createVNode(Comment, null, text)
}