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 { 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'],
|
||||||
|
@ -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}] || (`)
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
helper(FRAGMENT),
|
||||||
createCallExpression(helper(OPEN_BLOCK), [`true`]),
|
undefined,
|
||||||
createCallExpression(helper(CREATE_BLOCK), [
|
renderExp,
|
||||||
helper(FRAGMENT),
|
`${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`,
|
||||||
`null`,
|
undefined,
|
||||||
renderExp,
|
undefined,
|
||||||
`${fragmentFlag} /* ${PatchFlagNames[fragmentFlag]} */`
|
true /* isBlock */,
|
||||||
])
|
true /* isForBlock */,
|
||||||
]) as ForCodegenNode
|
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)
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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]
|
} else {
|
||||||
|
return createVNodeCall(
|
||||||
|
context,
|
||||||
|
helper(FRAGMENT),
|
||||||
|
createObjectExpression([keyProperty]),
|
||||||
|
children,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
branch.loc
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return createCallExpression(helper(CREATE_BLOCK), blockArgs)
|
|
||||||
} else {
|
} else {
|
||||||
const childCodegen = (firstChild as ElementNode).codegenNode as
|
const vnodeCall = (firstChild as ElementNode)
|
||||||
| ElementCodegenNode
|
.codegenNode as BlockCodegenNode
|
||||||
| ComponentCodegenNode
|
|
||||||
| SlotOutletCodegenNode
|
|
||||||
let vnodeCall = childCodegen
|
|
||||||
// Element with custom directives. Locate the actual createVNode() call.
|
|
||||||
if (vnodeCall.callee === WITH_DIRECTIVES) {
|
|
||||||
vnodeCall = vnodeCall.arguments[0]
|
|
||||||
}
|
|
||||||
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user