refactor(compiler): refine codegen node types
This commit is contained in:
parent
bfecf2cdce
commit
82bd9eb1db
@ -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\\")
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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(
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user