refactor(compiler-core): use dedicated node type for element codegen

Previously codegen node for elements and components used raw expressions,
which leads to multiple permutations of AST shapes based on whether the
node is a block or has directives. The complexity is spread across the
entire compiler and occurs whenever a transform needs to deal with
element codegen nodes.

This refactor centralizes the handling of all possible permutations
into the codegen phase, so that all elements/components will have a
consistent node type throughout the transform phase.

The refactor is split into two commits (with test updates in a separate
one) so changes can be easier to inspect.
This commit is contained in:
Evan You 2020-02-11 18:12:56 -05:00
parent fe9da2d0e4
commit e3988b40d8
10 changed files with 340 additions and 410 deletions

View File

@ -1,17 +1,17 @@
import { isString } from '@vue/shared' import { isString } from '@vue/shared'
import { ForParseResult } from './transforms/vFor' import { ForParseResult } from './transforms/vFor'
import { import {
CREATE_VNODE,
WITH_DIRECTIVES,
RENDER_SLOT, RENDER_SLOT,
CREATE_SLOTS, CREATE_SLOTS,
RENDER_LIST, RENDER_LIST,
OPEN_BLOCK, OPEN_BLOCK,
CREATE_BLOCK, CREATE_BLOCK,
FRAGMENT FRAGMENT,
CREATE_VNODE,
WITH_DIRECTIVES
} from './runtimeHelpers' } from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement' 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). // Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific // More namespaces like SVG and MathML are declared by platform specific
@ -38,12 +38,12 @@ export const enum NodeTypes {
FOR, FOR,
TEXT_CALL, TEXT_CALL,
// codegen // codegen
VNODE_CALL,
JS_CALL_EXPRESSION, JS_CALL_EXPRESSION,
JS_OBJECT_EXPRESSION, JS_OBJECT_EXPRESSION,
JS_PROPERTY, JS_PROPERTY,
JS_ARRAY_EXPRESSION, JS_ARRAY_EXPRESSION,
JS_FUNCTION_EXPRESSION, JS_FUNCTION_EXPRESSION,
JS_SEQUENCE_EXPRESSION,
JS_CONDITIONAL_EXPRESSION, JS_CONDITIONAL_EXPRESSION,
JS_CACHE_EXPRESSION, JS_CACHE_EXPRESSION,
@ -123,21 +123,14 @@ export interface BaseElementNode extends Node {
isSelfClosing: boolean isSelfClosing: boolean
props: Array<AttributeNode | DirectiveNode> props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[] children: TemplateChildNode[]
codegenNode:
| CallExpression
| SimpleExpressionNode
| CacheExpression
| SequenceExpression
| undefined
} }
export interface PlainElementNode extends BaseElementNode { export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT tagType: ElementTypes.ELEMENT
codegenNode: codegenNode:
| ElementCodegenNode | VNodeCall
| SimpleExpressionNode // when hoisted | SimpleExpressionNode // when hoisted
| CacheExpression // when cached by v-once | CacheExpression // when cached by v-once
| SequenceExpression // when turned into a block
| undefined | undefined
ssrCodegenNode?: TemplateLiteral ssrCodegenNode?: TemplateLiteral
} }
@ -145,7 +138,7 @@ export interface PlainElementNode extends BaseElementNode {
export interface ComponentNode extends BaseElementNode { export interface ComponentNode extends BaseElementNode {
tagType: ElementTypes.COMPONENT tagType: ElementTypes.COMPONENT
codegenNode: codegenNode:
| ComponentCodegenNode | VNodeCall
| CacheExpression // when cached by v-once | CacheExpression // when cached by v-once
| undefined | undefined
ssrCodegenNode?: CallExpression ssrCodegenNode?: CallExpression
@ -153,13 +146,17 @@ export interface ComponentNode extends BaseElementNode {
export interface SlotOutletNode extends BaseElementNode { export interface SlotOutletNode extends BaseElementNode {
tagType: ElementTypes.SLOT tagType: ElementTypes.SLOT
codegenNode: SlotOutletCodegenNode | undefined | CacheExpression // when cached by v-once codegenNode:
| RenderSlotCall
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: CallExpression ssrCodegenNode?: CallExpression
} }
export interface TemplateNode extends BaseElementNode { export interface TemplateNode extends BaseElementNode {
tagType: ElementTypes.TEMPLATE tagType: ElementTypes.TEMPLATE
// TemplateNode is a container type that always gets compiled away // TemplateNode is a container type that always gets compiled away
codegenNode: undefined
} }
export interface TextNode extends Node { export interface TextNode extends Node {
@ -220,7 +217,7 @@ export interface CompoundExpressionNode extends Node {
export interface IfNode extends Node { export interface IfNode extends Node {
type: NodeTypes.IF type: NodeTypes.IF
branches: IfBranchNode[] branches: IfBranchNode[]
codegenNode?: IfCodegenNode codegenNode?: IfConditionalExpression
} }
export interface IfBranchNode extends Node { export interface IfBranchNode extends Node {
@ -246,6 +243,28 @@ export interface TextCallNode extends Node {
codegenNode: CallExpression | SimpleExpressionNode // when hoisted 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 --------------------------------------------------------------- // JS Node Types ---------------------------------------------------------------
// We also include a number of JavaScript AST nodes for code generation. // We also include a number of JavaScript AST nodes for code generation.
@ -253,13 +272,13 @@ export interface TextCallNode extends Node {
// Vue render function generation. // Vue render function generation.
export type JSChildNode = export type JSChildNode =
| VNodeCall
| CallExpression | CallExpression
| ObjectExpression | ObjectExpression
| ArrayExpression | ArrayExpression
| ExpressionNode | ExpressionNode
| FunctionExpression | FunctionExpression
| ConditionalExpression | ConditionalExpression
| SequenceExpression
| CacheExpression | CacheExpression
| AssignmentExpression | AssignmentExpression
@ -301,11 +320,6 @@ export interface FunctionExpression extends Node {
isSlot: boolean isSlot: boolean
} }
export interface SequenceExpression extends Node {
type: NodeTypes.JS_SEQUENCE_EXPRESSION
expressions: JSChildNode[]
}
export interface ConditionalExpression extends Node { export interface ConditionalExpression extends Node {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION type: NodeTypes.JS_CONDITIONAL_EXPRESSION
test: JSChildNode test: JSChildNode
@ -360,57 +374,31 @@ export interface ReturnStatement extends Node {
// Codegen Node Types ---------------------------------------------------------- // Codegen Node Types ----------------------------------------------------------
// createVNode(...) export interface DirectiveArguments extends ArrayExpression {
export interface PlainElementCodegenNode extends CallExpression { elements: DirectiveArgumentNode[]
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 type ElementCodegenNode = export interface DirectiveArgumentNode extends ArrayExpression {
| PlainElementCodegenNode elements: // dir, exp, arg, modifiers
| CodegenNodeWithDirective<PlainElementCodegenNode> | [string]
| [string, ExpressionNode]
// createVNode(...) | [string, ExpressionNode, ExpressionNode]
export interface PlainComponentCodegenNode extends CallExpression { | [string, ExpressionNode, ExpressionNode, ObjectExpression]
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
]
| [
string | symbol,
'null' | PropsExpression,
'null' | SlotsExpression,
string,
string
]
} }
export type ComponentCodegenNode = // renderSlot(...)
| PlainComponentCodegenNode export interface RenderSlotCall extends CallExpression {
| CodegenNodeWithDirective<PlainComponentCodegenNode> callee: typeof RENDER_SLOT
arguments: // $slots, name, props, fallback
| [string, string | ExpressionNode]
| [string, string | ExpressionNode, PropsExpression]
| [
string,
string | ExpressionNode,
PropsExpression | '{}',
TemplateChildNode[]
]
}
export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
@ -462,63 +450,20 @@ export interface DynamicSlotFnProperty extends Property {
value: SlotFunctionExpression value: SlotFunctionExpression
} }
// withDirectives(createVNode(...), [ export type BlockCodegenNode = VNodeCall | RenderSlotCall
// [_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 interface IfConditionalExpression extends ConditionalExpression { export interface IfConditionalExpression extends ConditionalExpression {
consequent: BlockCodegenNode consequent: BlockCodegenNode
alternate: BlockCodegenNode | IfConditionalExpression alternate: BlockCodegenNode | IfConditionalExpression
} }
export interface ForCodegenNode extends SequenceExpression { export interface ForCodegenNode extends VNodeCall {
expressions: [OpenBlockExpression, ForBlockCodegenNode] isBlock: true
} tag: typeof FRAGMENT
props: undefined
export interface ForBlockCodegenNode extends CallExpression { children: ForRenderListExpression
callee: typeof CREATE_BLOCK patchFlag: string
arguments: [typeof FRAGMENT, 'null', ForRenderListExpression, string] isForBlock: true
} }
export interface ForRenderListExpression extends CallExpression { export interface ForRenderListExpression extends CallExpression {
@ -530,11 +475,6 @@ export interface ForIteratorExpression extends FunctionExpression {
returns: BlockCodegenNode returns: BlockCodegenNode
} }
export interface OpenBlockExpression extends CallExpression {
callee: typeof OPEN_BLOCK
arguments: []
}
// AST Utilities --------------------------------------------------------------- // AST Utilities ---------------------------------------------------------------
// Some expressions, e.g. sequence and conditional expressions, are never // Some expressions, e.g. sequence and conditional expressions, are never
@ -565,6 +505,42 @@ export function createRoot(
} }
} }
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( export function createArrayExpression(
elements: ArrayExpression['elements'], elements: ArrayExpression['elements'],
loc: SourceLocation = locStub loc: SourceLocation = locStub
@ -638,15 +614,9 @@ export function createCompoundExpression(
} }
} }
type InferCodegenNodeType<T> = T extends type InferCodegenNodeType<T> = T extends typeof RENDER_SLOT
| typeof CREATE_VNODE ? RenderSlotCall
| typeof CREATE_BLOCK : CallExpression
? PlainElementCodegenNode | PlainComponentCodegenNode
: T extends typeof WITH_DIRECTIVES
?
| CodegenNodeWithDirective<PlainElementCodegenNode>
| CodegenNodeWithDirective<PlainComponentCodegenNode>
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
export function createCallExpression<T extends CallExpression['callee']>( export function createCallExpression<T extends CallExpression['callee']>(
callee: T, callee: T,
@ -678,16 +648,6 @@ export function createFunctionExpression(
} }
} }
export function createSequenceExpression(
expressions: SequenceExpression['expressions']
): SequenceExpression {
return {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions,
loc: locStub
}
}
export function createConditionalExpression( export function createConditionalExpression(
test: ConditionalExpression['test'], test: ConditionalExpression['test'],
consequent: ConditionalExpression['consequent'], consequent: ConditionalExpression['consequent'],

View File

@ -15,7 +15,6 @@ import {
CompoundExpressionNode, CompoundExpressionNode,
SimpleExpressionNode, SimpleExpressionNode,
FunctionExpression, FunctionExpression,
SequenceExpression,
ConditionalExpression, ConditionalExpression,
CacheExpression, CacheExpression,
locStub, locStub,
@ -23,7 +22,8 @@ import {
TemplateLiteral, TemplateLiteral,
IfStatement, IfStatement,
AssignmentExpression, AssignmentExpression,
ReturnStatement ReturnStatement,
VNodeCall
} from './ast' } from './ast'
import { SourceMapGenerator, RawSourceMap } from 'source-map' import { SourceMapGenerator, RawSourceMap } from 'source-map'
import { import {
@ -45,7 +45,10 @@ import {
CREATE_TEXT, CREATE_TEXT,
PUSH_SCOPE_ID, PUSH_SCOPE_ID,
POP_SCOPE_ID, POP_SCOPE_ID,
WITH_SCOPE_ID WITH_SCOPE_ID,
WITH_DIRECTIVES,
CREATE_BLOCK,
OPEN_BLOCK
} from './runtimeHelpers' } from './runtimeHelpers'
import { ImportItem } from './transform' import { ImportItem } from './transform'
@ -547,6 +550,10 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
case NodeTypes.COMMENT: case NodeTypes.COMMENT:
genComment(node, context) genComment(node, context)
break break
case NodeTypes.VNODE_CALL:
genVNodeCall(node, context)
break
case NodeTypes.JS_CALL_EXPRESSION: case NodeTypes.JS_CALL_EXPRESSION:
genCallExpression(node, context) genCallExpression(node, context)
break break
@ -559,9 +566,6 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
case NodeTypes.JS_FUNCTION_EXPRESSION: case NodeTypes.JS_FUNCTION_EXPRESSION:
genFunctionExpression(node, context) genFunctionExpression(node, context)
break break
case NodeTypes.JS_SEQUENCE_EXPRESSION:
genSequenceExpression(node, context)
break
case NodeTypes.JS_CONDITIONAL_EXPRESSION: case NodeTypes.JS_CONDITIONAL_EXPRESSION:
genConditionalExpression(node, context) genConditionalExpression(node, context)
break break
@ -657,6 +661,48 @@ function genComment(node: CommentNode, context: CodegenContext) {
} }
} }
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 // JavaScript
function genCallExpression(node: CallExpression, context: CodegenContext) { function genCallExpression(node: CallExpression, context: CodegenContext) {
const callee = isString(node.callee) const callee = isString(node.callee)
@ -782,15 +828,6 @@ function genConditionalExpression(
needNewline && deindent(true /* without newline */) 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) { function genCacheExpression(node: CacheExpression, context: CodegenContext) {
const { push, helper, indent, deindent, newline } = context const { push, helper, indent, deindent, newline } = context
push(`_cache[${node.index}] || (`) push(`_cache[${node.index}] || (`)

View File

@ -12,12 +12,10 @@ import {
JSChildNode, JSChildNode,
SimpleExpressionNode, SimpleExpressionNode,
ElementTypes, ElementTypes,
ElementCodegenNode,
ComponentCodegenNode,
createCallExpression,
CacheExpression, CacheExpression,
createCacheExpression, createCacheExpression,
TemplateLiteral TemplateLiteral,
createVNodeCall
} from './ast' } from './ast'
import { import {
isString, isString,
@ -31,11 +29,11 @@ import {
TO_DISPLAY_STRING, TO_DISPLAY_STRING,
FRAGMENT, FRAGMENT,
helperNameMap, helperNameMap,
WITH_DIRECTIVES,
CREATE_BLOCK, CREATE_BLOCK,
CREATE_COMMENT CREATE_COMMENT,
OPEN_BLOCK
} from './runtimeHelpers' } from './runtimeHelpers'
import { isVSlot, createBlockExpression } from './utils' import { isVSlot } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic' import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
// There are two types of transforms: // There are two types of transforms:
@ -286,20 +284,13 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
if (isSingleElementRoot(root, child) && child.codegenNode) { if (isSingleElementRoot(root, child) && child.codegenNode) {
// single element root is never hoisted so codegenNode will never be // single element root is never hoisted so codegenNode will never be
// SimpleExpressionNode // SimpleExpressionNode
const codegenNode = child.codegenNode as const codegenNode = child.codegenNode
| ElementCodegenNode if (codegenNode.type === NodeTypes.VNODE_CALL) {
| ComponentCodegenNode codegenNode.isBlock = true
| CacheExpression helper(OPEN_BLOCK)
if (codegenNode.type !== NodeTypes.JS_CACHE_EXPRESSION) { helper(CREATE_BLOCK)
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 root.codegenNode = codegenNode
}
} else { } else {
// - single <slot/>, IfNode, ForNode: already blocks. // - single <slot/>, IfNode, ForNode: already blocks.
// - single text node: always patched. // - single text node: always patched.
@ -308,16 +299,17 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
} }
} else if (children.length > 1) { } else if (children.length > 1) {
// root has multiple nodes - return a fragment block. // root has multiple nodes - return a fragment block.
root.codegenNode = createBlockExpression( root.codegenNode = createVNodeCall(
createCallExpression(helper(CREATE_BLOCK), [ context,
helper(FRAGMENT), helper(FRAGMENT),
`null`, undefined,
root.children, root.children,
`${PatchFlags.STABLE_FRAGMENT} /* ${ `${PatchFlags.STABLE_FRAGMENT} /* ${
PatchFlagNames[PatchFlags.STABLE_FRAGMENT] PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
} */` } */`,
]), undefined,
context undefined,
true
) )
} else { } else {
// no children = noop. codegen will return null. // no children = noop. codegen will return null.

View File

@ -8,11 +8,9 @@ import {
ComponentNode, ComponentNode,
TemplateNode, TemplateNode,
ElementNode, ElementNode,
PlainElementCodegenNode, VNodeCall
CodegenNodeWithDirective
} from '../ast' } from '../ast'
import { TransformContext } from '../transform' import { TransformContext } from '../transform'
import { WITH_DIRECTIVES } from '../runtimeHelpers'
import { PatchFlags, isString, isSymbol } from '@vue/shared' import { PatchFlags, isString, isSymbol } from '@vue/shared'
import { isSlotOutlet, findProp } from '../utils' import { isSlotOutlet, findProp } from '../utils'
@ -60,7 +58,7 @@ function walk(
// node may contain dynamic children, but its props may be eligible for // node may contain dynamic children, but its props may be eligible for
// hoisting. // hoisting.
const codegenNode = child.codegenNode! const codegenNode = child.codegenNode!
if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) { if (codegenNode.type === NodeTypes.VNODE_CALL) {
const flag = getPatchFlag(codegenNode) const flag = getPatchFlag(codegenNode)
if ( if (
(!flag || (!flag ||
@ -70,8 +68,8 @@ function walk(
!hasCachedProps(child) !hasCachedProps(child)
) { ) {
const props = getNodeProps(child) const props = getNodeProps(child)
if (props && props !== `null`) { if (props) {
getVNodeCall(codegenNode).arguments[1] = context.hoist(props) codegenNode.props = context.hoist(props)
} }
} }
} }
@ -111,7 +109,7 @@ export function isStaticNode(
return cached return cached
} }
const codegenNode = node.codegenNode! const codegenNode = node.codegenNode!
if (codegenNode.type !== NodeTypes.JS_CALL_EXPRESSION) { if (codegenNode.type !== NodeTypes.VNODE_CALL) {
return false return false
} }
const flag = getPatchFlag(codegenNode) const flag = getPatchFlag(codegenNode)
@ -123,6 +121,12 @@ export function isStaticNode(
return false 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) resultCache.set(node, true)
return true return true
} else { } else {
@ -164,11 +168,7 @@ function hasCachedProps(node: PlainElementNode): boolean {
return false return false
} }
const props = getNodeProps(node) const props = getNodeProps(node)
if ( if (props && props.type === NodeTypes.JS_OBJECT_EXPRESSION) {
props &&
props !== 'null' &&
props.type === NodeTypes.JS_OBJECT_EXPRESSION
) {
const { properties } = props const { properties } = props
for (let i = 0; i < properties.length; i++) { for (let i = 0; i < properties.length; i++) {
if (properties[i].value.type === NodeTypes.JS_CACHE_EXPRESSION) { if (properties[i].value.type === NodeTypes.JS_CACHE_EXPRESSION) {
@ -181,30 +181,12 @@ function hasCachedProps(node: PlainElementNode): boolean {
function getNodeProps(node: PlainElementNode) { function getNodeProps(node: PlainElementNode) {
const codegenNode = node.codegenNode! const codegenNode = node.codegenNode!
if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) { if (codegenNode.type === NodeTypes.VNODE_CALL) {
return getVNodeArgAt( return codegenNode.props
codegenNode,
1
) as PlainElementCodegenNode['arguments'][1]
} }
} }
type NonCachedCodegenNode = function getPatchFlag(node: VNodeCall): number | undefined {
| PlainElementCodegenNode const flag = node.patchFlag
| 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
return flag ? parseInt(flag, 10) : undefined return flag ? parseInt(flag, 10) : undefined
} }

View File

@ -14,23 +14,22 @@ import {
createSimpleExpression, createSimpleExpression,
createObjectExpression, createObjectExpression,
Property, Property,
createSequenceExpression, ComponentNode,
ComponentNode VNodeCall,
TemplateTextChildNode,
DirectiveArguments,
createVNodeCall
} from '../ast' } from '../ast'
import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared' import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { import {
CREATE_VNODE,
WITH_DIRECTIVES,
RESOLVE_DIRECTIVE, RESOLVE_DIRECTIVE,
RESOLVE_COMPONENT, RESOLVE_COMPONENT,
RESOLVE_DYNAMIC_COMPONENT, RESOLVE_DYNAMIC_COMPONENT,
MERGE_PROPS, MERGE_PROPS,
TO_HANDLERS, TO_HANDLERS,
PORTAL, PORTAL,
KEEP_ALIVE, KEEP_ALIVE
OPEN_BLOCK,
CREATE_BLOCK
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { import {
getInnerRange, getInnerRange,
@ -63,6 +62,20 @@ export const transformElement: NodeTransform = (node, context) => {
const { tag, props } = node const { tag, props } = node
const isComponent = node.tagType === ElementTypes.COMPONENT 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 // <svg> and <foreignObject> must be forced into blocks so that block
// updates inside get proper isSVG flag at runtime. (#639, #643) // updates inside get proper isSVG flag at runtime. (#639, #643)
// This is technically web-specific, but splitting the logic out of core // This is technically web-specific, but splitting the logic out of core
@ -70,38 +83,24 @@ export const transformElement: NodeTransform = (node, context) => {
let shouldUseBlock = let shouldUseBlock =
!isComponent && (tag === 'svg' || tag === 'foreignObject') !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 // props
if (hasProps) { if (props.length > 0) {
const propsBuildResult = buildProps(node, context) const propsBuildResult = buildProps(node, context)
vnodeProps = propsBuildResult.props
patchFlag = propsBuildResult.patchFlag patchFlag = propsBuildResult.patchFlag
dynamicPropNames = propsBuildResult.dynamicPropNames dynamicPropNames = propsBuildResult.dynamicPropNames
runtimeDirectives = propsBuildResult.directives const directives = propsBuildResult.directives
if (!propsBuildResult.props) { vnodeDirectives =
hasProps = false directives && directives.length
} else { ? (createArrayExpression(
args.push(propsBuildResult.props) directives.map(dir => buildDirectiveArgs(dir, context))
} ) as DirectiveArguments)
: undefined
} }
// children // children
const hasChildren = node.children.length > 0 if (node.children.length > 0) {
if (hasChildren) { if (vnodeTag === KEEP_ALIVE) {
if (!hasProps) {
args.push(`null`)
}
if (nodeType === KEEP_ALIVE) {
// Although a built-in component, we compile KeepAlive with raw children // Although a built-in component, we compile KeepAlive with raw children
// instead of slot functions so that it can be used inside Transition // instead of slot functions so that it can be used inside Transition
// or other Transition-wrapping HOCs. // or other Transition-wrapping HOCs.
@ -125,13 +124,13 @@ export const transformElement: NodeTransform = (node, context) => {
const shouldBuildAsSlots = const shouldBuildAsSlots =
isComponent && isComponent &&
// Portal is not a real component has dedicated handling in the renderer // Portal is not a real component has dedicated handling in the renderer
nodeType !== PORTAL && vnodeTag !== PORTAL &&
// explained above. // explained above.
nodeType !== KEEP_ALIVE vnodeTag !== KEEP_ALIVE
if (shouldBuildAsSlots) { if (shouldBuildAsSlots) {
const { slots, hasDynamicSlots } = buildSlots(node, context) const { slots, hasDynamicSlots } = buildSlots(node, context)
args.push(slots) vnodeChildren = slots
if (hasDynamicSlots) { if (hasDynamicSlots) {
patchFlag |= PatchFlags.DYNAMIC_SLOTS patchFlag |= PatchFlags.DYNAMIC_SLOTS
} }
@ -148,60 +147,44 @@ export const transformElement: NodeTransform = (node, context) => {
// pass directly if the only child is a text node // pass directly if the only child is a text node
// (plain / interpolation / expression) // (plain / interpolation / expression)
if (hasDynamicTextChild || type === NodeTypes.TEXT) { if (hasDynamicTextChild || type === NodeTypes.TEXT) {
args.push(child) vnodeChildren = child as TemplateTextChildNode
} else { } else {
args.push(node.children) vnodeChildren = node.children
} }
} else { } else {
args.push(node.children) vnodeChildren = node.children
} }
} }
// patchFlag & dynamicPropNames // patchFlag & dynamicPropNames
if (patchFlag !== 0) { if (patchFlag !== 0) {
if (!hasChildren) {
if (!hasProps) {
args.push(`null`)
}
args.push(`null`)
}
if (__DEV__) { if (__DEV__) {
const flagNames = Object.keys(PatchFlagNames) const flagNames = Object.keys(PatchFlagNames)
.map(Number) .map(Number)
.filter(n => n > 0 && patchFlag & n) .filter(n => n > 0 && patchFlag & n)
.map(n => PatchFlagNames[n]) .map(n => PatchFlagNames[n])
.join(`, `) .join(`, `)
args.push(patchFlag + ` /* ${flagNames} */`) vnodePatchFlag = patchFlag + ` /* ${flagNames} */`
} else { } else {
args.push(patchFlag + '') vnodePatchFlag = String(patchFlag)
} }
if (dynamicPropNames && dynamicPropNames.length) { if (dynamicPropNames && dynamicPropNames.length) {
args.push(stringifyDynamicPropNames(dynamicPropNames)) vnodeDynamicProps = stringifyDynamicPropNames(dynamicPropNames)
} }
} }
const { loc } = node node.codegenNode = createVNodeCall(
const vnode = shouldUseBlock context,
? createSequenceExpression([ vnodeTag,
createCallExpression(context.helper(OPEN_BLOCK)), vnodeProps,
createCallExpression(context.helper(CREATE_BLOCK), args, loc) vnodeChildren,
]) vnodePatchFlag,
: createCallExpression(context.helper(CREATE_VNODE), args, loc) vnodeDynamicProps,
if (runtimeDirectives && runtimeDirectives.length) { vnodeDirectives,
node.codegenNode = createCallExpression( shouldUseBlock,
context.helper(WITH_DIRECTIVES), false /* isForBlock */,
[ node.loc
vnode,
createArrayExpression(
runtimeDirectives.map(dir => buildDirectiveArgs(dir, context)),
loc
) )
],
loc
)
} else {
node.codegenNode = vnode
}
} }
} }

View File

@ -8,26 +8,28 @@ import {
createSimpleExpression, createSimpleExpression,
SourceLocation, SourceLocation,
SimpleExpressionNode, SimpleExpressionNode,
createSequenceExpression,
createCallExpression, createCallExpression,
createFunctionExpression, createFunctionExpression,
ElementTypes, ElementTypes,
createObjectExpression, createObjectExpression,
createObjectProperty, createObjectProperty,
ForCodegenNode, ForCodegenNode,
ElementCodegenNode, RenderSlotCall,
SlotOutletCodegenNode,
SlotOutletNode, SlotOutletNode,
ElementNode, ElementNode,
DirectiveNode, DirectiveNode,
ForNode, ForNode,
PlainElementNode PlainElementNode,
createVNodeCall,
VNodeCall,
ForRenderListExpression,
BlockCodegenNode,
ForIteratorExpression
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { import {
getInnerRange, getInnerRange,
findProp, findProp,
createBlockExpression,
isTemplateNode, isTemplateNode,
isSlotOutlet, isSlotOutlet,
injectProp injectProp
@ -36,8 +38,7 @@ import {
RENDER_LIST, RENDER_LIST,
OPEN_BLOCK, OPEN_BLOCK,
CREATE_BLOCK, CREATE_BLOCK,
FRAGMENT, FRAGMENT
WITH_DIRECTIVES
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
import { PatchFlags, PatchFlagNames } from '@vue/shared' import { PatchFlags, PatchFlagNames } from '@vue/shared'
@ -51,26 +52,27 @@ export const transformFor = createStructuralDirectiveTransform(
// iterator on exit after all children have been traversed // iterator on exit after all children have been traversed
const renderExp = createCallExpression(helper(RENDER_LIST), [ const renderExp = createCallExpression(helper(RENDER_LIST), [
forNode.source forNode.source
]) ]) as ForRenderListExpression
const keyProp = findProp(node, `key`) const keyProp = findProp(node, `key`)
const fragmentFlag = keyProp const fragmentFlag = keyProp
? PatchFlags.KEYED_FRAGMENT ? PatchFlags.KEYED_FRAGMENT
: PatchFlags.UNKEYED_FRAGMENT : PatchFlags.UNKEYED_FRAGMENT
forNode.codegenNode = createSequenceExpression([ forNode.codegenNode = createVNodeCall(
// v-for fragment blocks disable tracking since they always diff their context,
// children
createCallExpression(helper(OPEN_BLOCK), [`true`]),
createCallExpression(helper(CREATE_BLOCK), [
helper(FRAGMENT), helper(FRAGMENT),
`null`, undefined,
renderExp, renderExp,
`${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */` `${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`,
]) undefined,
]) as ForCodegenNode undefined,
true /* isBlock */,
true /* isForBlock */,
node.loc
) as ForCodegenNode
return () => { return () => {
// finish the codegen now that all children have been traversed // finish the codegen now that all children have been traversed
let childBlock let childBlock: BlockCodegenNode
const isTemplate = isTemplateNode(node) const isTemplate = isTemplateNode(node)
const { children } = forNode const { children } = forNode
const needFragmentWrapper = const needFragmentWrapper =
@ -92,7 +94,7 @@ export const transformFor = createStructuralDirectiveTransform(
: null : null
if (slotOutlet) { if (slotOutlet) {
// <slot v-for="..."> or <template v-for="..."><slot/></template> // <slot v-for="..."> or <template v-for="..."><slot/></template>
childBlock = slotOutlet.codegenNode as SlotOutletCodegenNode childBlock = slotOutlet.codegenNode as RenderSlotCall
if (isTemplate && keyProperty) { if (isTemplate && keyProperty) {
// <template v-for="..." :key="..."><slot/></template> // <template v-for="..." :key="..."><slot/></template>
// we need to inject the key to the renderSlot() call. // we need to inject the key to the renderSlot() call.
@ -102,37 +104,33 @@ export const transformFor = createStructuralDirectiveTransform(
} else if (needFragmentWrapper) { } else if (needFragmentWrapper) {
// <template v-for="..."> with text or multi-elements // <template v-for="..."> with text or multi-elements
// should generate a fragment block for each loop // should generate a fragment block for each loop
childBlock = createBlockExpression( childBlock = createVNodeCall(
createCallExpression(helper(CREATE_BLOCK), [ context,
helper(FRAGMENT), helper(FRAGMENT),
keyProperty ? createObjectExpression([keyProperty]) : `null`, keyProperty ? createObjectExpression([keyProperty]) : undefined,
node.children, node.children,
`${PatchFlags.STABLE_FRAGMENT} /* ${ `${PatchFlags.STABLE_FRAGMENT} /* ${
PatchFlagNames[PatchFlags.STABLE_FRAGMENT] PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
} */` } */`,
]), undefined,
context undefined,
true
) )
} else { } else {
// Normal element v-for. Directly use the child's codegenNode // Normal element v-for. Directly use the child's codegenNode
// arguments, but replace createVNode() with createBlock() // but mark it as a block.
let codegenNode = (children[0] as PlainElementNode) childBlock = (children[0] as PlainElementNode)
.codegenNode as ElementCodegenNode .codegenNode as VNodeCall
if (codegenNode.callee === WITH_DIRECTIVES) { childBlock.isBlock = true
codegenNode.arguments[0].callee = helper(CREATE_BLOCK) helper(OPEN_BLOCK)
} else { helper(CREATE_BLOCK)
codegenNode.callee = helper(CREATE_BLOCK)
}
childBlock = createBlockExpression(codegenNode, context)
} }
renderExp.arguments.push( renderExp.arguments.push(createFunctionExpression(
createFunctionExpression(
createForLoopParams(forNode.parseResult), createForLoopParams(forNode.parseResult),
childBlock, childBlock,
true /* force newline */ true /* force newline */
) ) as ForIteratorExpression)
)
} }
}) })
} }

View File

@ -10,31 +10,23 @@ import {
DirectiveNode, DirectiveNode,
IfBranchNode, IfBranchNode,
SimpleExpressionNode, SimpleExpressionNode,
createSequenceExpression,
createCallExpression, createCallExpression,
createConditionalExpression, createConditionalExpression,
ConditionalExpression,
CallExpression,
createSimpleExpression, createSimpleExpression,
createObjectProperty, createObjectProperty,
createObjectExpression, createObjectExpression,
IfCodegenNode,
IfConditionalExpression, IfConditionalExpression,
BlockCodegenNode, BlockCodegenNode,
SlotOutletCodegenNode, IfNode,
ElementCodegenNode, createVNodeCall
ComponentCodegenNode,
IfNode
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
import { import {
OPEN_BLOCK,
CREATE_BLOCK, CREATE_BLOCK,
FRAGMENT, FRAGMENT,
WITH_DIRECTIVES, CREATE_COMMENT,
CREATE_VNODE, OPEN_BLOCK
CREATE_COMMENT
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { injectProp } from '../utils' import { injectProp } from '../utils'
@ -46,14 +38,14 @@ export const transformIf = createStructuralDirectiveTransform(
// transformed. // transformed.
return () => { return () => {
if (isRoot) { if (isRoot) {
ifNode.codegenNode = createSequenceExpression([ ifNode.codegenNode = createCodegenNodeForBranch(
createCallExpression(context.helper(OPEN_BLOCK)), branch,
createCodegenNodeForBranch(branch, 0, context) 0,
]) as IfCodegenNode context
) as IfConditionalExpression
} else { } else {
// attach this branch's codegen node to the v-if root. // attach this branch's codegen node to the v-if root.
let parentCondition = ifNode.codegenNode! let parentCondition = ifNode.codegenNode!
.expressions[1] as ConditionalExpression
while ( while (
parentCondition.alternate.type === parentCondition.alternate.type ===
NodeTypes.JS_CONDITIONAL_EXPRESSION NodeTypes.JS_CONDITIONAL_EXPRESSION
@ -175,7 +167,7 @@ function createCodegenNodeForBranch(
]) ])
) as IfConditionalExpression ) as IfConditionalExpression
} else { } else {
return createChildrenCodegenNode(branch, index, context) as BlockCodegenNode return createChildrenCodegenNode(branch, index, context)
} }
} }
@ -183,7 +175,7 @@ function createChildrenCodegenNode(
branch: IfBranchNode, branch: IfBranchNode,
index: number, index: number,
context: TransformContext context: TransformContext
): CallExpression { ): BlockCodegenNode {
const { helper } = context const { helper } = context
const keyProperty = createObjectProperty( const keyProperty = createObjectProperty(
`key`, `key`,
@ -194,35 +186,36 @@ function createChildrenCodegenNode(
const needFragmentWrapper = const needFragmentWrapper =
children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT children.length !== 1 || firstChild.type !== NodeTypes.ELEMENT
if (needFragmentWrapper) { if (needFragmentWrapper) {
const blockArgs: CallExpression['arguments'] = [
helper(FRAGMENT),
createObjectExpression([keyProperty]),
children
]
if (children.length === 1 && firstChild.type === NodeTypes.FOR) { if (children.length === 1 && firstChild.type === NodeTypes.FOR) {
// optimize away nested fragments when child is a ForNode // optimize away nested fragments when child is a ForNode
const forBlockArgs = firstChild.codegenNode!.expressions[1].arguments const vnodeCall = firstChild.codegenNode!
// directly use the for block's children and patchFlag injectProp(vnodeCall, keyProperty, context)
blockArgs[2] = forBlockArgs[2] return vnodeCall
blockArgs[3] = forBlockArgs[3]
}
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
} else { } else {
const childCodegen = (firstChild as ElementNode).codegenNode as return createVNodeCall(
| ElementCodegenNode context,
| ComponentCodegenNode helper(FRAGMENT),
| SlotOutletCodegenNode createObjectExpression([keyProperty]),
let vnodeCall = childCodegen children,
// Element with custom directives. Locate the actual createVNode() call. undefined,
if (vnodeCall.callee === WITH_DIRECTIVES) { undefined,
vnodeCall = vnodeCall.arguments[0] undefined,
true,
false,
branch.loc
)
} }
} else {
const vnodeCall = (firstChild as ElementNode)
.codegenNode as BlockCodegenNode
// Change createVNode to createBlock. // Change createVNode to createBlock.
if (vnodeCall.callee === CREATE_VNODE) { if (vnodeCall.type === NodeTypes.VNODE_CALL) {
vnodeCall.callee = helper(CREATE_BLOCK) vnodeCall.isBlock = true
helper(OPEN_BLOCK)
helper(CREATE_BLOCK)
} }
// inject branch key // inject branch key
injectProp(vnodeCall, keyProperty, context) injectProp(vnodeCall, keyProperty, context)
return childCodegen return vnodeCall
} }
} }

View File

@ -19,7 +19,8 @@ import {
FunctionExpression, FunctionExpression,
CallExpression, CallExpression,
createCallExpression, createCallExpression,
createArrayExpression createArrayExpression,
SlotsExpression
} from '../ast' } from '../ast'
import { TransformContext, NodeTransform } from '../transform' import { TransformContext, NodeTransform } from '../transform'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
@ -115,7 +116,7 @@ export function buildSlots(
context: TransformContext, context: TransformContext,
buildSlotFn: SlotFnBuilder = buildClientSlotFn buildSlotFn: SlotFnBuilder = buildClientSlotFn
): { ): {
slots: ObjectExpression | CallExpression slots: SlotsExpression
hasDynamicSlots: boolean hasDynamicSlots: boolean
} { } {
const { children, loc } = node const { children, loc } = node
@ -312,17 +313,17 @@ export function buildSlots(
} }
} }
let slots: ObjectExpression | CallExpression = createObjectExpression( let slots = createObjectExpression(
slotsProperties.concat( slotsProperties.concat(
createObjectProperty(`_compiled`, createSimpleExpression(`true`, false)) createObjectProperty(`_compiled`, createSimpleExpression(`true`, false))
), ),
loc loc
) ) as SlotsExpression
if (dynamicSlots.length) { if (dynamicSlots.length) {
slots = createCallExpression(context.helper(CREATE_SLOTS), [ slots = createCallExpression(context.helper(CREATE_SLOTS), [
slots, slots,
createArrayExpression(dynamicSlots) createArrayExpression(dynamicSlots)
]) ]) as SlotsExpression
} }
return { return {

View File

@ -4,8 +4,6 @@ import {
ElementNode, ElementNode,
NodeTypes, NodeTypes,
CallExpression, CallExpression,
SequenceExpression,
createSequenceExpression,
createCallExpression, createCallExpression,
DirectiveNode, DirectiveNode,
ElementTypes, ElementTypes,
@ -17,22 +15,18 @@ import {
createObjectExpression, createObjectExpression,
SlotOutletNode, SlotOutletNode,
TemplateNode, TemplateNode,
BlockCodegenNode, RenderSlotCall,
ElementCodegenNode,
SlotOutletCodegenNode,
ComponentCodegenNode,
ExpressionNode, ExpressionNode,
IfBranchNode, IfBranchNode,
TextNode, TextNode,
InterpolationNode InterpolationNode,
VNodeCall
} from './ast' } from './ast'
import { parse } from 'acorn' import { parse } from 'acorn'
import { walk } from 'estree-walker' import { walk } from 'estree-walker'
import { TransformContext } from './transform' import { TransformContext } from './transform'
import { import {
OPEN_BLOCK,
MERGE_PROPS, MERGE_PROPS,
RENDER_SLOT,
PORTAL, PORTAL,
SUSPENSE, SUSPENSE,
KEEP_ALIVE, KEEP_ALIVE,
@ -218,16 +212,6 @@ export function hasDynamicKeyVBind(node: ElementNode): boolean {
) )
} }
export function createBlockExpression(
blockExp: BlockCodegenNode,
context: TransformContext
): SequenceExpression {
return createSequenceExpression([
createCallExpression(context.helper(OPEN_BLOCK)),
blockExp
])
}
export function isText( export function isText(
node: TemplateChildNode node: TemplateChildNode
): node is TextNode | InterpolationNode { ): node is TextNode | InterpolationNode {
@ -253,13 +237,13 @@ export function isSlotOutlet(
} }
export function injectProp( export function injectProp(
node: ElementCodegenNode | ComponentCodegenNode | SlotOutletCodegenNode, node: VNodeCall | RenderSlotCall,
prop: Property, prop: Property,
context: TransformContext context: TransformContext
) { ) {
let propsWithInjection: ObjectExpression | CallExpression let propsWithInjection: ObjectExpression | CallExpression
const props = 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)) { if (props == null || isString(props)) {
propsWithInjection = createObjectExpression([prop]) propsWithInjection = createObjectExpression([prop])
} else if (props.type === NodeTypes.JS_CALL_EXPRESSION) { } else if (props.type === NodeTypes.JS_CALL_EXPRESSION) {
@ -295,10 +279,10 @@ export function injectProp(
props props
]) ])
} }
if (node.callee === RENDER_SLOT) { if (node.type === NodeTypes.VNODE_CALL) {
node.arguments[2] = propsWithInjection node.props = propsWithInjection
} else { } else {
node.arguments[1] = propsWithInjection node.arguments[2] = propsWithInjection
} }
} }

View File

@ -335,7 +335,7 @@ export function createCommentVNode(
asBlock: boolean = false asBlock: boolean = false
): VNode { ): VNode {
return asBlock return asBlock
? createBlock(Comment, null, text) ? (openBlock(), createBlock(Comment, null, text))
: createVNode(Comment, null, text) : createVNode(Comment, null, text)
} }