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:
parent
fe9da2d0e4
commit
e3988b40d8
@ -1,17 +1,17 @@
|
||||
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
|
||||
@ -38,12 +38,12 @@ export const enum NodeTypes {
|
||||
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,
|
||||
|
||||
@ -123,21 +123,14 @@ export interface BaseElementNode extends Node {
|
||||
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
|
||||
}
|
||||
@ -145,7 +138,7 @@ export interface PlainElementNode extends BaseElementNode {
|
||||
export interface ComponentNode extends BaseElementNode {
|
||||
tagType: ElementTypes.COMPONENT
|
||||
codegenNode:
|
||||
| ComponentCodegenNode
|
||||
| VNodeCall
|
||||
| CacheExpression // when cached by v-once
|
||||
| undefined
|
||||
ssrCodegenNode?: CallExpression
|
||||
@ -153,13 +146,17 @@ export interface ComponentNode extends BaseElementNode {
|
||||
|
||||
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 {
|
||||
@ -220,7 +217,7 @@ export interface CompoundExpressionNode extends Node {
|
||||
export interface IfNode extends Node {
|
||||
type: NodeTypes.IF
|
||||
branches: IfBranchNode[]
|
||||
codegenNode?: IfCodegenNode
|
||||
codegenNode?: IfConditionalExpression
|
||||
}
|
||||
|
||||
export interface IfBranchNode extends Node {
|
||||
@ -246,6 +243,28 @@ export interface TextCallNode 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.
|
||||
@ -253,13 +272,13 @@ export interface TextCallNode extends Node {
|
||||
// Vue render function generation.
|
||||
|
||||
export type JSChildNode =
|
||||
| VNodeCall
|
||||
| CallExpression
|
||||
| ObjectExpression
|
||||
| ArrayExpression
|
||||
| ExpressionNode
|
||||
| FunctionExpression
|
||||
| ConditionalExpression
|
||||
| SequenceExpression
|
||||
| CacheExpression
|
||||
| AssignmentExpression
|
||||
|
||||
@ -301,11 +320,6 @@ export interface FunctionExpression extends Node {
|
||||
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
|
||||
@ -360,57 +374,31 @@ export interface ReturnStatement extends Node {
|
||||
|
||||
// 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>
|
||||
|
||||
// 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
|
||||
]
|
||||
| [
|
||||
string | symbol,
|
||||
'null' | PropsExpression,
|
||||
'null' | SlotsExpression,
|
||||
string,
|
||||
string
|
||||
]
|
||||
export interface DirectiveArgumentNode extends ArrayExpression {
|
||||
elements: // dir, exp, arg, modifiers
|
||||
| [string]
|
||||
| [string, ExpressionNode]
|
||||
| [string, ExpressionNode, ExpressionNode]
|
||||
| [string, ExpressionNode, ExpressionNode, ObjectExpression]
|
||||
}
|
||||
|
||||
export type ComponentCodegenNode =
|
||||
| PlainComponentCodegenNode
|
||||
| CodegenNodeWithDirective<PlainComponentCodegenNode>
|
||||
// renderSlot(...)
|
||||
export interface RenderSlotCall 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 SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
|
||||
|
||||
@ -462,63 +450,20 @@ export interface DynamicSlotFnProperty extends Property {
|
||||
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 {
|
||||
@ -530,11 +475,6 @@ export interface ForIteratorExpression extends FunctionExpression {
|
||||
returns: BlockCodegenNode
|
||||
}
|
||||
|
||||
export interface OpenBlockExpression extends CallExpression {
|
||||
callee: typeof OPEN_BLOCK
|
||||
arguments: []
|
||||
}
|
||||
|
||||
// AST Utilities ---------------------------------------------------------------
|
||||
|
||||
// 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(
|
||||
elements: ArrayExpression['elements'],
|
||||
loc: SourceLocation = locStub
|
||||
@ -638,15 +614,9 @@ export function createCompoundExpression(
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@ -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(
|
||||
test: ConditionalExpression['test'],
|
||||
consequent: ConditionalExpression['consequent'],
|
||||
|
@ -15,7 +15,6 @@ import {
|
||||
CompoundExpressionNode,
|
||||
SimpleExpressionNode,
|
||||
FunctionExpression,
|
||||
SequenceExpression,
|
||||
ConditionalExpression,
|
||||
CacheExpression,
|
||||
locStub,
|
||||
@ -23,7 +22,8 @@ import {
|
||||
TemplateLiteral,
|
||||
IfStatement,
|
||||
AssignmentExpression,
|
||||
ReturnStatement
|
||||
ReturnStatement,
|
||||
VNodeCall
|
||||
} from './ast'
|
||||
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||
import {
|
||||
@ -45,7 +45,10 @@ 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'
|
||||
|
||||
@ -547,6 +550,10 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
||||
case NodeTypes.COMMENT:
|
||||
genComment(node, context)
|
||||
break
|
||||
case NodeTypes.VNODE_CALL:
|
||||
genVNodeCall(node, context)
|
||||
break
|
||||
|
||||
case NodeTypes.JS_CALL_EXPRESSION:
|
||||
genCallExpression(node, context)
|
||||
break
|
||||
@ -559,9 +566,6 @@ function genNode(node: CodegenNode | symbol | string, context: CodegenContext) {
|
||||
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
|
||||
@ -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
|
||||
function genCallExpression(node: CallExpression, context: CodegenContext) {
|
||||
const callee = isString(node.callee)
|
||||
@ -782,15 +828,6 @@ function genConditionalExpression(
|
||||
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}] || (`)
|
||||
|
@ -12,12 +12,10 @@ import {
|
||||
JSChildNode,
|
||||
SimpleExpressionNode,
|
||||
ElementTypes,
|
||||
ElementCodegenNode,
|
||||
ComponentCodegenNode,
|
||||
createCallExpression,
|
||||
CacheExpression,
|
||||
createCacheExpression,
|
||||
TemplateLiteral
|
||||
TemplateLiteral,
|
||||
createVNodeCall
|
||||
} from './ast'
|
||||
import {
|
||||
isString,
|
||||
@ -31,11 +29,11 @@ import {
|
||||
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:
|
||||
@ -286,20 +284,13 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
||||
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.
|
||||
@ -308,16 +299,17 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
|
||||
}
|
||||
} 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.
|
||||
|
@ -8,11 +8,9 @@ import {
|
||||
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'
|
||||
|
||||
@ -60,7 +58,7 @@ function walk(
|
||||
// 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 ||
|
||||
@ -70,8 +68,8 @@ function walk(
|
||||
!hasCachedProps(child)
|
||||
) {
|
||||
const props = getNodeProps(child)
|
||||
if (props && props !== `null`) {
|
||||
getVNodeCall(codegenNode).arguments[1] = context.hoist(props)
|
||||
if (props) {
|
||||
codegenNode.props = context.hoist(props)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,7 +109,7 @@ export function isStaticNode(
|
||||
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)
|
||||
@ -123,6 +121,12 @@ export function isStaticNode(
|
||||
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 {
|
||||
@ -164,11 +168,7 @@ function hasCachedProps(node: PlainElementNode): boolean {
|
||||
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) {
|
||||
@ -181,30 +181,12 @@ function hasCachedProps(node: PlainElementNode): boolean {
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -14,23 +14,22 @@ import {
|
||||
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,
|
||||
@ -63,6 +62,20 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
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
|
||||
@ -70,38 +83,24 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
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.
|
||||
@ -125,13 +124,13 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
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
|
||||
}
|
||||
@ -148,60 +147,44 @@ export const transformElement: NodeTransform = (node, context) => {
|
||||
// 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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,26 +8,28 @@ import {
|
||||
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
|
||||
@ -36,8 +38,7 @@ import {
|
||||
RENDER_LIST,
|
||||
OPEN_BLOCK,
|
||||
CREATE_BLOCK,
|
||||
FRAGMENT,
|
||||
WITH_DIRECTIVES
|
||||
FRAGMENT
|
||||
} from '../runtimeHelpers'
|
||||
import { processExpression } from './transformExpression'
|
||||
import { PatchFlags, PatchFlagNames } from '@vue/shared'
|
||||
@ -51,26 +52,27 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
// 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 =
|
||||
@ -92,7 +94,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
: 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.
|
||||
@ -102,37 +104,33 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
} 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -10,31 +10,23 @@ import {
|
||||
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'
|
||||
|
||||
@ -46,14 +38,14 @@ export const transformIf = createStructuralDirectiveTransform(
|
||||
// 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
|
||||
@ -175,7 +167,7 @@ function createCodegenNodeForBranch(
|
||||
])
|
||||
) as IfConditionalExpression
|
||||
} else {
|
||||
return createChildrenCodegenNode(branch, index, context) as BlockCodegenNode
|
||||
return createChildrenCodegenNode(branch, index, context)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +175,7 @@ function createChildrenCodegenNode(
|
||||
branch: IfBranchNode,
|
||||
index: number,
|
||||
context: TransformContext
|
||||
): CallExpression {
|
||||
): BlockCodegenNode {
|
||||
const { helper } = context
|
||||
const keyProperty = createObjectProperty(
|
||||
`key`,
|
||||
@ -194,35 +186,36 @@ function createChildrenCodegenNode(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ import {
|
||||
FunctionExpression,
|
||||
CallExpression,
|
||||
createCallExpression,
|
||||
createArrayExpression
|
||||
createArrayExpression,
|
||||
SlotsExpression
|
||||
} from '../ast'
|
||||
import { TransformContext, NodeTransform } from '../transform'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
@ -115,7 +116,7 @@ export function buildSlots(
|
||||
context: TransformContext,
|
||||
buildSlotFn: SlotFnBuilder = buildClientSlotFn
|
||||
): {
|
||||
slots: ObjectExpression | CallExpression
|
||||
slots: SlotsExpression
|
||||
hasDynamicSlots: boolean
|
||||
} {
|
||||
const { children, loc } = node
|
||||
@ -312,17 +313,17 @@ export function buildSlots(
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -4,8 +4,6 @@ import {
|
||||
ElementNode,
|
||||
NodeTypes,
|
||||
CallExpression,
|
||||
SequenceExpression,
|
||||
createSequenceExpression,
|
||||
createCallExpression,
|
||||
DirectiveNode,
|
||||
ElementTypes,
|
||||
@ -17,22 +15,18 @@ import {
|
||||
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,
|
||||
@ -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(
|
||||
node: TemplateChildNode
|
||||
): node is TextNode | InterpolationNode {
|
||||
@ -253,13 +237,13 @@ export function isSlotOutlet(
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -295,10 +279,10 @@ export function injectProp(
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ export function createCommentVNode(
|
||||
asBlock: boolean = false
|
||||
): VNode {
|
||||
return asBlock
|
||||
? createBlock(Comment, null, text)
|
||||
? (openBlock(), createBlock(Comment, null, text))
|
||||
: createVNode(Comment, null, text)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user