Skip to content

Commit e3988b4

Browse files
committed
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.
1 parent fe9da2d commit e3988b4

File tree

10 files changed

+337
-407
lines changed

10 files changed

+337
-407
lines changed

packages/compiler-core/src/ast.ts

Lines changed: 101 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { isString } from '@vue/shared'
22
import { ForParseResult } from './transforms/vFor'
33
import {
4-
CREATE_VNODE,
5-
WITH_DIRECTIVES,
64
RENDER_SLOT,
75
CREATE_SLOTS,
86
RENDER_LIST,
97
OPEN_BLOCK,
108
CREATE_BLOCK,
11-
FRAGMENT
9+
FRAGMENT,
10+
CREATE_VNODE,
11+
WITH_DIRECTIVES
1212
} from './runtimeHelpers'
1313
import { PropsExpression } from './transforms/transformElement'
14-
import { ImportItem } from './transform'
14+
import { ImportItem, TransformContext } from './transform'
1515

1616
// Vue template is a platform-agnostic superset of HTML (syntax only).
1717
// More namespaces like SVG and MathML are declared by platform specific
@@ -38,12 +38,12 @@ export const enum NodeTypes {
3838
FOR,
3939
TEXT_CALL,
4040
// codegen
41+
VNODE_CALL,
4142
JS_CALL_EXPRESSION,
4243
JS_OBJECT_EXPRESSION,
4344
JS_PROPERTY,
4445
JS_ARRAY_EXPRESSION,
4546
JS_FUNCTION_EXPRESSION,
46-
JS_SEQUENCE_EXPRESSION,
4747
JS_CONDITIONAL_EXPRESSION,
4848
JS_CACHE_EXPRESSION,
4949

@@ -123,43 +123,40 @@ export interface BaseElementNode extends Node {
123123
isSelfClosing: boolean
124124
props: Array<AttributeNode | DirectiveNode>
125125
children: TemplateChildNode[]
126-
codegenNode:
127-
| CallExpression
128-
| SimpleExpressionNode
129-
| CacheExpression
130-
| SequenceExpression
131-
| undefined
132126
}
133127

134128
export interface PlainElementNode extends BaseElementNode {
135129
tagType: ElementTypes.ELEMENT
136130
codegenNode:
137-
| ElementCodegenNode
131+
| VNodeCall
138132
| SimpleExpressionNode // when hoisted
139133
| CacheExpression // when cached by v-once
140-
| SequenceExpression // when turned into a block
141134
| undefined
142135
ssrCodegenNode?: TemplateLiteral
143136
}
144137

145138
export interface ComponentNode extends BaseElementNode {
146139
tagType: ElementTypes.COMPONENT
147140
codegenNode:
148-
| ComponentCodegenNode
141+
| VNodeCall
149142
| CacheExpression // when cached by v-once
150143
| undefined
151144
ssrCodegenNode?: CallExpression
152145
}
153146

154147
export interface SlotOutletNode extends BaseElementNode {
155148
tagType: ElementTypes.SLOT
156-
codegenNode: SlotOutletCodegenNode | undefined | CacheExpression // when cached by v-once
149+
codegenNode:
150+
| RenderSlotCall
151+
| CacheExpression // when cached by v-once
152+
| undefined
157153
ssrCodegenNode?: CallExpression
158154
}
159155

160156
export interface TemplateNode extends BaseElementNode {
161157
tagType: ElementTypes.TEMPLATE
162158
// TemplateNode is a container type that always gets compiled away
159+
codegenNode: undefined
163160
}
164161

165162
export interface TextNode extends Node {
@@ -220,7 +217,7 @@ export interface CompoundExpressionNode extends Node {
220217
export interface IfNode extends Node {
221218
type: NodeTypes.IF
222219
branches: IfBranchNode[]
223-
codegenNode?: IfCodegenNode
220+
codegenNode?: IfConditionalExpression
224221
}
225222

226223
export interface IfBranchNode extends Node {
@@ -246,20 +243,42 @@ export interface TextCallNode extends Node {
246243
codegenNode: CallExpression | SimpleExpressionNode // when hoisted
247244
}
248245

246+
export type TemplateTextChildNode =
247+
| TextNode
248+
| InterpolationNode
249+
| CompoundExpressionNode
250+
251+
export interface VNodeCall extends Node {
252+
type: NodeTypes.VNODE_CALL
253+
tag: string | symbol | CallExpression
254+
props: PropsExpression | undefined
255+
children:
256+
| TemplateChildNode[] // multiple children
257+
| TemplateTextChildNode // single text child
258+
| SlotsExpression // component slots
259+
| ForRenderListExpression // v-for fragment call
260+
| undefined
261+
patchFlag: string | undefined
262+
dynamicProps: string | undefined
263+
directives: DirectiveArguments | undefined
264+
isBlock: boolean
265+
isForBlock: boolean
266+
}
267+
249268
// JS Node Types ---------------------------------------------------------------
250269

251270
// We also include a number of JavaScript AST nodes for code generation.
252271
// The AST is an intentionally minimal subset just to meet the exact needs of
253272
// Vue render function generation.
254273

255274
export type JSChildNode =
275+
| VNodeCall
256276
| CallExpression
257277
| ObjectExpression
258278
| ArrayExpression
259279
| ExpressionNode
260280
| FunctionExpression
261281
| ConditionalExpression
262-
| SequenceExpression
263282
| CacheExpression
264283
| AssignmentExpression
265284

@@ -301,11 +320,6 @@ export interface FunctionExpression extends Node {
301320
isSlot: boolean
302321
}
303322

304-
export interface SequenceExpression extends Node {
305-
type: NodeTypes.JS_SEQUENCE_EXPRESSION
306-
expressions: JSChildNode[]
307-
}
308-
309323
export interface ConditionalExpression extends Node {
310324
type: NodeTypes.JS_CONDITIONAL_EXPRESSION
311325
test: JSChildNode
@@ -360,58 +374,32 @@ export interface ReturnStatement extends Node {
360374

361375
// Codegen Node Types ----------------------------------------------------------
362376

363-
// createVNode(...)
364-
export interface PlainElementCodegenNode extends CallExpression {
365-
callee: typeof CREATE_VNODE | typeof CREATE_BLOCK
366-
arguments: // tag, props, children, patchFlag, dynamicProps
367-
| [string | symbol]
368-
| [string | symbol, PropsExpression]
369-
| [string | symbol, 'null' | PropsExpression, TemplateChildNode[]]
370-
| [
371-
string | symbol,
372-
'null' | PropsExpression,
373-
'null' | TemplateChildNode[],
374-
string
375-
]
376-
| [
377-
string | symbol,
378-
'null' | PropsExpression,
379-
'null' | TemplateChildNode[],
380-
string,
381-
string
382-
]
377+
export interface DirectiveArguments extends ArrayExpression {
378+
elements: DirectiveArgumentNode[]
383379
}
384380

385-
export type ElementCodegenNode =
386-
| PlainElementCodegenNode
387-
| CodegenNodeWithDirective<PlainElementCodegenNode>
381+
export interface DirectiveArgumentNode extends ArrayExpression {
382+
elements: // dir, exp, arg, modifiers
383+
| [string]
384+
| [string, ExpressionNode]
385+
| [string, ExpressionNode, ExpressionNode]
386+
| [string, ExpressionNode, ExpressionNode, ObjectExpression]
387+
}
388388

389-
// createVNode(...)
390-
export interface PlainComponentCodegenNode extends CallExpression {
391-
callee: typeof CREATE_VNODE | typeof CREATE_BLOCK
392-
arguments: // Comp, props, slots, patchFlag, dynamicProps
393-
| [string | symbol]
394-
| [string | symbol, PropsExpression]
395-
| [string | symbol, 'null' | PropsExpression, SlotsExpression]
396-
| [
397-
string | symbol,
398-
'null' | PropsExpression,
399-
'null' | SlotsExpression,
400-
string
401-
]
389+
// renderSlot(...)
390+
export interface RenderSlotCall extends CallExpression {
391+
callee: typeof RENDER_SLOT
392+
arguments: // $slots, name, props, fallback
393+
| [string, string | ExpressionNode]
394+
| [string, string | ExpressionNode, PropsExpression]
402395
| [
403-
string | symbol,
404-
'null' | PropsExpression,
405-
'null' | SlotsExpression,
406396
string,
407-
string
397+
string | ExpressionNode,
398+
PropsExpression | '{}',
399+
TemplateChildNode[]
408400
]
409401
}
410402

411-
export type ComponentCodegenNode =
412-
| PlainComponentCodegenNode
413-
| CodegenNodeWithDirective<PlainComponentCodegenNode>
414-
415403
export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
416404

417405
// { foo: () => [...] }
@@ -462,63 +450,20 @@ export interface DynamicSlotFnProperty extends Property {
462450
value: SlotFunctionExpression
463451
}
464452

465-
// withDirectives(createVNode(...), [
466-
// [_directive_foo, someValue],
467-
// [_directive_bar, someValue, "arg", { mod: true }]
468-
// ])
469-
export interface CodegenNodeWithDirective<T extends CallExpression>
470-
extends CallExpression {
471-
callee: typeof WITH_DIRECTIVES
472-
arguments: [T, DirectiveArguments]
473-
}
474-
475-
export interface DirectiveArguments extends ArrayExpression {
476-
elements: DirectiveArgumentNode[]
477-
}
478-
479-
export interface DirectiveArgumentNode extends ArrayExpression {
480-
elements: // dir, exp, arg, modifiers
481-
| [string]
482-
| [string, ExpressionNode]
483-
| [string, ExpressionNode, ExpressionNode]
484-
| [string, ExpressionNode, ExpressionNode, ObjectExpression]
485-
}
486-
487-
// renderSlot(...)
488-
export interface SlotOutletCodegenNode extends CallExpression {
489-
callee: typeof RENDER_SLOT
490-
arguments: // $slots, name, props, fallback
491-
| [string, string | ExpressionNode]
492-
| [string, string | ExpressionNode, PropsExpression]
493-
| [
494-
string,
495-
string | ExpressionNode,
496-
PropsExpression | '{}',
497-
TemplateChildNode[]
498-
]
499-
}
500-
501-
export type BlockCodegenNode =
502-
| ElementCodegenNode
503-
| ComponentCodegenNode
504-
| SlotOutletCodegenNode
505-
506-
export interface IfCodegenNode extends SequenceExpression {
507-
expressions: [OpenBlockExpression, IfConditionalExpression]
508-
}
453+
export type BlockCodegenNode = VNodeCall | RenderSlotCall
509454

510455
export interface IfConditionalExpression extends ConditionalExpression {
511456
consequent: BlockCodegenNode
512457
alternate: BlockCodegenNode | IfConditionalExpression
513458
}
514459

515-
export interface ForCodegenNode extends SequenceExpression {
516-
expressions: [OpenBlockExpression, ForBlockCodegenNode]
517-
}
518-
519-
export interface ForBlockCodegenNode extends CallExpression {
520-
callee: typeof CREATE_BLOCK
521-
arguments: [typeof FRAGMENT, 'null', ForRenderListExpression, string]
460+
export interface ForCodegenNode extends VNodeCall {
461+
isBlock: true
462+
tag: typeof FRAGMENT
463+
props: undefined
464+
children: ForRenderListExpression
465+
patchFlag: string
466+
isForBlock: true
522467
}
523468

524469
export interface ForRenderListExpression extends CallExpression {
@@ -530,11 +475,6 @@ export interface ForIteratorExpression extends FunctionExpression {
530475
returns: BlockCodegenNode
531476
}
532477

533-
export interface OpenBlockExpression extends CallExpression {
534-
callee: typeof OPEN_BLOCK
535-
arguments: []
536-
}
537-
538478
// AST Utilities ---------------------------------------------------------------
539479

540480
// Some expressions, e.g. sequence and conditional expressions, are never
@@ -565,6 +505,42 @@ export function createRoot(
565505
}
566506
}
567507

508+
export function createVNodeCall(
509+
context: TransformContext,
510+
tag: VNodeCall['tag'],
511+
props?: VNodeCall['props'],
512+
children?: VNodeCall['children'],
513+
patchFlag?: VNodeCall['patchFlag'],
514+
dynamicProps?: VNodeCall['dynamicProps'],
515+
directives?: VNodeCall['directives'],
516+
isBlock: VNodeCall['isBlock'] = false,
517+
isForBlock: VNodeCall['isForBlock'] = false,
518+
loc = locStub
519+
): VNodeCall {
520+
if (isBlock) {
521+
context.helper(OPEN_BLOCK)
522+
context.helper(CREATE_BLOCK)
523+
} else {
524+
context.helper(CREATE_VNODE)
525+
}
526+
if (directives) {
527+
context.helper(WITH_DIRECTIVES)
528+
}
529+
530+
return {
531+
type: NodeTypes.VNODE_CALL,
532+
tag,
533+
props,
534+
children,
535+
patchFlag,
536+
dynamicProps,
537+
directives,
538+
isBlock,
539+
isForBlock,
540+
loc
541+
}
542+
}
543+
568544
export function createArrayExpression(
569545
elements: ArrayExpression['elements'],
570546
loc: SourceLocation = locStub
@@ -638,15 +614,9 @@ export function createCompoundExpression(
638614
}
639615
}
640616

641-
type InferCodegenNodeType<T> = T extends
642-
| typeof CREATE_VNODE
643-
| typeof CREATE_BLOCK
644-
? PlainElementCodegenNode | PlainComponentCodegenNode
645-
: T extends typeof WITH_DIRECTIVES
646-
?
647-
| CodegenNodeWithDirective<PlainElementCodegenNode>
648-
| CodegenNodeWithDirective<PlainComponentCodegenNode>
649-
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
617+
type InferCodegenNodeType<T> = T extends typeof RENDER_SLOT
618+
? RenderSlotCall
619+
: CallExpression
650620

651621
export function createCallExpression<T extends CallExpression['callee']>(
652622
callee: T,
@@ -678,16 +648,6 @@ export function createFunctionExpression(
678648
}
679649
}
680650

681-
export function createSequenceExpression(
682-
expressions: SequenceExpression['expressions']
683-
): SequenceExpression {
684-
return {
685-
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
686-
expressions,
687-
loc: locStub
688-
}
689-
}
690-
691651
export function createConditionalExpression(
692652
test: ConditionalExpression['test'],
693653
consequent: ConditionalExpression['consequent'],

0 commit comments

Comments
 (0)