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

350 lines
7.9 KiB
TypeScript
Raw Normal View History

import { isString } from '@vue/shared'
// 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,
// codegen
2019-09-23 04:50:57 +08:00
JS_CALL_EXPRESSION,
JS_OBJECT_EXPRESSION,
JS_PROPERTY,
JS_ARRAY_EXPRESSION,
JS_FUNCTION_EXPRESSION,
JS_SEQUENCE_EXPRESSION,
JS_CONDITIONAL_EXPRESSION
}
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
export interface RootNode extends Node {
type: NodeTypes.ROOT
children: TemplateChildNode[]
imports: string[]
statements: string[]
hoists: JSChildNode[]
codegenNode: TemplateChildNode | JSChildNode | undefined
}
export interface ElementNode 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[]
2019-09-22 03:47:26 +08:00
codegenNode: CallExpression | undefined
}
export interface TextNode extends Node {
type: NodeTypes.TEXT
content: string
isEmpty: boolean
}
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[]
}
export interface SimpleExpressionNode extends Node {
type: NodeTypes.SIMPLE_EXPRESSION
content: string
isStatic: 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
}
// always dynamic
export interface CompoundExpressionNode extends Node {
type: NodeTypes.COMPOUND_EXPRESSION
children: (SimpleExpressionNode | InterpolationNode | TextNode | string)[]
// 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: SequenceExpression
}
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
children: TemplateChildNode[]
codegenNode: SequenceExpression
}
2019-09-23 04:50:57 +08:00
// We also include a number of JavaScript AST nodes for code generation.
// The AST is an intentioanlly 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 =
2019-09-22 03:47:26 +08:00
| CallExpression
| ObjectExpression
| ArrayExpression
| ExpressionNode
| FunctionExpression
| ConditionalExpression
| SequenceExpression
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
callee: string
arguments: (string | JSChildNode | 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
params: ExpressionNode | ExpressionNode[] | undefined
returns: TemplateChildNode | TemplateChildNode[] | JSChildNode
newline: boolean
}
export interface SequenceExpression extends Node {
type: NodeTypes.JS_SEQUENCE_EXPRESSION
expressions: JSChildNode[]
}
export interface ConditionalExpression extends Node {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION
test: ExpressionNode
consequent: JSChildNode
alternate: JSChildNode
}
// 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 }
}
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'],
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,
2019-09-22 05:42:12 +08:00
key,
value
}
}
export function createSimpleExpression(
content: SimpleExpressionNode['content'],
isStatic: SimpleExpressionNode['isStatic'],
loc: SourceLocation = locStub
): SimpleExpressionNode {
2019-09-22 05:42:12 +08:00
return {
type: NodeTypes.SIMPLE_EXPRESSION,
2019-09-22 05:42:12 +08:00
loc,
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']
): CompoundExpressionNode {
return {
type: NodeTypes.COMPOUND_EXPRESSION,
loc: locStub,
children
2019-09-22 05:42:12 +08:00
}
}
export function createCallExpression(
callee: CallExpression['callee'],
args: CallExpression['arguments'] = [],
loc: SourceLocation = locStub
2019-09-22 05:42:12 +08:00
): CallExpression {
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
}
}
export function createFunctionExpression(
params: FunctionExpression['params'],
returns: FunctionExpression['returns'],
newline: boolean = false,
loc: SourceLocation = locStub
): FunctionExpression {
return {
type: NodeTypes.JS_FUNCTION_EXPRESSION,
params,
returns,
newline,
loc
}
}
export function createSequenceExpression(
expressions: SequenceExpression['expressions']
): SequenceExpression {
return {
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
expressions,
loc: locStub
}
}
export function createConditionalExpression(
test: ConditionalExpression['test'],
consequent: ConditionalExpression['consequent'],
alternate: ConditionalExpression['alternate']
): ConditionalExpression {
return {
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
test,
consequent,
alternate,
loc: locStub
}
}