refactor(compiler): refine codegen node types

This commit is contained in:
Evan You 2019-10-05 22:47:20 -04:00
parent bfecf2cdce
commit 82bd9eb1db
10 changed files with 636 additions and 288 deletions

View File

@ -1,5 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`compiler: codegen ArrayExpression 1`] = `
"
return function render() {
with (this) {
return [
foo,
bar(baz)
]
}
}"
`;
exports[`compiler: codegen ConditionalExpression 1`] = ` exports[`compiler: codegen ConditionalExpression 1`] = `
" "
return function render() { return function render() {
@ -13,7 +25,7 @@ return function render() {
}" }"
`; `;
exports[`compiler: codegen Element (callExpression + objectExpression + arrayExpression) 1`] = ` exports[`compiler: codegen Element (callExpression + objectExpression + TemplateChildNode[]) 1`] = `
" "
return function render() { return function render() {
with (this) { with (this) {
@ -23,10 +35,7 @@ return function render() {
[foo + bar]: bar [foo + bar]: bar
}, [ }, [
_createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" }) _createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
], [ ], 16)
foo,
_createVNode(\\"p\\")
])
} }
}" }"
`; `;

View File

@ -11,7 +11,9 @@ import {
createInterpolation, createInterpolation,
createSequenceExpression, createSequenceExpression,
createCallExpression, createCallExpression,
createConditionalExpression createConditionalExpression,
IfCodegenNode,
ForCodegenNode
} from '../src' } from '../src'
import { import {
CREATE_VNODE, CREATE_VNODE,
@ -22,6 +24,7 @@ import {
RESOLVE_COMPONENT RESOLVE_COMPONENT
} from '../src/runtimeHelpers' } from '../src/runtimeHelpers'
import { createElementWithCodegen } from './testUtils' import { createElementWithCodegen } from './testUtils'
import { PatchFlags } from 'vue'
function createRoot(options: Partial<RootNode> = {}): RootNode { function createRoot(options: Partial<RootNode> = {}): RootNode {
return { return {
@ -205,7 +208,7 @@ describe('compiler: codegen', () => {
codegenNode: createSequenceExpression([ codegenNode: createSequenceExpression([
createSimpleExpression('foo', false), createSimpleExpression('foo', false),
createSimpleExpression('bar', false) createSimpleExpression('bar', false)
]) ]) as IfCodegenNode
} }
}) })
) )
@ -227,7 +230,7 @@ describe('compiler: codegen', () => {
codegenNode: createSequenceExpression([ codegenNode: createSequenceExpression([
createSimpleExpression('foo', false), createSimpleExpression('foo', false),
createSimpleExpression('bar', false) createSimpleExpression('bar', false)
]) ]) as ForCodegenNode
} }
}) })
) )
@ -235,7 +238,7 @@ describe('compiler: codegen', () => {
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
test('Element (callExpression + objectExpression + arrayExpression)', () => { test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
const { code } = generate( const { code } = generate(
createRoot({ createRoot({
codegenNode: createElementWithCodegen([ codegenNode: createElementWithCodegen([
@ -283,19 +286,8 @@ describe('compiler: codegen', () => {
) )
]) ])
], ],
// ArrayExpression // flag
createArrayExpression( PatchFlags.FULL_PROPS + ''
[
'foo',
{
type: NodeTypes.JS_CALL_EXPRESSION,
loc: locStub,
callee: CREATE_VNODE,
arguments: [`"p"`]
}
],
locStub
)
]) ])
}) })
) )
@ -306,10 +298,23 @@ describe('compiler: codegen', () => {
[foo + bar]: bar [foo + bar]: bar
}, [ }, [
_${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" }) _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
], [ ], ${PatchFlags.FULL_PROPS})`)
expect(code).toMatchSnapshot()
})
test('ArrayExpression', () => {
const { code } = generate(
createRoot({
codegenNode: createArrayExpression([
createSimpleExpression(`foo`, false),
createCallExpression(`bar`, [`baz`])
])
})
)
expect(code).toMatch(`return [
foo, foo,
_${helperNameMap[CREATE_VNODE]}("p") bar(baz)
])`) ]`)
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })

View File

