]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: Sequence & Conditional expressions for AST
authorEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 13:27:34 +0000 (09:27 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 1 Oct 2019 16:25:29 +0000 (12:25 -0400)
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/optimizeText.ts
packages/compiler-core/src/transforms/vSlot.ts

index eb7ad3791473603e7749f356381c84cdd38251f0..b894be857059217845e1658078d8d052e5940857 100644 (file)
@@ -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
+  }
+}
index 1b3f53c821a3b958a9db19332b6c2310790be549..0d01772ad46b731eeaf7600ebb5a388b6da9b4db 100644 (file)
@@ -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(`)`)
+}
index 128f70034e488527d0a0780b47b1347bb1ce6f86..bc4f5258ff88c2c7cd25980a8634579b4ae73da2 100644 (file)
@@ -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['))
index ad0c512740b7feb3aa26edb789970b2bb2641dfe..6b8bc70cf147ca7b2bdaf9dd95e30f960a145c60 100644 (file)
@@ -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
index 09e637eeb0c2eff393a169b9c0946430f75e85c0..93bd15b7f06d9b9768874b89ab059540314fb9ae 100644 (file)
@@ -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
index eb9fd12bb6c87ed44f8476b5938d60a1529b3378..92ab4a6b657360274906312d9ca0dc359ad50976 100644 (file)
@@ -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(