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