vue3-yuanma/packages/compiler-core/src/ast.ts

738 lines
17 KiB
TypeScript
Raw Normal View History

import { isString } from '@vue/shared'
import { ForParseResult } from './transforms/vFor'
import {
RENDER_SLOT,
CREATE_SLOTS,
RENDER_LIST,
OPEN_BLOCK,
CREATE_BLOCK,
FRAGMENT,
CREATE_VNODE,
WITH_DIRECTIVES
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
import { ImportItem, TransformContext } from './transform'
// Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific
// compilers.
export type Namespace = number
export const enum Namespaces {
HTML
}
export const enum NodeTypes {
ROOT,
ELEMENT,
TEXT,
COMMENT,
SIMPLE_EXPRESSION,
INTERPOLATION,
ATTRIBUTE,
DIRECTIVE,
2019-09-22 03:47:26 +08:00
// containers
2019-09-23 14:52:54 +08:00
COMPOUND_EXPRESSION,
IF,
IF_BRANCH,
2019-09-22 03:47:26 +08:00
FOR,
TEXT_CALL,
2019-09-22 03:47:26 +08:00
// codegen
VNODE_CALL,
2019-09-23 04:50:57 +08:00
JS_CALL_EXPRESSION,
JS_OBJECT_EXPRESSION,
JS_PROPERTY,
JS_ARRAY_EXPRESSION,
JS_FUNCTION_EXPRESSION,
2019-10-19 09:51:34 +08:00
JS_CONDITIONAL_EXPRESSION,
JS_CACHE_EXPRESSION,
// ssr codegen
JS_BLOCK_STATEMENT,
JS_TEMPLATE_LITERAL,
JS_IF_STATEMENT,
2020-02-07 14:06:51 +08:00
JS_ASSIGNMENT_EXPRESSION,
JS_RETURN_STATEMENT
}
export const enum ElementTypes {
ELEMENT,
COMPONENT,
SLOT,
TEMPLATE
}
export interface Node {
type: NodeTypes
loc: SourceLocation
}
2019-09-22 03:47:26 +08:00
// The node's range. The `start` is inclusive and `end` is exclusive.
// [start, end)
export interface SourceLocation {
start: Position
end: Position
source: string
}
export interface Position {
offset: number // from start of file
line: number
column: number
}
export type ParentNode = RootNode | ElementNode | IfBranchNode | ForNode
2019-09-22 03:47:26 +08:00
export type ExpressionNode = SimpleExpressionNode | CompoundExpressionNode
export type TemplateChildNode =
| ElementNode
| InterpolationNode
| CompoundExpressionNode
| TextNode
| CommentNode
| IfNode
| ForNode
| TextCallNode
export interface RootNode extends Node {
type: NodeTypes.ROOT
children: TemplateChildNode[]
2019-10-11 06:02:51 +08:00
helpers: symbol[]
components: string[]
directives: string[]
hoists: JSChildNode[]
imports: ImportItem[]
2019-10-19 09:51:34 +08:00
cached: number
temps: number
2020-02-04 06:47:06 +08:00
ssrHelpers?: symbol[]
codegenNode?: TemplateChildNode | JSChildNode | BlockStatement | undefined
}
export type ElementNode =
| PlainElementNode
| ComponentNode
| SlotOutletNode
| TemplateNode
export interface BaseElementNode extends Node {
type: NodeTypes.ELEMENT
ns: Namespace
tag: string
tagType: ElementTypes
isSelfClosing: boolean
2019-09-22 05:42:12 +08:00
props: Array<AttributeNode | DirectiveNode>
children: TemplateChildNode[]
}
export interface PlainElementNode extends BaseElementNode {
tagType: ElementTypes.ELEMENT
codegenNode:
| VNodeCall
| SimpleExpressionNode // when hoisted
| CacheExpression // when cached by v-once
| undefined
ssrCodegenNode?: TemplateLiteral
}
export interface ComponentNode extends BaseElementNode {
tagType: ElementTypes.COMPONENT
codegenNode:
| VNodeCall
| CacheExpression // when cached by v-once
| undefined
2020-02-06 10:04:40 +08:00
ssrCodegenNode?: CallExpression
}
export interface SlotOutletNode extends BaseElementNode {
tagType: ElementTypes.SLOT
codegenNode:
| RenderSlotCall
| CacheExpression // when cached by v-once
| undefined
2020-02-06 10:04:40 +08:00
ssrCodegenNode?: CallExpression
}
export interface TemplateNode extends BaseElementNode {
tagType: ElementTypes.TEMPLATE
// TemplateNode is a container type that always gets compiled away
codegenNode: undefined
}
export interface TextNode extends Node {
type: NodeTypes.TEXT
content: string
}
export interface CommentNode extends Node {
type: NodeTypes.COMMENT
content: string
}
export interface AttributeNode extends Node {
type: NodeTypes.ATTRIBUTE
name: string
value: TextNode | undefined
}
export interface DirectiveNode extends Node {
type: NodeTypes.DIRECTIVE
name: string
exp: ExpressionNode | undefined
arg: ExpressionNode | undefined
modifiers: string[]
// optional property to cache the expression parse result for v-for
parseResult?: ForParseResult
}
export interface SimpleExpressionNode extends Node {
type: NodeTypes.SIMPLE_EXPRESSION
content: string
isStatic: boolean
isConstant: boolean
// an expression parsed as the params of a function will track
// the identifiers declared inside the function body.
identifiers?: string[]
}
export interface InterpolationNode extends Node {
type: NodeTypes.INTERPOLATION
content: ExpressionNode
}
export interface CompoundExpressionNode extends Node {
type: NodeTypes.COMPOUND_EXPRESSION
children: (
| SimpleExpressionNode
| CompoundExpressionNode
| InterpolationNode
| TextNode
| string
2019-10-11 06:02:51 +08:00
| symbol)[]
// an expression parsed as the params of a function will track
// the identifiers declared inside the function body.
identifiers?: string[]
}
export interface IfNode extends Node {
type: NodeTypes.IF
branches: IfBranchNode[]
codegenNode?: IfConditionalExpression
}
export interface IfBranchNode extends Node {
type: NodeTypes.IF_BRANCH
condition: ExpressionNode | undefined // else
children: TemplateChildNode[]
}
export interface ForNode extends Node {
type: NodeTypes.FOR
source: ExpressionNode
valueAlias: ExpressionNode | undefined
keyAlias: ExpressionNode | undefined
objectIndexAlias: ExpressionNode | undefined
2020-02-04 07:30:56 +08:00
parseResult: ForParseResult
children: TemplateChildNode[]
2020-02-04 04:51:41 +08:00
codegenNode?: ForCodegenNode
}
export interface TextCallNode extends Node {
type: NodeTypes.TEXT_CALL
content: TextNode | InterpolationNode | CompoundExpressionNode
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 ---------------------------------------------------------------
2019-09-23 04:50:57 +08:00
// 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
2019-09-22 03:47:26 +08:00
// Vue render function generation.
2019-09-23 04:50:57 +08:00
export type JSChildNode =
| VNodeCall
2019-09-22 03:47:26 +08:00
| CallExpression
| ObjectExpression
| ArrayExpression
| ExpressionNode
| FunctionExpression
| ConditionalExpression
2019-10-19 09:51:34 +08:00
| CacheExpression
| AssignmentExpression
2019-09-22 03:47:26 +08:00
export interface CallExpression extends Node {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_CALL_EXPRESSION
2019-10-11 06:02:51 +08:00
callee: string | symbol
arguments: (
| string
2019-10-11 06:02:51 +08:00
| symbol
| JSChildNode
| SSRCodegenNode
| TemplateChildNode
| TemplateChildNode[])[]
}
2019-09-22 03:47:26 +08:00
export interface ObjectExpression extends Node {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_OBJECT_EXPRESSION
2019-09-22 03:47:26 +08:00
properties: Array<Property>
}
export interface Property extends Node {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_PROPERTY
2019-09-22 03:47:26 +08:00
key: ExpressionNode
2019-09-26 00:39:46 +08:00
value: JSChildNode
2019-09-22 03:47:26 +08:00
}
export interface ArrayExpression extends Node {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_ARRAY_EXPRESSION
elements: Array<string | JSChildNode>
}
2019-09-22 05:42:12 +08:00
export interface FunctionExpression extends Node {
type: NodeTypes.JS_FUNCTION_EXPRESSION
2020-02-07 01:07:25 +08:00
params: ExpressionNode | string | (ExpressionNode | string)[] | undefined
returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode
2020-02-07 14:06:51 +08:00
body?: BlockStatement | IfStatement
newline: boolean
// so that codegen knows it needs to generate ScopeId wrapper
isSlot: boolean
}
export interface ConditionalExpression extends Node {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION
test: JSChildNode
consequent: JSChildNode
alternate: JSChildNode
newline: boolean
}
2019-10-19 09:51:34 +08:00
export interface CacheExpression extends Node {
type: NodeTypes.JS_CACHE_EXPRESSION
index: number
value: JSChildNode
isVNode: boolean
2019-10-19 09:51:34 +08:00
}
// SSR-specific Node Types -----------------------------------------------------
2020-02-07 14:06:51 +08:00
export type SSRCodegenNode =
| BlockStatement
| TemplateLiteral
| IfStatement
| AssignmentExpression
| ReturnStatement
export interface BlockStatement extends Node {
type: NodeTypes.JS_BLOCK_STATEMENT
body: (JSChildNode | IfStatement)[]
}
export interface TemplateLiteral extends Node {
type: NodeTypes.JS_TEMPLATE_LITERAL
elements: (string | JSChildNode)[]
}
export interface IfStatement extends Node {
type: NodeTypes.JS_IF_STATEMENT
test: ExpressionNode
consequent: BlockStatement
2020-02-07 14:06:51 +08:00
alternate: IfStatement | BlockStatement | ReturnStatement | undefined
}
export interface AssignmentExpression extends Node {
type: NodeTypes.JS_ASSIGNMENT_EXPRESSION
left: SimpleExpressionNode
right: JSChildNode
}
2020-02-07 14:06:51 +08:00
export interface ReturnStatement extends Node {
type: NodeTypes.JS_RETURN_STATEMENT
returns: TemplateChildNode | TemplateChildNode[] | JSChildNode
}
// Codegen Node Types ----------------------------------------------------------
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 RenderSlotCall extends CallExpression {
callee: typeof RENDER_SLOT
arguments: // $slots, name, props, fallback
| [string, string | ExpressionNode]
| [string, string | ExpressionNode, PropsExpression]
| [
string,
string | ExpressionNode,
PropsExpression | '{}',
TemplateChildNode[]
]
}
export type SlotsExpression = SlotsObjectExpression | DynamicSlotsExpression
// { 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 {
2019-10-10 22:26:03 +08:00
elements: (ConditionalDynamicSlotNode | ListDynamicSlotNode)[]
}
export interface ConditionalDynamicSlotNode extends ConditionalExpression {
consequent: DynamicSlotNode
alternate: DynamicSlotNode | SimpleExpressionNode
}
2019-10-10 22:26:03 +08:00
export interface ListDynamicSlotNode extends CallExpression {
callee: typeof RENDER_LIST
2019-10-11 21:59:52 +08:00
arguments: [ExpressionNode, ListDynamicSlotIterator]
}
2019-10-11 21:59:52 +08:00
export interface ListDynamicSlotIterator extends FunctionExpression {
returns: DynamicSlotNode
}
export interface DynamicSlotNode extends ObjectExpression {
properties: [Property, DynamicSlotFnProperty]
}
export interface DynamicSlotFnProperty extends Property {
value: SlotFunctionExpression
}
export type BlockCodegenNode = VNodeCall | RenderSlotCall
export interface IfConditionalExpression extends ConditionalExpression {
consequent: BlockCodegenNode
alternate: BlockCodegenNode | IfConditionalExpression
}
export interface ForCodegenNode extends VNodeCall {
isBlock: true
tag: typeof FRAGMENT
props: undefined
children: ForRenderListExpression
patchFlag: string
isForBlock: true
}
export interface ForRenderListExpression extends CallExpression {
callee: typeof RENDER_LIST
arguments: [ExpressionNode, ForIteratorExpression]
}
export interface ForIteratorExpression extends FunctionExpression {
returns: BlockCodegenNode
}
// AST Utilities ---------------------------------------------------------------
// Some expressions, e.g. sequence and conditional expressions, are never
// associated with template nodes, so their source locations are just a stub.
// Container types like CompoundExpression also don't need a real location.
export const locStub: SourceLocation = {
source: '',
start: { line: 1, column: 1, offset: 0 },
end: { line: 1, column: 1, offset: 0 }
}
export function createRoot(
children: TemplateChildNode[],
loc = locStub
): RootNode {
return {
type: NodeTypes.ROOT,
children,
helpers: [],
components: [],
directives: [],
hoists: [],
imports: [],
cached: 0,
temps: 0,
codegenNode: undefined,
loc
}
}
export function createVNodeCall(
2020-02-12 07:40:21 +08:00
context: TransformContext | null,
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 {
2020-02-12 07:40:21 +08:00
if (context) {
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
}
}
2019-09-22 05:42:12 +08:00
export function createArrayExpression(
elements: ArrayExpression['elements'],
loc: SourceLocation = locStub
2019-09-22 05:42:12 +08:00
): ArrayExpression {
return {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_ARRAY_EXPRESSION,
2019-09-22 05:42:12 +08:00
loc,
elements
}
}
export function createObjectExpression(
properties: ObjectExpression['properties'],
loc: SourceLocation = locStub
2019-09-22 05:42:12 +08:00
): ObjectExpression {
return {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_OBJECT_EXPRESSION,
2019-09-22 05:42:12 +08:00
loc,
properties
}
}
export function createObjectProperty(
key: Property['key'] | string,
value: Property['value']
2019-09-22 05:42:12 +08:00
): Property {
return {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_PROPERTY,
loc: locStub,
key: isString(key) ? createSimpleExpression(key, true) : key,
2019-09-22 05:42:12 +08:00
value
}
}
export function createSimpleExpression(
content: SimpleExpressionNode['content'],
isStatic: SimpleExpressionNode['isStatic'],
loc: SourceLocation = locStub,
isConstant: boolean = false
): SimpleExpressionNode {
2019-09-22 05:42:12 +08:00
return {
type: NodeTypes.SIMPLE_EXPRESSION,
2019-09-22 05:42:12 +08:00
loc,
isConstant,
2019-09-22 05:42:12 +08:00
content,
isStatic
}
}
export function createInterpolation(
content: InterpolationNode['content'] | string,
loc: SourceLocation
): InterpolationNode {
return {
type: NodeTypes.INTERPOLATION,
loc,
content: isString(content)
? createSimpleExpression(content, false, loc)
: content
}
}
export function createCompoundExpression(
children: CompoundExpressionNode['children'],
loc: SourceLocation = locStub
): CompoundExpressionNode {
return {
type: NodeTypes.COMPOUND_EXPRESSION,
loc,
children
2019-09-22 05:42:12 +08:00
}
}
type InferCodegenNodeType<T> = T extends typeof RENDER_SLOT
? RenderSlotCall
: CallExpression
export function createCallExpression<T extends CallExpression['callee']>(
callee: T,
args: CallExpression['arguments'] = [],
loc: SourceLocation = locStub
): InferCodegenNodeType<T> {
2019-09-22 05:42:12 +08:00
return {
2019-09-23 04:50:57 +08:00
type: NodeTypes.JS_CALL_EXPRESSION,
2019-09-22 05:42:12 +08:00
loc,
callee,
arguments: args
} as any
2019-09-22 05:42:12 +08:00
}
export function createFunctionExpression(
params: FunctionExpression['params'],
2020-02-04 07:30:56 +08:00
returns: FunctionExpression['returns'] = undefined,
newline: boolean = false,
isSlot: boolean = false,
loc: SourceLocation = locStub
): FunctionExpression {
return {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params,
returns,
newline,
isSlot,
loc
}
}
export function createConditionalExpression(
test: ConditionalExpression['test'],
consequent: ConditionalExpression['consequent'],
alternate: ConditionalExpression['alternate'],
newline = true
): ConditionalExpression {
return {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test,
consequent,
alternate,
newline,
loc: locStub
}
}
2019-10-19 09:51:34 +08:00
export function createCacheExpression(
index: number,
value: JSChildNode,
isVNode: boolean = false
2019-10-19 09:51:34 +08:00
): CacheExpression {
return {
type: NodeTypes.JS_CACHE_EXPRESSION,
index,
value,
isVNode,
2019-10-19 09:51:34 +08:00
loc: locStub
}
}
export function createBlockStatement(
body: BlockStatement['body']
): BlockStatement {
return {
type: NodeTypes.JS_BLOCK_STATEMENT,
body,
loc: locStub
}
}
export function createTemplateLiteral(
elements: TemplateLiteral['elements']
): TemplateLiteral {
return {
type: NodeTypes.JS_TEMPLATE_LITERAL,
elements,
loc: locStub
}
}
export function createIfStatement(
test: IfStatement['test'],
consequent: IfStatement['consequent'],
alternate?: IfStatement['alternate']
): IfStatement {
return {
type: NodeTypes.JS_IF_STATEMENT,
test,
consequent,
alternate,
loc: locStub
}
}
export function createAssignmentExpression(
left: AssignmentExpression['left'],
right: AssignmentExpression['right']
): AssignmentExpression {
return {
type: NodeTypes.JS_ASSIGNMENT_EXPRESSION,
left,
right,
loc: locStub
}
}
2020-02-07 14:06:51 +08:00
export function createReturnStatement(
returns: ReturnStatement['returns']
): ReturnStatement {
return {
type: NodeTypes.JS_RETURN_STATEMENT,
returns,
loc: locStub
}
}