JS_OBJECT_EXPRESSION,
JS_PROPERTY,
JS_ARRAY_EXPRESSION,
- JS_SLOT_FUNCTION
+ JS_SLOT_FUNCTION,
+ JS_SEQUENCE_EXPRESSION,
+ JS_CONDITIONAL_EXPRESSION
}
export const enum ElementTypes {
export type ExpressionNode = SimpleExpressionNode | CompoundExpressionNode
-export type ChildNode =
+export type TemplateChildNode =
| ElementNode
| InterpolationNode
| CompoundExpressionNode
export interface RootNode extends Node {
type: NodeTypes.ROOT
- children: ChildNode[]
+ children: TemplateChildNode[]
imports: string[]
statements: string[]
hoists: JSChildNode[]
tagType: ElementTypes
isSelfClosing: boolean
props: Array<AttributeNode | DirectiveNode>
- children: ChildNode[]
+ children: TemplateChildNode[]
codegenNode: CallExpression | undefined
}
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 {
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.
| 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 {
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(
export function createFunctionExpression(
params: ExpressionNode | undefined,
- returns: ChildNode[],
+ returns: TemplateChildNode[],
loc: SourceLocation
): SlotFunctionExpression {
return {
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
+ }
+}
import {
RootNode,
- ChildNode,
+ TemplateChildNode,
ElementNode,
IfNode,
ForNode,
CompoundExpressionNode,
SimpleExpressionNode,
ElementTypes,
- SlotFunctionExpression
+ SlotFunctionExpression,
+ SequenceExpression,
+ ConditionalExpression
} from './ast'
import { SourceMapGenerator, RawSourceMap } from 'source-map'
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
// - expression
// - <slot> outlet, which always produces an array
function genChildren(
- children: ChildNode[],
+ children: TemplateChildNode[],
context: CodegenContext,
allowSingle: boolean = false
) {
}
function genNodeListAsArray(
- nodes: (string | CodegenNode | ChildNode[])[],
+ nodes: (string | CodegenNode | TemplateChildNode[])[],
context: CodegenContext
) {
const multilines =
}
function genNodeList(
- nodes: (string | CodegenNode | ChildNode[])[],
+ nodes: (string | CodegenNode | TemplateChildNode[])[],
context: CodegenContext,
multilines: boolean = false
) {
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__) {
// 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(`)`)
+}
RootNode,
SourceLocation,
TextNode,
- ChildNode,
+ TemplateChildNode,
InterpolationNode
} from './ast'
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])) {
// '{{'
function pushNode(
context: ParserContext,
- nodes: ChildNode[],
- node: ChildNode
+ nodes: TemplateChildNode[],
+ node: TemplateChildNode
): void {
// ignore comments in production
/* istanbul ignore next */
function parseCDATA(
context: ParserContext,
ancestors: ElementNode[]
-): ChildNode[] {
+): TemplateChildNode[] {
__DEV__ &&
assert(last(ancestors) == null || last(ancestors)!.ns !== Namespaces.HTML)
__DEV__ && assert(startsWith(context.source, '<![CDATA['))
RootNode,
NodeTypes,
ParentNode,
- ChildNode,
+ TemplateChildNode,
ElementNode,
DirectiveNode,
Property,
// 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)[]
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
}
export function traverseNode(
- node: RootNode | ChildNode,
+ node: RootNode | TemplateChildNode,
context: TransformContext
) {
// apply transform plugins
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
ElementTypes,
ExpressionNode,
Property,
- ChildNode,
+ TemplateChildNode,
SourceLocation
} from '../ast'
import { TransformContext, NodeTransform } from '../transform'
// 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]
function buildSlot(
name: string | ExpressionNode,
slotProps: ExpressionNode | undefined,
- children: ChildNode[],
+ children: TemplateChildNode[],
loc: SourceLocation
): Property {
return createObjectProperty(