@ -1,10 +1,10 @@
import { import {
NodeTypes, NodeTypes,
CallExpression,
ElementNode, ElementNode,
locStub, locStub,
Namespaces, Namespaces,
ElementTypes ElementTypes,
ElementCodegenNode
} from '../src' } from '../src'
import { CREATE_VNODE } from '../src/runtimeHelpers' import { CREATE_VNODE } from '../src/runtimeHelpers'
import { isString } from '@vue/shared' import { isString } from '@vue/shared'
@ -39,7 +39,7 @@ export function createObjectMatcher(obj: any) {
} }
export function createElementWithCodegen( export function createElementWithCodegen(
args: CallExpression['arguments'] args: ElementCodegenNode['arguments']
): ElementNode { ): ElementNode {
return { return {
type: NodeTypes.ELEMENT, type: NodeTypes.ELEMENT,

View File

@ -1,6 +1,17 @@
import { isString } from '@vue/shared' import { isString } from '@vue/shared'
import { ForParseResult } from './transforms/vFor' import { ForParseResult } from './transforms/vFor'
import { CREATE_VNODE, RuntimeHelper } from './runtimeHelpers' import {
CREATE_VNODE,
RuntimeHelper,
APPLY_DIRECTIVES,
RENDER_SLOT,
CREATE_SLOTS,
RENDER_LIST,
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
// 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
@ -84,7 +95,13 @@ export interface RootNode extends Node {
codegenNode: TemplateChildNode | JSChildNode | undefined codegenNode: TemplateChildNode | JSChildNode | undefined
} }
export interface ElementNode extends Node { export type ElementNode =
| PlainElementNode
| ComponentNode
| SlotOutletNode
| TemplateNode
export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT type: NodeTypes.ELEMENT
ns: Namespace ns: Namespace
tag: string tag: string
@ -95,29 +112,32 @@ export interface ElementNode extends Node {
codegenNode: CallExpression | SimpleExpressionNode | undefined codegenNode: CallExpression | SimpleExpressionNode | undefined
} }
export interface PlainElementNode extends ElementNode { export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT tagType: ElementTypes.ELEMENT
codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode codegenNode:
| ElementCodegenNode
| CodegenNodeWithDirective<ElementCodegenNode>
| undefined
// | SimpleExpressionNode (only when hoisted)
} }
export interface ComponentNode extends ElementNode { export interface ComponentNode extends BaseElementNode {
tagType: ElementTypes.COMPONENT tagType: ElementTypes.COMPONENT
codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode codegenNode:
| ComponentCodegenNode
| CodegenNodeWithDirective<ComponentCodegenNode>
| undefined
} }
export interface SlotOutletNode extends ElementNode { export interface SlotOutletNode extends BaseElementNode {
tagType: ElementTypes.SLOT tagType: ElementTypes.SLOT
codegenNode: SlotOutletCodegenNode codegenNode: SlotOutletCodegenNode | undefined
} }
export interface VNodeCodegenNode extends CallExpression { export interface TemplateNode extends BaseElementNode {
callee: typeof CREATE_VNODE tagType: ElementTypes.TEMPLATE
} }
export interface VNodeWithDirectiveCodegenNode extends CallExpression {}
export interface SlotOutletCodegenNode extends CallExpression {}
export interface TextNode extends Node { export interface TextNode extends Node {
type: NodeTypes.TEXT type: NodeTypes.TEXT
content: string content: string
@ -179,8 +199,6 @@ export interface IfNode extends Node {
codegenNode: IfCodegenNode codegenNode: IfCodegenNode
} }
export interface IfCodegenNode extends SequenceExpression {}
export interface IfBranchNode extends Node { export interface IfBranchNode extends Node {
type: NodeTypes.IF_BRANCH type: NodeTypes.IF_BRANCH
condition: ExpressionNode | undefined // else condition: ExpressionNode | undefined // else
@ -197,8 +215,6 @@ export interface ForNode extends Node {
codegenNode: ForCodegenNode codegenNode: ForCodegenNode
} }
export interface ForCodegenNode extends SequenceExpression {}
// We also include a number of JavaScript AST nodes for code generation. // We also include a number of JavaScript AST nodes for code generation.
// The AST is an intentionally minimal subset just to meet the exact needs of // The AST is an intentionally minimal subset just to meet the exact needs of
// Vue render function generation. // Vue render function generation.
@ -257,6 +273,204 @@ export interface ConditionalExpression extends Node {
alternate: JSChildNode alternate: JSChildNode
} }
// Codegen Node Types ----------------------------------------------------------
// createVNode(...)
export interface ElementCodegenNode extends CallExpression {
callee: typeof CREATE_VNODE
arguments: // tag, props, children, patchFlag, dynamicProps
| [string | RuntimeHelper]
| [string | RuntimeHelper, PropsExpression]
| [string | RuntimeHelper, 'null' | PropsExpression, TemplateChildNode[]]
| [
string | RuntimeHelper,
'null' | PropsExpression,
'null' | TemplateChildNode[],
string
]
| [
string | RuntimeHelper,
'null' | PropsExpression,
'null' | TemplateChildNode[],
string,
string
]
}
export type ElementCodegenNodeWithDirective = CodegenNodeWithDirective<
ElementCodegenNode
>
// createVNode(...)
export interface ComponentCodegenNode extends CallExpression {
callee: typeof CREATE_VNODE
arguments: // Comp, props, slots, patchFlag, dynamicProps
| [string | RuntimeHelper]
| [string | RuntimeHelper, PropsExpression]
| [string | RuntimeHelper, 'null' | PropsExpression, SlotsExpression]
| [
string | RuntimeHelper,
'null' | PropsExpression,
'null' | SlotsExpression,
string
]
| [
string | RuntimeHelper,
'null' | PropsExpression,
'null' | SlotsExpression,
string,
string
]
}
export type CompoenntCodegenNodeWithDirective = CodegenNodeWithDirective<
ComponentCodegenNode
>
export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
// { foo: () => [...] }
export interface SlotsObjectExpression extends ObjectExpression {
properties: SlotsObjectProperty[]
}
export interface SlotsObjectProperty extends Property {
value: SlotFunctionExpression
}
export interface SlotFunctionExpression extends FunctionExpression {
returns: TemplateChildNode[]
}
// createSlots({ ... }, [
// foo ? () => [] : undefined,
// renderList(list, i => () => [i])
// ])
export interface DynamicSlotsExpression extends CallExpression {
callee: typeof CREATE_SLOTS
arguments: [SlotsObjectExpression, DynamicSlotEntries]
}
export interface DynamicSlotEntries extends ArrayExpression {
elements: (ConditionalDynamicSlotNode | ListDyanmicSlotNode)[]
}
export interface ConditionalDynamicSlotNode extends ConditionalExpression {
consequent: DynamicSlotNode
alternate: DynamicSlotNode | SimpleExpressionNode
}
export interface ListDyanmicSlotNode extends CallExpression {
callee: typeof RENDER_LIST
arguments: [ExpressionNode, ListDyanmicSlotIterator]
}
export interface ListDyanmicSlotIterator extends FunctionExpression {
returns: DynamicSlotNode
}
export interface DynamicSlotNode extends ObjectExpression {
properties: [Property, DynamicSlotFnProperty]
}
export interface DynamicSlotFnProperty extends Property {
value: SlotFunctionExpression
}
// applyDirectives(createVNode(...), [
// [_directive_foo, someValue],
// [_directive_bar, someValue, "arg", { mod: true }]
// ])
export interface CodegenNodeWithDirective<T extends CallExpression>
extends CallExpression {
callee: typeof APPLY_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 interface IfCodegenNode extends SequenceExpression {
expressions: [OpenBlockExpression, IfConditionalExpression]
}
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 ForRenderListExpression extends CallExpression {
callee: typeof RENDER_LIST
arguments: [ExpressionNode, ForIteratorExpression]
}
export interface ForIteratorExpression extends FunctionExpression {
returns: BlockCodegenNode
}
export interface OpenBlockExpression extends CallExpression {
callee: typeof OPEN_BLOCK
arguments: []
}
export type BlockCodegenNode =
| BlockElementCodegenNode
| BlockComponentCodegenNode
| BlockElementCodegenNodeWithDirective
| BlockComponentCodegenNodeWithDirective
export type BlockElementCodegenNode = ElementCodegenNode & {
callee: typeof CREATE_BLOCK
}
export type BlockComponentCodegenNode = ComponentCodegenNode & {
callee: typeof CREATE_BLOCK
}
export type BlockElementCodegenNodeWithDirective = CodegenNodeWithDirective<
BlockElementCodegenNode
>
export type BlockComponentCodegenNodeWithDirective = CodegenNodeWithDirective<
BlockComponentCodegenNode
>
// AST Utilities --------------------------------------------------------------- // AST Utilities ---------------------------------------------------------------
// Some expressions, e.g. sequence and conditional expressions, are never // Some expressions, e.g. sequence and conditional expressions, are never
@ -338,17 +552,25 @@ export function createCompoundExpression(
} }
} }
export function createCallExpression( type InferCodegenNodeType<T> = T extends typeof CREATE_VNODE
callee: CallExpression['callee'], ? ElementCodegenNode | ComponentCodegenNode
: T extends typeof CREATE_BLOCK
? BlockElementCodegenNode | BlockComponentCodegenNode
: T extends typeof APPLY_DIRECTIVES
? CodegenNodeWithDirective<ElementCodegenNode | ComponentCodegenNode>
: T extends typeof RENDER_SLOT ? SlotOutletCodegenNode : CallExpression
export function createCallExpression<T extends CallExpression['callee']>(
callee: T,
args: CallExpression['arguments'] = [], args: CallExpression['arguments'] = [],
loc: SourceLocation = locStub loc: SourceLocation = locStub
): CallExpression { ): InferCodegenNodeType<T> {
return { return {
type: NodeTypes.JS_CALL_EXPRESSION, type: NodeTypes.JS_CALL_EXPRESSION,
loc, loc,
callee, callee,
arguments: args arguments: args
} } as any
} }
export function createFunctionExpression( export function createFunctionExpression(

View File

@ -83,8 +83,9 @@ export function parse(content: string, options: ParserOptions = {}): RootNode {
return { return {
type: NodeTypes.ROOT, type: NodeTypes.ROOT,
children: parseChildren(context, TextModes.DATA, []), children: parseChildren(context, TextModes.DATA, []),
imports: [], helpers: [],
statements: [], components: [],
directives: [],
hoists: [], hoists: [],
codegenNode: undefined, codegenNode: undefined,
loc: getSelection(context, start) loc: getSelection(context, start)

View File

@ -2,13 +2,13 @@ import {
RootNode, RootNode,
NodeTypes, NodeTypes,
TemplateChildNode, TemplateChildNode,
CallExpression,
ElementNode, ElementNode,
ElementTypes ElementTypes,
ElementCodegenNode,
ElementCodegenNodeWithDirective
} from '../ast' } from '../ast'
import { TransformContext } from '../transform' import { TransformContext } from '../transform'
import { APPLY_DIRECTIVES } from '../runtimeHelpers' import { APPLY_DIRECTIVES } from '../runtimeHelpers'
import { PropsExpression } from './transformElement'
import { PatchFlags } from '@vue/shared' import { PatchFlags } from '@vue/shared'
export function hoistStatic(root: RootNode, context: TransformContext) { export function hoistStatic(root: RootNode, context: TransformContext) {
@ -29,7 +29,7 @@ function walk(
) { ) {
if (isStaticNode(child, resultCache)) { if (isStaticNode(child, resultCache)) {
// whole tree is static // whole tree is static
child.codegenNode = context.hoist(child.codegenNode!) ;(child as any).codegenNode = context.hoist(child.codegenNode!)
continue continue
} else { } else {
// node may contain dynamic children, but its props may be eligible for // node may contain dynamic children, but its props may be eligible for
@ -40,18 +40,13 @@ function walk(
flag === PatchFlags.NEED_PATCH || flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT flag === PatchFlags.TEXT
) { ) {
let codegenNode = child.codegenNode as CallExpression let codegenNode = child.codegenNode!
if (codegenNode.callee === APPLY_DIRECTIVES) { if (codegenNode.callee === APPLY_DIRECTIVES) {
codegenNode = codegenNode.arguments[0] as CallExpression codegenNode = codegenNode.arguments[0]
} }
const props = codegenNode.arguments[1] as const props = codegenNode.arguments[1]
| PropsExpression
| `null`
| undefined
if (props && props !== `null`) { if (props && props !== `null`) {
;(child.codegenNode as CallExpression).arguments[1] = context.hoist( codegenNode.arguments[1] = context.hoist(props)
props
)
} }
} }
} }
@ -67,9 +62,11 @@ function walk(
} }
function getPatchFlag(node: ElementNode): number | undefined { function getPatchFlag(node: ElementNode): number | undefined {
let codegenNode = node.codegenNode as CallExpression let codegenNode = node.codegenNode as
| ElementCodegenNode
| ElementCodegenNodeWithDirective
if (codegenNode.callee === APPLY_DIRECTIVES) { if (codegenNode.callee === APPLY_DIRECTIVES) {
codegenNode = codegenNode.arguments[0] as CallExpression codegenNode = codegenNode.arguments[0]
} }
const flag = codegenNode.arguments[3] const flag = codegenNode.arguments[3]
return flag ? parseInt(flag as string, 10) : undefined return flag ? parseInt(flag as string, 10) : undefined

View File

@ -14,7 +14,8 @@ import {
ElementTypes, ElementTypes,
createObjectExpression, createObjectExpression,
createObjectProperty, createObjectProperty,
CallExpression ForCodegenNode,
PlainElementNode
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { import {
@ -66,7 +67,7 @@ export const transformFor = createStructuralDirectiveTransform(
fragmentFlag + fragmentFlag +
(__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``) (__DEV__ ? ` /* ${PatchFlagNames[fragmentFlag]} */` : ``)
]) ])
]) ]) as ForCodegenNode
context.replaceNode({ context.replaceNode({
type: NodeTypes.FOR, type: NodeTypes.FOR,
@ -118,7 +119,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 CallExpression childBlock = slotOutlet.codegenNode!
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.
@ -148,7 +149,7 @@ export const transformFor = createStructuralDirectiveTransform(
// 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() // arguments, but replace createVNode() with createBlock()
childBlock = createBlockExpression( childBlock = createBlockExpression(
(node.codegenNode as CallExpression).arguments, (node as PlainElementNode).codegenNode!.arguments,
context context
) )
} }

View File

@ -17,7 +17,15 @@ import {
CallExpression, CallExpression,
createSimpleExpression, createSimpleExpression,
createObjectProperty, createObjectProperty,
createObjectExpression createObjectExpression,
IfCodegenNode,
IfConditionalExpression,
BlockCodegenNode,
SlotOutletCodegenNode,
ElementCodegenNode,
ComponentCodegenNode,
ElementCodegenNodeWithDirective,
CompoenntCodegenNodeWithDirective
} from '../ast' } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { processExpression } from './transformExpression' import { processExpression } from './transformExpression'
@ -31,7 +39,6 @@ import {
RENDER_SLOT RENDER_SLOT
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { injectProp } from '../utils' import { injectProp } from '../utils'
import { PropsExpression } from './transformElement'
export const transformIf = createStructuralDirectiveTransform( export const transformIf = createStructuralDirectiveTransform(
/^(if|else|else-if)$/, /^(if|else|else-if)$/,
@ -57,7 +64,8 @@ export const transformIf = createStructuralDirectiveTransform(
const branch = createIfBranch(node, dir) const branch = createIfBranch(node, dir)
const codegenNode = createSequenceExpression([ const codegenNode = createSequenceExpression([
createCallExpression(context.helper(OPEN_BLOCK)) createCallExpression(context.helper(OPEN_BLOCK))
]) ]) as IfCodegenNode
context.replaceNode({ context.replaceNode({
type: NodeTypes.IF, type: NodeTypes.IF,
loc: node.loc, loc: node.loc,
@ -68,9 +76,11 @@ export const transformIf = createStructuralDirectiveTransform(
// Exit callback. Complete the codegenNode when all children have been // Exit callback. Complete the codegenNode when all children have been
// transformed. // transformed.
return () => { return () => {
codegenNode.expressions.push( codegenNode.expressions.push(createCodegenNodeForBranch(
createCodegenNodeForBranch(branch, 0, context) branch,
) 0,
context
) as IfConditionalExpression)
} }
} else { } else {
// locate the adjacent v-if // locate the adjacent v-if
@ -137,7 +147,7 @@ function createCodegenNodeForBranch(
branch: IfBranchNode, branch: IfBranchNode,
index: number, index: number,
context: TransformContext context: TransformContext
): ConditionalExpression | CallExpression { ): IfConditionalExpression | BlockCodegenNode {
if (branch.condition) { if (branch.condition) {
return createConditionalExpression( return createConditionalExpression(
branch.condition, branch.condition,
@ -145,9 +155,9 @@ function createCodegenNodeForBranch(
createCallExpression(context.helper(CREATE_BLOCK), [ createCallExpression(context.helper(CREATE_BLOCK), [
context.helper(EMPTY) context.helper(EMPTY)
]) ])
) ) as IfConditionalExpression
} else { } else {
return createChildrenCodegenNode(branch, index, context) return createChildrenCodegenNode(branch, index, context) as BlockCodegenNode
} }
} }
@ -173,23 +183,27 @@ function createChildrenCodegenNode(
] ]
if (children.length === 1 && child.type === NodeTypes.FOR) { if (children.length === 1 && child.type === NodeTypes.FOR) {
// optimize away nested fragments when child is a ForNode // optimize away nested fragments when child is a ForNode
const forBlockArgs = (child.codegenNode.expressions[1] as CallExpression) const forBlockArgs = child.codegenNode.expressions[1].arguments
.arguments
// directly use the for block's children and patchFlag // directly use the for block's children and patchFlag
blockArgs[2] = forBlockArgs[2] blockArgs[2] = forBlockArgs[2]
blockArgs[3] = forBlockArgs[3] blockArgs[3] = forBlockArgs[3]
} }
return createCallExpression(helper(CREATE_BLOCK), blockArgs) return createCallExpression(helper(CREATE_BLOCK), blockArgs)
} else { } else {
const childCodegen = (child as ElementNode).codegenNode as CallExpression const childCodegen = (child as ElementNode).codegenNode as
| ElementCodegenNode
| ComponentCodegenNode
| ElementCodegenNodeWithDirective
| CompoenntCodegenNodeWithDirective
| SlotOutletCodegenNode
let vnodeCall = childCodegen let vnodeCall = childCodegen
// Element with custom directives. Locate the actual createVNode() call. // Element with custom directives. Locate the actual createVNode() call.
if (vnodeCall.callee === APPLY_DIRECTIVES) { if (vnodeCall.callee === APPLY_DIRECTIVES) {
vnodeCall = vnodeCall.arguments[0] as CallExpression vnodeCall = vnodeCall.arguments[0]
} }
// Change createVNode to createBlock. // Change createVNode to createBlock.
if (vnodeCall.callee === CREATE_VNODE) { if (vnodeCall.callee === CREATE_VNODE) {
vnodeCall.callee = helper(CREATE_BLOCK) ;(vnodeCall as any).callee = helper(CREATE_BLOCK)
} }
// It's possible to have renderSlot() here as well - which already produces // It's possible to have renderSlot() here as well - which already produces
// a block, so no need to change the callee. However it accepts props at // a block, so no need to change the callee. However it accepts props at
@ -197,10 +211,9 @@ function createChildrenCodegenNode(
// logic below works for it too. // logic below works for it too.
const propsIndex = vnodeCall.callee === RENDER_SLOT ? 2 : 1 const propsIndex = vnodeCall.callee === RENDER_SLOT ? 2 : 1
// inject branch key // inject branch key
const existingProps = vnodeCall.arguments[propsIndex] as const existingProps = vnodeCall.arguments[
| PropsExpression propsIndex
| undefined ] as ElementCodegenNode['arguments'][1]
| 'null'
vnodeCall.arguments[propsIndex] = injectProp( vnodeCall.arguments[propsIndex] = injectProp(
existingProps, existingProps,
keyProperty, keyProperty,

View File

@ -14,7 +14,9 @@ import {
ObjectExpression, ObjectExpression,
Property, Property,
JSChildNode, JSChildNode,
createObjectExpression createObjectExpression,
SlotOutletNode,
TemplateNode
} from './ast' } from './ast'
import { parse } from 'acorn' import { parse } from 'acorn'
import { walk } from 'estree-walker' import { walk } from 'estree-walker'
@ -180,12 +182,12 @@ export const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode =>
export const isTemplateNode = ( export const isTemplateNode = (
node: RootNode | TemplateChildNode node: RootNode | TemplateChildNode
): node is ElementNode & { tagType: ElementTypes.TEMPLATE } => ): node is TemplateNode =>
node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.TEMPLATE node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.TEMPLATE
export const isSlotOutlet = ( export const isSlotOutlet = (
node: RootNode | TemplateChildNode node: RootNode | TemplateChildNode
): node is ElementNode & { tagType: ElementTypes.ELEMENT } => ): node is SlotOutletNode =>
node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.SLOT
export function injectProp( export function injectProp(