wip: Sequence & Conditional expressions for AST
This commit is contained in:
parent
2f155dbcb8
commit
e31fb3c172
@ -28,7 +28,9 @@ export const enum NodeTypes {
|
||||
JS_OBJECT_EXPRESSION,
|
||||
JS_PROPERTY,
|
||||
JS_ARRAY_EXPRESSION,
|
||||
JS_SLOT_FUNCTION
|
||||
JS_SLOT_FUNCTION,
|
||||
JS_SEQUENCE_EXPRESSION,
|
||||
JS_CONDITIONAL_EXPRESSION
|
||||
}
|
||||
|
||||
export const enum ElementTypes {
|
||||
@ -61,7 +63,7 @@ export type ParentNode = RootNode | ElementNode | IfBranchNode | ForNode
|
||||
|
||||
export type ExpressionNode = SimpleExpressionNode | CompoundExpressionNode
|
||||
|
||||
export type ChildNode =
|
||||
export type TemplateChildNode =
|
||||
| ElementNode
|
||||
| InterpolationNode
|
||||
| CompoundExpressionNode
|
||||
@ -72,7 +74,7 @@ export type ChildNode =
|
||||
|
||||
export interface RootNode extends Node {
|
||||
type: NodeTypes.ROOT
|
||||
children: ChildNode[]
|
||||
children: TemplateChildNode[]
|
||||
imports: string[]
|
||||
statements: string[]
|
||||
hoists: JSChildNode[]
|
||||
@ -85,7 +87,7 @@ export interface ElementNode extends Node {
|
||||
tagType: ElementTypes
|
||||
isSelfClosing: boolean
|
||||
props: Array<AttributeNode | DirectiveNode>
|
||||
children: ChildNode[]
|
||||
children: TemplateChildNode[]
|
||||
codegenNode: CallExpression | undefined
|
||||
}
|
||||
|
||||
@ -140,12 +142,13 @@ export interface CompoundExpressionNode extends Node {
|
||||
export interface IfNode extends Node {
|
||||
type: NodeTypes.IF
|
||||
branches: IfBranchNode[]
|
||||
codegenNode: JSChildNode | undefined
|
||||
}
|
||||
|
||||
export interface IfBranchNode extends Node {
|
||||
type: NodeTypes.IF_BRANCH
|
||||
condition: ExpressionNode | undefined // else
|
||||
children: ChildNode[]
|
||||
children: TemplateChildNode[]
|
||||
}
|
||||
|
||||
export interface ForNode extends Node {
|
||||
@ -154,7 +157,7 @@ export interface ForNode extends Node {
|
||||
valueAlias: ExpressionNode | undefined
|
||||
keyAlias: ExpressionNode | undefined
|
||||
objectIndexAlias: ExpressionNode | undefined
|
||||
children: ChildNode[]
|
||||
children: TemplateChildNode[]
|
||||
}
|
||||
|
||||
// We also include a number of JavaScript AST nodes for code generation.
|
||||
@ -166,11 +169,13 @@ export type JSChildNode =
|
||||
| ArrayExpression
|
||||
| ExpressionNode
|
||||
| SlotFunctionExpression
|
||||
| ConditionalExpression
|
||||
| SequenceExpression
|
||||
|
||||
export interface CallExpression extends Node {
|
||||
type: NodeTypes.JS_CALL_EXPRESSION
|
||||
callee: string
|
||||
arguments: (string | JSChildNode | ChildNode[])[]
|
||||
arguments: (string | JSChildNode | TemplateChildNode[])[]
|
||||
}
|
||||
|
||||
export interface ObjectExpression extends Node {
|
||||
@ -192,7 +197,19 @@ export interface ArrayExpression extends Node {
|
||||
export interface SlotFunctionExpression extends Node {
|
||||
type: NodeTypes.JS_SLOT_FUNCTION
|
||||
params: ExpressionNode | undefined
|
||||
returns: ChildNode[]
|
||||
returns: TemplateChildNode[]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export function createArrayExpression(
|
||||
@ -282,7 +299,7 @@ export function createCallExpression(
|
||||
|
||||
export function createFunctionExpression(
|
||||
params: ExpressionNode | undefined,
|
||||
returns: ChildNode[],
|
||||
returns: TemplateChildNode[],
|
||||
loc: SourceLocation
|
||||
): SlotFunctionExpression {
|
||||
return {
|
||||
@ -292,3 +309,33 @@ export function createFunctionExpression(
|
||||
loc
|
||||
}
|
||||
}
|
||||
|
||||
// sequence and conditional expressions are never associated with template nodes,
|
||||
// so their source locations are just a stub.
|
||||
const locStub: SourceLocation = {
|
||||
source: '',
|
||||
start: { line: 1, column: 1, offset: 0 },
|
||||
end: { line: 1, column: 1, offset: 0 }
|
||||
}
|
||||
|
||||
export function createSequenceExpression(expressions: JSChildNode[]) {
|
||||
return {
|
||||
type: NodeTypes.JS_SEQUENCE_EXPRESSION,
|
||||
expressions,
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
||||
export function createConditionalExpression(
|
||||
test: ExpressionNode,
|
||||
consequent: JSChildNode,
|
||||
alternate: JSChildNode
|
||||
) {
|
||||
return {
|
||||
type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
|
||||
test,
|
||||
consequent,
|
||||
alternate,
|
||||
loc: locStub
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
RootNode,
|
||||
ChildNode,
|
||||
TemplateChildNode,
|
||||
ElementNode,
|
||||
IfNode,
|
||||
ForNode,
|
||||
@ -19,7 +19,9 @@ import {
|
||||
CompoundExpressionNode,
|
||||
SimpleExpressionNode,
|
||||
ElementTypes,
|
||||
SlotFunctionExpression
|
||||
SlotFunctionExpression,
|
||||
SequenceExpression,
|
||||
ConditionalExpression
|
||||
} from './ast'
|
||||
import { SourceMapGenerator, RawSourceMap } from 'source-map'
|
||||
import {
|
||||
@ -35,7 +37,7 @@ import {
|
||||
COMMENT
|
||||
} from './runtimeConstants'
|
||||
|
||||
type CodegenNode = ChildNode | JSChildNode
|
||||
type CodegenNode = TemplateChildNode | JSChildNode
|
||||
|
||||
export interface CodegenOptions {
|
||||
// - Module mode will generate ES module import statements for helpers
|
||||
@ -269,7 +271,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
// - expression
|
||||
// - <slot> outlet, which always produces an array
|
||||
function genChildren(
|
||||
children: ChildNode[],
|
||||
children: TemplateChildNode[],
|
||||
context: CodegenContext,
|
||||
allowSingle: boolean = false
|
||||
) {
|
||||
@ -294,7 +296,7 @@ function genChildren(
|
||||
}
|
||||
|
||||
function genNodeListAsArray(
|
||||
nodes: (string | CodegenNode | ChildNode[])[],
|
||||
nodes: (string | CodegenNode | TemplateChildNode[])[],
|
||||
context: CodegenContext
|
||||
) {
|
||||
const multilines =
|
||||
@ -312,7 +314,7 @@ function genNodeListAsArray(
|
||||
}
|
||||
|
||||
function genNodeList(
|
||||
nodes: (string | CodegenNode | ChildNode[])[],
|
||||
nodes: (string | CodegenNode | TemplateChildNode[])[],
|
||||
context: CodegenContext,
|
||||
multilines: boolean = false
|
||||
) {
|
||||
@ -375,6 +377,12 @@ function genNode(node: CodegenNode, context: CodegenContext) {
|
||||
case NodeTypes.JS_SLOT_FUNCTION:
|
||||
genSlotFunction(node, context)
|
||||
break
|
||||
case NodeTypes.JS_SEQUENCE_EXPRESSION:
|
||||
genSequenceExpression(node, context)
|
||||
break
|
||||
case NodeTypes.JS_CONDITIONAL_EXPRESSION:
|
||||
genConditionalExpression(node, context)
|
||||
break
|
||||
/* istanbul ignore next */
|
||||
default:
|
||||
if (__DEV__) {
|
||||
@ -593,3 +601,37 @@ function genSlotFunction(
|
||||
// pre-normalized slots should always return arrays
|
||||
genNodeListAsArray(node.returns, context)
|
||||
}
|
||||
|
||||
function genConditionalExpression(
|
||||
node: ConditionalExpression,
|
||||
context: CodegenContext
|
||||
) {
|
||||
const { test, consequent, alternate } = node
|
||||
const { push, indent, deindent, newline } = context
|
||||
if (test.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||
const needsQuote = !isSimpleIdentifier(test.content)
|
||||
needsQuote && push(`(`)
|
||||
genExpression(test, context)
|
||||
needsQuote && push(`)`)
|
||||
} else {
|
||||
genCompoundExpression(test, context)
|
||||
}
|
||||
indent()
|
||||
context.indentLevel++
|
||||
push(`? `)
|
||||
genNode(consequent, context)
|
||||
context.indentLevel--
|
||||
newline()
|
||||
push(`: `)
|
||||
genNode(alternate, context)
|
||||
deindent(true /* without newline */)
|
||||
}
|
||||
|
||||
function genSequenceExpression(
|
||||
node: SequenceExpression,
|
||||
context: CodegenContext
|
||||
) {
|
||||
context.push(`(`)
|
||||
genNodeList(node.expressions, context)
|
||||
context.push(`)`)
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import {
|
||||
RootNode,
|
||||
SourceLocation,
|
||||
TextNode,
|
||||
ChildNode,
|
||||
TemplateChildNode,
|
||||
InterpolationNode
|
||||
} from './ast'
|
||||
|
||||
@ -115,15 +115,15 @@ function parseChildren(
|
||||
context: ParserContext,
|
||||
mode: TextModes,
|
||||
ancestors: ElementNode[]
|
||||
): ChildNode[] {
|
||||
): TemplateChildNode[] {
|
||||
const parent = last(ancestors)
|
||||
const ns = parent ? parent.ns : Namespaces.HTML
|
||||
const nodes: ChildNode[] = []
|
||||
const nodes: TemplateChildNode[] = []
|
||||
|
||||
while (!isEnd(context, mode, ancestors)) {
|
||||
__DEV__ && assert(context.source.length > 0)
|
||||
const s = context.source
|
||||
let node: ChildNode | ChildNode[] | undefined = undefined
|
||||
let node: TemplateChildNode | TemplateChildNode[] | undefined = undefined
|
||||
|
||||
if (startsWith(s, context.options.delimiters[0])) {
|
||||
// '{{'
|
||||
@ -197,8 +197,8 @@ function parseChildren(
|
||||
|
||||
function pushNode(
|
||||
context: ParserContext,
|
||||
nodes: ChildNode[],
|
||||
node: ChildNode
|
||||
nodes: TemplateChildNode[],
|
||||
node: TemplateChildNode
|
||||
): void {
|
||||
// ignore comments in production
|
||||
/* istanbul ignore next */
|
||||
@ -234,7 +234,7 @@ function pushNode(
|
||||
function parseCDATA(
|
||||
context: ParserContext,
|
||||
ancestors: ElementNode[]
|
||||
): ChildNode[] {
|
||||
): TemplateChildNode[] {
|
||||
__DEV__ &&
|
||||
assert(last(ancestors) == null || last(ancestors)!.ns !== Namespaces.HTML)
|
||||
__DEV__ && assert(startsWith(context.source, '<![CDATA['))
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
RootNode,
|
||||
NodeTypes,
|
||||
ParentNode,
|
||||
ChildNode,
|
||||
TemplateChildNode,
|
||||
ElementNode,
|
||||
DirectiveNode,
|
||||
Property,
|
||||
@ -21,7 +21,7 @@ import { TO_STRING, COMMENT, CREATE_VNODE } from './runtimeConstants'
|
||||
// Transforms that operate directly on a ChildNode. NodeTransforms may mutate,
|
||||
// replace or remove the node being processed.
|
||||
export type NodeTransform = (
|
||||
node: RootNode | ChildNode,
|
||||
node: RootNode | TemplateChildNode,
|
||||
context: TransformContext
|
||||
) => void | (() => void) | (() => void)[]
|
||||
|
||||
@ -59,10 +59,10 @@ export interface TransformContext extends Required<TransformOptions> {
|
||||
identifiers: { [name: string]: number | undefined }
|
||||
parent: ParentNode | null
|
||||
childIndex: number
|
||||
currentNode: RootNode | ChildNode | null
|
||||
currentNode: RootNode | TemplateChildNode | null
|
||||
helper(name: string): string
|
||||
replaceNode(node: ChildNode): void
|
||||
removeNode(node?: ChildNode): void
|
||||
replaceNode(node: TemplateChildNode): void
|
||||
removeNode(node?: TemplateChildNode): void
|
||||
onNodeRemoved: () => void
|
||||
addIdentifiers(exp: ExpressionNode): void
|
||||
removeIdentifiers(exp: ExpressionNode): void
|
||||
@ -207,7 +207,7 @@ export function traverseChildren(
|
||||
}
|
||||
|
||||
export function traverseNode(
|
||||
node: RootNode | ChildNode,
|
||||
node: RootNode | TemplateChildNode,
|
||||
context: TransformContext
|
||||
) {
|
||||
// apply transform plugins
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { NodeTransform } from '../transform'
|
||||
import {
|
||||
NodeTypes,
|
||||
ChildNode,
|
||||
TemplateChildNode,
|
||||
TextNode,
|
||||
InterpolationNode,
|
||||
CompoundExpressionNode
|
||||
} from '../ast'
|
||||
|
||||
const isText = (node: ChildNode): node is TextNode | InterpolationNode =>
|
||||
const isText = (
|
||||
node: TemplateChildNode
|
||||
): node is TextNode | InterpolationNode =>
|
||||
node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT
|
||||
|
||||
// Merge adjacent text nodes and expressions into a single expression
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
ElementTypes,
|
||||
ExpressionNode,
|
||||
Property,
|
||||
ChildNode,
|
||||
TemplateChildNode,
|
||||
SourceLocation
|
||||
} from '../ast'
|
||||
import { TransformContext, NodeTransform } from '../transform'
|
||||
@ -67,7 +67,7 @@ export function buildSlots(
|
||||
// 2. Iterate through children and check for template slots
|
||||
// <template v-slot:foo="{ prop }">
|
||||
let hasTemplateSlots = false
|
||||
let extraneousChild: ChildNode | undefined = undefined
|
||||
let extraneousChild: TemplateChildNode | undefined = undefined
|
||||
const seenSlotNames = new Set<string>()
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i]
|
||||
@ -135,7 +135,7 @@ export function buildSlots(
|
||||
function buildSlot(
|
||||
name: string | ExpressionNode,
|
||||
slotProps: ExpressionNode | undefined,
|
||||
children: ChildNode[],
|
||||
children: TemplateChildNode[],
|
||||
loc: SourceLocation
|
||||
): Property {
|
||||
return createObjectProperty(
|
||||
|
Loading…
Reference in New Issue
Block a user