]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler): basic v-bind & v-on transforms
authorEvan You <yyx990803@gmail.com>
Mon, 23 Sep 2019 02:19:42 +0000 (22:19 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 23 Sep 2019 02:19:42 +0000 (22:19 -0400)
14 files changed:
packages/compiler-core/__tests__/parse.spec.ts
packages/compiler-core/__tests__/transform.spec.ts
packages/compiler-core/__tests__/transforms/element.spec.ts [new file with mode: 0644]
packages/compiler-core/__tests__/transforms/vFor.spec.ts
packages/compiler-core/__tests__/transforms/vIf.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/element.ts
packages/compiler-core/src/transforms/vBind.ts
packages/compiler-core/src/transforms/vFor.ts
packages/compiler-core/src/transforms/vIf.ts
packages/compiler-core/src/transforms/vOn.ts

index bb4116baa8ac114229cc1c7025597ab48c279c27..2bf25e6785ecc5429e2641cabb9fad0e6b205bb3 100644 (file)
@@ -195,7 +195,9 @@ describe('compiler: parse', () => {
         [
           {
             code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
-            loc: { offset: 4, line: 1, column: 5 }
+            loc: {
+              start: { offset: 4, line: 1, column: 5 }
+            }
           }
         ]
       ])
@@ -249,7 +251,9 @@ describe('compiler: parse', () => {
         [
           {
             code: ErrorCodes.MISSING_SEMICOLON_AFTER_CHARACTER_REFERENCE,
-            loc: { offset: 45, line: 1, column: 46 }
+            loc: {
+              start: { offset: 45, line: 1, column: 46 }
+            }
           }
         ]
       ])
@@ -274,7 +278,9 @@ describe('compiler: parse', () => {
         [
           {
             code: ErrorCodes.CONTROL_CHARACTER_REFERENCE,
-            loc: { offset: 0, line: 1, column: 1 }
+            loc: {
+              start: { offset: 0, line: 1, column: 1 }
+            }
           }
         ]
       ])
@@ -1254,9 +1260,11 @@ describe('compiler: parse', () => {
         {
           code: ErrorCodes.X_MISSING_END_TAG,
           loc: {
-            offset: 13,
-            line: 3,
-            column: 1
+            start: {
+              offset: 13,
+              line: 3,
+              column: 1
+            }
           }
         }
       ],
@@ -1264,9 +1272,11 @@ describe('compiler: parse', () => {
         {
           code: ErrorCodes.X_INVALID_END_TAG,
           loc: {
-            offset: 20,
-            line: 4,
-            column: 1
+            start: {
+              offset: 20,
+              line: 4,
+              column: 1
+            }
           }
         }
       ]
@@ -2386,7 +2396,7 @@ describe('compiler: parse', () => {
               expect(
                 spy.mock.calls.map(([err]) => ({
                   type: err.code,
-                  loc: err.loc
+                  loc: err.loc.start
                 }))
               ).toMatchObject(errors)
               expect(ast).toMatchSnapshot()
index c8aa3a97beb4133c76f9fc5dffca646b551286ec..3d262f2e937eb0d774ba2f211505291fabb3584e 100644 (file)
@@ -162,10 +162,10 @@ describe('compiler: transform', () => {
 
   test('onError option', () => {
     const ast = parse(`<div/>`)
-    const loc = ast.children[0].loc.start
+    const loc = ast.children[0].loc
     const plugin: NodeTransform = (node, context) => {
       context.onError(
-        createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc.start)
+        createCompilerError(ErrorCodes.X_INVALID_END_TAG, node.loc)
       )
     }
     const spy = jest.fn()
diff --git a/packages/compiler-core/__tests__/transforms/element.spec.ts b/packages/compiler-core/__tests__/transforms/element.spec.ts
new file mode 100644 (file)
index 0000000..4107656
--- /dev/null
@@ -0,0 +1,3 @@
+describe('compiler: element transform', () => {
+  test.todo('should work')
+})
index 64ed06e7e731a54ff2c6e9f1f978e8c5663a4d2c..5c5bc1cd67364ad7ebb833266c9919993453d26a 100644 (file)
@@ -4,142 +4,293 @@ import { transformFor } from '../../src/transforms/vFor'
 import { ForNode, NodeTypes } from '../../src/ast'
 import { ErrorCodes } from '../../src/errors'
 
-describe('v-for', () => {
-  describe('transform', () => {
-    test('number expression', () => {
-      const node = parse('<span v-for="index in 5" />')
+describe('compiler: transform v-for', () => {
+  test('number expression', () => {
+    const node = parse('<span v-for="index in 5" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).toBeUndefined()
-      expect(forNode.valueAlias!.content).toBe('index')
-      expect(forNode.source.content).toBe('5')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('index')
+    expect(forNode.source.content).toBe('5')
+  })
 
-    test('value', () => {
-      const node = parse('<span v-for="(item) in items" />')
+  test('value', () => {
+    const node = parse('<span v-for="(item) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).toBeUndefined()
-      expect(forNode.valueAlias!.content).toBe('item')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('item')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('object de-structured value', () => {
-      const node = parse('<span v-for="({ id, value }) in items" />')
+  test('object de-structured value', () => {
+    const node = parse('<span v-for="({ id, value }) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).toBeUndefined()
-      expect(forNode.valueAlias!.content).toBe('{ id, value }')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('{ id, value }')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('array de-structured value', () => {
-      const node = parse('<span v-for="([ id, value ]) in items" />')
+  test('array de-structured value', () => {
+    const node = parse('<span v-for="([ id, value ]) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).toBeUndefined()
-      expect(forNode.valueAlias!.content).toBe('[ id, value ]')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('[ id, value ]')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('value and key', () => {
-      const node = parse('<span v-for="(item, key) in items" />')
+  test('value and key', () => {
+    const node = parse('<span v-for="(item, key) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.keyAlias).not.toBeUndefined()
-      expect(forNode.keyAlias!.content).toBe('key')
-      expect(forNode.objectIndexAlias).toBeUndefined()
-      expect(forNode.valueAlias!.content).toBe('item')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.keyAlias).not.toBeUndefined()
+    expect(forNode.keyAlias!.content).toBe('key')
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('item')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('value, key and index', () => {
-      const node = parse('<span v-for="(value, key, index) in items" />')
+  test('value, key and index', () => {
+    const node = parse('<span v-for="(value, key, index) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).not.toBeUndefined()
-      expect(forNode.keyAlias!.content).toBe('key')
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
-      expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias!.content).toBe('value')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).not.toBeUndefined()
+    expect(forNode.keyAlias!.content).toBe('key')
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias!.content).toBe('value')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('skipped key', () => {
-      const node = parse('<span v-for="(value,,index) in items" />')
+  test('skipped key', () => {
+    const node = parse('<span v-for="(value,,index) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
-      expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias!.content).toBe('value')
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias!.content).toBe('value')
+    expect(forNode.source.content).toBe('items')
+  })
 
-    test('skipped value and key', () => {
-      const node = parse('<span v-for="(,,index) in items" />')
+  test('skipped value and key', () => {
+    const node = parse('<span v-for="(,,index) in items" />')
 
-      transform(node, { nodeTransforms: [transformFor] })
+    transform(node, { nodeTransforms: [transformFor] })
 
-      expect(node.children.length).toBe(1)
+    expect(node.children.length).toBe(1)
 
-      const forNode = node.children[0] as ForNode
+    const forNode = node.children[0] as ForNode
 
-      expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
-      expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias).toBeUndefined()
-      expect(forNode.source.content).toBe('items')
-    })
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias).toBeUndefined()
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('unbracketed value', () => {
+    const node = parse('<span v-for="item in items" />')
+
+    transform(node, { nodeTransforms: [transformFor] })
+
+    expect(node.children.length).toBe(1)
+
+    const forNode = node.children[0] as ForNode
+
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('item')
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('unbracketed value and key', () => {
+    const node = parse('<span v-for="item, key in items" />')
+
+    transform(node, { nodeTransforms: [transformFor] })
+
+    expect(node.children.length).toBe(1)
+
+    const forNode = node.children[0] as ForNode
+
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).not.toBeUndefined()
+    expect(forNode.keyAlias!.content).toBe('key')
+    expect(forNode.objectIndexAlias).toBeUndefined()
+    expect(forNode.valueAlias!.content).toBe('item')
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('unbracketed value, key and index', () => {
+    const node = parse('<span v-for="value, key, index in items" />')
+
+    transform(node, { nodeTransforms: [transformFor] })
+
+    expect(node.children.length).toBe(1)
+
+    const forNode = node.children[0] as ForNode
+
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).not.toBeUndefined()
+    expect(forNode.keyAlias!.content).toBe('key')
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias!.content).toBe('value')
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('unbracketed skipped key', () => {
+    const node = parse('<span v-for="value, , index in items" />')
+
+    transform(node, { nodeTransforms: [transformFor] })
+
+    expect(node.children.length).toBe(1)
+
+    const forNode = node.children[0] as ForNode
 
-    test('unbracketed value', () => {
-      const node = parse('<span v-for="item in items" />')
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias!.content).toBe('value')
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('unbracketed skipped value and key', () => {
+    const node = parse('<span v-for=", , index in items" />')
+
+    transform(node, { nodeTransforms: [transformFor] })
+
+    expect(node.children.length).toBe(1)
+
+    const forNode = node.children[0] as ForNode
+
+    expect(forNode.type).toBe(NodeTypes.FOR)
+    expect(forNode.keyAlias).toBeUndefined()
+    expect(forNode.objectIndexAlias).not.toBeUndefined()
+    expect(forNode.objectIndexAlias!.content).toBe('index')
+    expect(forNode.valueAlias).toBeUndefined()
+    expect(forNode.source.content).toBe('items')
+  })
+
+  test('missing expression', () => {
+    const node = parse('<span v-for />')
+    const onError = jest.fn()
+    transform(node, { nodeTransforms: [transformFor], onError })
+
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(
+      expect.objectContaining({
+        code: ErrorCodes.X_FOR_NO_EXPRESSION
+      })
+    )
+  })
+
+  test('empty expression', () => {
+    const node = parse('<span v-for="" />')
+    const onError = jest.fn()
+    transform(node, { nodeTransforms: [transformFor], onError })
+
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(
+      expect.objectContaining({
+        code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
+      })
+    )
+  })
+
+  test('invalid expression', () => {
+    const node = parse('<span v-for="items" />')
+    const onError = jest.fn()
+    transform(node, { nodeTransforms: [transformFor], onError })
+
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(
+      expect.objectContaining({
+        code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
+      })
+    )
+  })
+
+  test('missing source', () => {
+    const node = parse('<span v-for="item in" />')
+    const onError = jest.fn()
+    transform(node, { nodeTransforms: [transformFor], onError })
+
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(
+      expect.objectContaining({
+        code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
+      })
+    )
+  })
+
+  test('missing value', () => {
+    const node = parse('<span v-for="in items" />')
+    const onError = jest.fn()
+    transform(node, { nodeTransforms: [transformFor], onError })
+
+    expect(onError).toHaveBeenCalledTimes(1)
+    expect(onError).toHaveBeenCalledWith(
+      expect.objectContaining({
+        code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
+      })
+    )
+  })
+
+  describe('source location', () => {
+    test('value & source', () => {
+      const source = '<span v-for="item in items" />'
+      const node = parse(source)
 
       transform(node, { nodeTransforms: [transformFor] })
 
@@ -148,14 +299,29 @@ describe('v-for', () => {
       const forNode = node.children[0] as ForNode
 
       expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).toBeUndefined()
+
       expect(forNode.valueAlias!.content).toBe('item')
+      expect(forNode.valueAlias!.loc.start.offset).toBe(
+        source.indexOf('item') - 1
+      )
+      expect(forNode.valueAlias!.loc.start.line).toBe(1)
+      expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
+      expect(forNode.valueAlias!.loc.end.line).toBe(1)
+      expect(forNode.valueAlias!.loc.end.column).toBe(
+        source.indexOf('item') + 4
+      )
+
       expect(forNode.source.content).toBe('items')
+      expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
+      expect(forNode.source.loc.start.line).toBe(1)
+      expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
+      expect(forNode.source.loc.end.line).toBe(1)
+      expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
     })
 
-    test('unbracketed value and key', () => {
-      const node = parse('<span v-for="item, key in items" />')
+    test('bracketed value', () => {
+      const source = '<span v-for="( item ) in items" />'
+      const node = parse(source)
 
       transform(node, { nodeTransforms: [transformFor] })
 
@@ -164,15 +330,29 @@ describe('v-for', () => {
       const forNode = node.children[0] as ForNode
 
       expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).not.toBeUndefined()
-      expect(forNode.keyAlias!.content).toBe('key')
-      expect(forNode.objectIndexAlias).toBeUndefined()
+
       expect(forNode.valueAlias!.content).toBe('item')
+      expect(forNode.valueAlias!.loc.start.offset).toBe(
+        source.indexOf('item') - 1
+      )
+      expect(forNode.valueAlias!.loc.start.line).toBe(1)
+      expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
+      expect(forNode.valueAlias!.loc.end.line).toBe(1)
+      expect(forNode.valueAlias!.loc.end.column).toBe(
+        source.indexOf('item') + 4
+      )
+
       expect(forNode.source.content).toBe('items')
+      expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
+      expect(forNode.source.loc.start.line).toBe(1)
+      expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
+      expect(forNode.source.loc.end.line).toBe(1)
+      expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
     })
 
-    test('unbracketed value, key and index', () => {
-      const node = parse('<span v-for="value, key, index in items" />')
+    test('de-structured value', () => {
+      const source = '<span v-for="(  { id, key })in items" />'
+      const node = parse(source)
 
       transform(node, { nodeTransforms: [transformFor] })
 
@@ -181,16 +361,31 @@ describe('v-for', () => {
       const forNode = node.children[0] as ForNode
 
       expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).not.toBeUndefined()
-      expect(forNode.keyAlias!.content).toBe('key')
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
-      expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias!.content).toBe('value')
+
+      expect(forNode.valueAlias!.content).toBe('{ id, key }')
+      expect(forNode.valueAlias!.loc.start.offset).toBe(
+        source.indexOf('{ id, key }') - 1
+      )
+      expect(forNode.valueAlias!.loc.start.line).toBe(1)
+      expect(forNode.valueAlias!.loc.start.column).toBe(
+        source.indexOf('{ id, key }')
+      )
+      expect(forNode.valueAlias!.loc.end.line).toBe(1)
+      expect(forNode.valueAlias!.loc.end.column).toBe(
+        source.indexOf('{ id, key }') + '{ id, key }'.length
+      )
+
       expect(forNode.source.content).toBe('items')
+      expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
+      expect(forNode.source.loc.start.line).toBe(1)
+      expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
+      expect(forNode.source.loc.end.line).toBe(1)
+      expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
     })
 
-    test('unbracketed skipped key', () => {
-      const node = parse('<span v-for="value, , index in items" />')
+    test('bracketed value, key, index', () => {
+      const source = '<span v-for="( item, key, index ) in items" />'
+      const node = parse(source)
 
       transform(node, { nodeTransforms: [transformFor] })
 
@@ -199,15 +394,49 @@ describe('v-for', () => {
       const forNode = node.children[0] as ForNode
 
       expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
+
+      expect(forNode.valueAlias!.content).toBe('item')
+      expect(forNode.valueAlias!.loc.start.offset).toBe(
+        source.indexOf('item') - 1
+      )
+      expect(forNode.valueAlias!.loc.start.line).toBe(1)
+      expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
+      expect(forNode.valueAlias!.loc.end.line).toBe(1)
+      expect(forNode.valueAlias!.loc.end.column).toBe(
+        source.indexOf('item') + 4
+      )
+
+      expect(forNode.keyAlias!.content).toBe('key')
+      expect(forNode.keyAlias!.loc.start.offset).toBe(source.indexOf('key') - 1)
+      expect(forNode.keyAlias!.loc.start.line).toBe(1)
+      expect(forNode.keyAlias!.loc.start.column).toBe(source.indexOf('key'))
+      expect(forNode.keyAlias!.loc.end.line).toBe(1)
+      expect(forNode.keyAlias!.loc.end.column).toBe(source.indexOf('key') + 3)
+
       expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias!.content).toBe('value')
+      expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
+        source.indexOf('index') - 1
+      )
+      expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
+      expect(forNode.objectIndexAlias!.loc.start.column).toBe(
+        source.indexOf('index')
+      )
+      expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
+      expect(forNode.objectIndexAlias!.loc.end.column).toBe(
+        source.indexOf('index') + 5
+      )
+
       expect(forNode.source.content).toBe('items')
+      expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
+      expect(forNode.source.loc.start.line).toBe(1)
+      expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
+      expect(forNode.source.loc.end.line).toBe(1)
+      expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
     })
 
-    test('unbracketed skipped value and key', () => {
-      const node = parse('<span v-for=", , index in items" />')
+    test('skipped key', () => {
+      const source = '<span v-for="( item,, index ) in items" />'
+      const node = parse(source)
 
       transform(node, { nodeTransforms: [transformFor] })
 
@@ -216,288 +445,37 @@ describe('v-for', () => {
       const forNode = node.children[0] as ForNode
 
       expect(forNode.type).toBe(NodeTypes.FOR)
-      expect(forNode.keyAlias).toBeUndefined()
-      expect(forNode.objectIndexAlias).not.toBeUndefined()
-      expect(forNode.objectIndexAlias!.content).toBe('index')
-      expect(forNode.valueAlias).toBeUndefined()
-      expect(forNode.source.content).toBe('items')
-    })
-
-    test('missing expression', () => {
-      const node = parse('<span v-for />')
-      const onError = jest.fn()
-      transform(node, { nodeTransforms: [transformFor], onError })
 
-      expect(onError).toHaveBeenCalledTimes(1)
-      expect(onError).toHaveBeenCalledWith(
-        expect.objectContaining({
-          code: ErrorCodes.X_FOR_NO_EXPRESSION
-        })
+      expect(forNode.valueAlias!.content).toBe('item')
+      expect(forNode.valueAlias!.loc.start.offset).toBe(
+        source.indexOf('item') - 1
       )
-    })
-
-    test('empty expression', () => {
-      const node = parse('<span v-for="" />')
-      const onError = jest.fn()
-      transform(node, { nodeTransforms: [transformFor], onError })
-
-      expect(onError).toHaveBeenCalledTimes(1)
-      expect(onError).toHaveBeenCalledWith(
-        expect.objectContaining({
-          code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
-        })
+      expect(forNode.valueAlias!.loc.start.line).toBe(1)
+      expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item'))
+      expect(forNode.valueAlias!.loc.end.line).toBe(1)
+      expect(forNode.valueAlias!.loc.end.column).toBe(
+        source.indexOf('item') + 4
       )
-    })
-
-    test('invalid expression', () => {
-      const node = parse('<span v-for="items" />')
-      const onError = jest.fn()
-      transform(node, { nodeTransforms: [transformFor], onError })
 
-      expect(onError).toHaveBeenCalledTimes(1)
-      expect(onError).toHaveBeenCalledWith(
-        expect.objectContaining({
-          code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
-        })
+      expect(forNode.objectIndexAlias!.content).toBe('index')
+      expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
+        source.indexOf('index') - 1
       )
-    })
-
-    test('missing source', () => {
-      const node = parse('<span v-for="item in" />')
-      const onError = jest.fn()
-      transform(node, { nodeTransforms: [transformFor], onError })
-
-      expect(onError).toHaveBeenCalledTimes(1)
-      expect(onError).toHaveBeenCalledWith(
-        expect.objectContaining({
-          code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
-        })
+      expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
+      expect(forNode.objectIndexAlias!.loc.start.column).toBe(
+        source.indexOf('index')
       )
-    })
-
-    test('missing value', () => {
-      const node = parse('<span v-for="in items" />')
-      const onError = jest.fn()
-      transform(node, { nodeTransforms: [transformFor], onError })
-
-      expect(onError).toHaveBeenCalledTimes(1)
-      expect(onError).toHaveBeenCalledWith(
-        expect.objectContaining({
-          code: ErrorCodes.X_FOR_MALFORMED_EXPRESSION
-        })
+      expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
+      expect(forNode.objectIndexAlias!.loc.end.column).toBe(
+        source.indexOf('index') + 5
       )
-    })
 
-    describe('source location', () => {
-      test('value & source', () => {
-        const source = '<span v-for="item in items" />'
-        const node = parse(source)
-
-        transform(node, { nodeTransforms: [transformFor] })
-
-        expect(node.children.length).toBe(1)
-
-        const forNode = node.children[0] as ForNode
-
-        expect(forNode.type).toBe(NodeTypes.FOR)
-
-        expect(forNode.valueAlias!.content).toBe('item')
-        expect(forNode.valueAlias!.loc.start.offset).toBe(
-          source.indexOf('item') - 1
-        )
-        expect(forNode.valueAlias!.loc.start.line).toBe(1)
-        expect(forNode.valueAlias!.loc.start.column).toBe(
-          source.indexOf('item')
-        )
-        expect(forNode.valueAlias!.loc.end.line).toBe(1)
-        expect(forNode.valueAlias!.loc.end.column).toBe(
-          source.indexOf('item') + 4
-        )
-
-        expect(forNode.source.content).toBe('items')
-        expect(forNode.source.loc.start.offset).toBe(
-          source.indexOf('items') - 1
-        )
-        expect(forNode.source.loc.start.line).toBe(1)
-        expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
-        expect(forNode.source.loc.end.line).toBe(1)
-        expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
-      })
-
-      test('bracketed value', () => {
-        const source = '<span v-for="( item ) in items" />'
-        const node = parse(source)
-
-        transform(node, { nodeTransforms: [transformFor] })
-
-        expect(node.children.length).toBe(1)
-
-        const forNode = node.children[0] as ForNode
-
-        expect(forNode.type).toBe(NodeTypes.FOR)
-
-        expect(forNode.valueAlias!.content).toBe('item')
-        expect(forNode.valueAlias!.loc.start.offset).toBe(
-          source.indexOf('item') - 1
-        )
-        expect(forNode.valueAlias!.loc.start.line).toBe(1)
-        expect(forNode.valueAlias!.loc.start.column).toBe(
-          source.indexOf('item')
-        )
-        expect(forNode.valueAlias!.loc.end.line).toBe(1)
-        expect(forNode.valueAlias!.loc.end.column).toBe(
-          source.indexOf('item') + 4
-        )
-
-        expect(forNode.source.content).toBe('items')
-        expect(forNode.source.loc.start.offset).toBe(
-          source.indexOf('items') - 1
-        )
-        expect(forNode.source.loc.start.line).toBe(1)
-        expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
-        expect(forNode.source.loc.end.line).toBe(1)
-        expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
-      })
-
-      test('de-structured value', () => {
-        const source = '<span v-for="(  { id, key })in items" />'
-        const node = parse(source)
-
-        transform(node, { nodeTransforms: [transformFor] })
-
-        expect(node.children.length).toBe(1)
-
-        const forNode = node.children[0] as ForNode
-
-        expect(forNode.type).toBe(NodeTypes.FOR)
-
-        expect(forNode.valueAlias!.content).toBe('{ id, key }')
-        expect(forNode.valueAlias!.loc.start.offset).toBe(
-          source.indexOf('{ id, key }') - 1
-        )
-        expect(forNode.valueAlias!.loc.start.line).toBe(1)
-        expect(forNode.valueAlias!.loc.start.column).toBe(
-          source.indexOf('{ id, key }')
-        )
-        expect(forNode.valueAlias!.loc.end.line).toBe(1)
-        expect(forNode.valueAlias!.loc.end.column).toBe(
-          source.indexOf('{ id, key }') + '{ id, key }'.length
-        )
-
-        expect(forNode.source.content).toBe('items')
-        expect(forNode.source.loc.start.offset).toBe(
-          source.indexOf('items') - 1
-        )
-        expect(forNode.source.loc.start.line).toBe(1)
-        expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
-        expect(forNode.source.loc.end.line).toBe(1)
-        expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
-      })
-
-      test('bracketed value, key, index', () => {
-        const source = '<span v-for="( item, key, index ) in items" />'
-        const node = parse(source)
-
-        transform(node, { nodeTransforms: [transformFor] })
-
-        expect(node.children.length).toBe(1)
-
-        const forNode = node.children[0] as ForNode
-
-        expect(forNode.type).toBe(NodeTypes.FOR)
-
-        expect(forNode.valueAlias!.content).toBe('item')
-        expect(forNode.valueAlias!.loc.start.offset).toBe(
-          source.indexOf('item') - 1
-        )
-        expect(forNode.valueAlias!.loc.start.line).toBe(1)
-        expect(forNode.valueAlias!.loc.start.column).toBe(
-          source.indexOf('item')
-        )
-        expect(forNode.valueAlias!.loc.end.line).toBe(1)
-        expect(forNode.valueAlias!.loc.end.column).toBe(
-          source.indexOf('item') + 4
-        )
-
-        expect(forNode.keyAlias!.content).toBe('key')
-        expect(forNode.keyAlias!.loc.start.offset).toBe(
-          source.indexOf('key') - 1
-        )
-        expect(forNode.keyAlias!.loc.start.line).toBe(1)
-        expect(forNode.keyAlias!.loc.start.column).toBe(source.indexOf('key'))
-        expect(forNode.keyAlias!.loc.end.line).toBe(1)
-        expect(forNode.keyAlias!.loc.end.column).toBe(source.indexOf('key') + 3)
-
-        expect(forNode.objectIndexAlias!.content).toBe('index')
-        expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
-          source.indexOf('index') - 1
-        )
-        expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
-        expect(forNode.objectIndexAlias!.loc.start.column).toBe(
-          source.indexOf('index')
-        )
-        expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
-        expect(forNode.objectIndexAlias!.loc.end.column).toBe(
-          source.indexOf('index') + 5
-        )
-
-        expect(forNode.source.content).toBe('items')
-        expect(forNode.source.loc.start.offset).toBe(
-          source.indexOf('items') - 1
-        )
-        expect(forNode.source.loc.start.line).toBe(1)
-        expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
-        expect(forNode.source.loc.end.line).toBe(1)
-        expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
-      })
-
-      test('skipped key', () => {
-        const source = '<span v-for="( item,, index ) in items" />'
-        const node = parse(source)
-
-        transform(node, { nodeTransforms: [transformFor] })
-
-        expect(node.children.length).toBe(1)
-
-        const forNode = node.children[0] as ForNode
-
-        expect(forNode.type).toBe(NodeTypes.FOR)
-
-        expect(forNode.valueAlias!.content).toBe('item')
-        expect(forNode.valueAlias!.loc.start.offset).toBe(
-          source.indexOf('item') - 1
-        )
-        expect(forNode.valueAlias!.loc.start.line).toBe(1)
-        expect(forNode.valueAlias!.loc.start.column).toBe(
-          source.indexOf('item')
-        )
-        expect(forNode.valueAlias!.loc.end.line).toBe(1)
-        expect(forNode.valueAlias!.loc.end.column).toBe(
-          source.indexOf('item') + 4
-        )
-
-        expect(forNode.objectIndexAlias!.content).toBe('index')
-        expect(forNode.objectIndexAlias!.loc.start.offset).toBe(
-          source.indexOf('index') - 1
-        )
-        expect(forNode.objectIndexAlias!.loc.start.line).toBe(1)
-        expect(forNode.objectIndexAlias!.loc.start.column).toBe(
-          source.indexOf('index')
-        )
-        expect(forNode.objectIndexAlias!.loc.end.line).toBe(1)
-        expect(forNode.objectIndexAlias!.loc.end.column).toBe(
-          source.indexOf('index') + 5
-        )
-
-        expect(forNode.source.content).toBe('items')
-        expect(forNode.source.loc.start.offset).toBe(
-          source.indexOf('items') - 1
-        )
-        expect(forNode.source.loc.start.line).toBe(1)
-        expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
-        expect(forNode.source.loc.end.line).toBe(1)
-        expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
-      })
+      expect(forNode.source.content).toBe('items')
+      expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1)
+      expect(forNode.source.loc.start.line).toBe(1)
+      expect(forNode.source.loc.start.column).toBe(source.indexOf('items'))
+      expect(forNode.source.loc.end.line).toBe(1)
+      expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5)
     })
   })
 })
index 86809b9bdbf33cff9174beba1db07fc607947168..6339f4de2226614ea88f37d934092dd8bffc139f 100644 (file)
@@ -10,248 +10,242 @@ import {
 } from '../../src/ast'
 import { ErrorCodes } from '../../src/errors'
 
-describe('compiler: v-if', () => {
-  describe('transform', () => {
-    test('basic v-if', () => {
-      const ast = parse(`<div v-if="ok"/>`)
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(1)
-      expect(node.branches[0].condition!.content).toBe(`ok`)
-      expect(node.branches[0].children.length).toBe(1)
-      expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
+describe('compiler: transform v-if', () => {
+  test('basic v-if', () => {
+    const ast = parse(`<div v-if="ok"/>`)
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(1)
+    expect(node.branches[0].condition!.content).toBe(`ok`)
+    expect(node.branches[0].children.length).toBe(1)
+    expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
+  })
 
-    test('template v-if', () => {
-      const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(1)
-      expect(node.branches[0].condition!.content).toBe(`ok`)
-      expect(node.branches[0].children.length).toBe(3)
-      expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
-      expect(node.branches[0].children[1].type).toBe(NodeTypes.TEXT)
-      expect((node.branches[0].children[1] as TextNode).content).toBe(`hello`)
-      expect(node.branches[0].children[2].type).toBe(NodeTypes.ELEMENT)
-      expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
+  test('template v-if', () => {
+    const ast = parse(`<template v-if="ok"><div/>hello<p/></template>`)
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(1)
+    expect(node.branches[0].condition!.content).toBe(`ok`)
+    expect(node.branches[0].children.length).toBe(3)
+    expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((node.branches[0].children[0] as ElementNode).tag).toBe(`div`)
+    expect(node.branches[0].children[1].type).toBe(NodeTypes.TEXT)
+    expect((node.branches[0].children[1] as TextNode).content).toBe(`hello`)
+    expect(node.branches[0].children[2].type).toBe(NodeTypes.ELEMENT)
+    expect((node.branches[0].children[2] as ElementNode).tag).toBe(`p`)
+  })
 
-    test('v-if + v-else', () => {
-      const ast = parse(`<div v-if="ok"/><p v-else/>`)
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      // should fold branches
-      expect(ast.children.length).toBe(1)
-
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(2)
-
-      const b1 = node.branches[0]
-      expect(b1.condition!.content).toBe(`ok`)
-      expect(b1.children.length).toBe(1)
-      expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b1.children[0] as ElementNode).tag).toBe(`div`)
-
-      const b2 = node.branches[1]
-      expect(b2.condition).toBeUndefined()
-      expect(b2.children.length).toBe(1)
-      expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b2.children[0] as ElementNode).tag).toBe(`p`)
+  test('v-if + v-else', () => {
+    const ast = parse(`<div v-if="ok"/><p v-else/>`)
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    // should fold branches
+    expect(ast.children.length).toBe(1)
+
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(2)
+
+    const b1 = node.branches[0]
+    expect(b1.condition!.content).toBe(`ok`)
+    expect(b1.children.length).toBe(1)
+    expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b1.children[0] as ElementNode).tag).toBe(`div`)
+
+    const b2 = node.branches[1]
+    expect(b2.condition).toBeUndefined()
+    expect(b2.children.length).toBe(1)
+    expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b2.children[0] as ElementNode).tag).toBe(`p`)
+  })
 
-    test('v-if + v-else-if', () => {
-      const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      // should fold branches
-      expect(ast.children.length).toBe(1)
-
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(2)
-
-      const b1 = node.branches[0]
-      expect(b1.condition!.content).toBe(`ok`)
-      expect(b1.children.length).toBe(1)
-      expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b1.children[0] as ElementNode).tag).toBe(`div`)
-
-      const b2 = node.branches[1]
-      expect(b2.condition!.content).toBe(`orNot`)
-      expect(b2.children.length).toBe(1)
-      expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b2.children[0] as ElementNode).tag).toBe(`p`)
+  test('v-if + v-else-if', () => {
+    const ast = parse(`<div v-if="ok"/><p v-else-if="orNot"/>`)
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    // should fold branches
+    expect(ast.children.length).toBe(1)
+
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(2)
+
+    const b1 = node.branches[0]
+    expect(b1.condition!.content).toBe(`ok`)
+    expect(b1.children.length).toBe(1)
+    expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b1.children[0] as ElementNode).tag).toBe(`div`)
+
+    const b2 = node.branches[1]
+    expect(b2.condition!.content).toBe(`orNot`)
+    expect(b2.children.length).toBe(1)
+    expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b2.children[0] as ElementNode).tag).toBe(`p`)
+  })
 
-    test('v-if + v-else-if + v-else', () => {
-      const ast = parse(
-        `<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
-      )
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      // should fold branches
-      expect(ast.children.length).toBe(1)
-
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(3)
-
-      const b1 = node.branches[0]
-      expect(b1.condition!.content).toBe(`ok`)
-      expect(b1.children.length).toBe(1)
-      expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b1.children[0] as ElementNode).tag).toBe(`div`)
-
-      const b2 = node.branches[1]
-      expect(b2.condition!.content).toBe(`orNot`)
-      expect(b2.children.length).toBe(1)
-      expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b2.children[0] as ElementNode).tag).toBe(`p`)
-
-      const b3 = node.branches[2]
-      expect(b3.condition).toBeUndefined()
-      expect(b3.children.length).toBe(1)
-      expect(b3.children[0].type).toBe(NodeTypes.TEXT)
-      expect((b3.children[0] as TextNode).content).toBe(`fine`)
+  test('v-if + v-else-if + v-else', () => {
+    const ast = parse(
+      `<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`
+    )
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    // should fold branches
+    expect(ast.children.length).toBe(1)
+
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(3)
+
+    const b1 = node.branches[0]
+    expect(b1.condition!.content).toBe(`ok`)
+    expect(b1.children.length).toBe(1)
+    expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b1.children[0] as ElementNode).tag).toBe(`div`)
+
+    const b2 = node.branches[1]
+    expect(b2.condition!.content).toBe(`orNot`)
+    expect(b2.children.length).toBe(1)
+    expect(b2.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b2.children[0] as ElementNode).tag).toBe(`p`)
+
+    const b3 = node.branches[2]
+    expect(b3.condition).toBeUndefined()
+    expect(b3.children.length).toBe(1)
+    expect(b3.children[0].type).toBe(NodeTypes.TEXT)
+    expect((b3.children[0] as TextNode).content).toBe(`fine`)
+  })
 
-    test('comment between branches', () => {
-      const ast = parse(`
-        <div v-if="ok"/>
-        <!--foo-->
-        <p v-else-if="orNot"/>
-        <!--bar-->
-        <template v-else>fine</template>
-      `)
-      transform(ast, {
-        nodeTransforms: [transformIf]
-      })
-      // should fold branches
-      expect(ast.children.length).toBe(1)
-
-      const node = ast.children[0] as IfNode
-      expect(node.type).toBe(NodeTypes.IF)
-      expect(node.branches.length).toBe(3)
-
-      const b1 = node.branches[0]
-      expect(b1.condition!.content).toBe(`ok`)
-      expect(b1.children.length).toBe(1)
-      expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
-      expect((b1.children[0] as ElementNode).tag).toBe(`div`)
-
-      const b2 = node.branches[1]
-      expect(b2.condition!.content).toBe(`orNot`)
-      expect(b2.children.length).toBe(2)
-      expect(b2.children[0].type).toBe(NodeTypes.COMMENT)
-      expect((b2.children[0] as CommentNode).content).toBe(`foo`)
-      expect(b2.children[1].type).toBe(NodeTypes.ELEMENT)
-      expect((b2.children[1] as ElementNode).tag).toBe(`p`)
-
-      const b3 = node.branches[2]
-      expect(b3.condition).toBeUndefined()
-      expect(b3.children.length).toBe(2)
-      expect(b3.children[0].type).toBe(NodeTypes.COMMENT)
-      expect((b3.children[0] as CommentNode).content).toBe(`bar`)
-      expect(b3.children[1].type).toBe(NodeTypes.TEXT)
-      expect((b3.children[1] as TextNode).content).toBe(`fine`)
+  test('comment between branches', () => {
+    const ast = parse(`
+      <div v-if="ok"/>
+      <!--foo-->
+      <p v-else-if="orNot"/>
+      <!--bar-->
+      <template v-else>fine</template>
+    `)
+    transform(ast, {
+      nodeTransforms: [transformIf]
     })
+    // should fold branches
+    expect(ast.children.length).toBe(1)
+
+    const node = ast.children[0] as IfNode
+    expect(node.type).toBe(NodeTypes.IF)
+    expect(node.branches.length).toBe(3)
+
+    const b1 = node.branches[0]
+    expect(b1.condition!.content).toBe(`ok`)
+    expect(b1.children.length).toBe(1)
+    expect(b1.children[0].type).toBe(NodeTypes.ELEMENT)
+    expect((b1.children[0] as ElementNode).tag).toBe(`div`)
+
+    const b2 = node.branches[1]
+    expect(b2.condition!.content).toBe(`orNot`)
+    expect(b2.children.length).toBe(2)
+    expect(b2.children[0].type).toBe(NodeTypes.COMMENT)
+    expect((b2.children[0] as CommentNode).content).toBe(`foo`)
+    expect(b2.children[1].type).toBe(NodeTypes.ELEMENT)
+    expect((b2.children[1] as ElementNode).tag).toBe(`p`)
+
+    const b3 = node.branches[2]
+    expect(b3.condition).toBeUndefined()
+    expect(b3.children.length).toBe(2)
+    expect(b3.children[0].type).toBe(NodeTypes.COMMENT)
+    expect((b3.children[0] as CommentNode).content).toBe(`bar`)
+    expect(b3.children[1].type).toBe(NodeTypes.TEXT)
+    expect((b3.children[1] as TextNode).content).toBe(`fine`)
+  })
 
-    test('error on v-else missing adjacent v-if', () => {
-      const ast = parse(`<div v-else/>`)
-      const spy = jest.fn()
-      transform(ast, {
-        nodeTransforms: [transformIf],
-        onError: spy
-      })
-      expect(spy.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
-          loc: ast.children[0].loc.start
-        }
-      ])
-
-      const ast2 = parse(`<div/><div v-else/>`)
-      const spy2 = jest.fn()
-      transform(ast2, {
-        nodeTransforms: [transformIf],
-        onError: spy2
-      })
-      expect(spy2.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
-          loc: ast2.children[1].loc.start
-        }
-      ])
-
-      const ast3 = parse(`<div/>foo<div v-else/>`)
-      const spy3 = jest.fn()
-      transform(ast3, {
-        nodeTransforms: [transformIf],
-        onError: spy3
-      })
-      expect(spy3.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
-          loc: ast3.children[2].loc.start
-        }
-      ])
+  test('error on v-else missing adjacent v-if', () => {
+    const ast = parse(`<div v-else/>`)
+    const spy = jest.fn()
+    transform(ast, {
+      nodeTransforms: [transformIf],
+      onError: spy
     })
-
-    test('error on v-else-if missing adjacent v-if', () => {
-      const ast = parse(`<div v-else-if="foo"/>`)
-      const spy = jest.fn()
-      transform(ast, {
-        nodeTransforms: [transformIf],
-        onError: spy
-      })
-      expect(spy.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
-          loc: ast.children[0].loc.start
-        }
-      ])
-
-      const ast2 = parse(`<div/><div v-else-if="foo"/>`)
-      const spy2 = jest.fn()
-      transform(ast2, {
-        nodeTransforms: [transformIf],
-        onError: spy2
-      })
-      expect(spy2.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
-          loc: ast2.children[1].loc.start
-        }
-      ])
-
-      const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
-      const spy3 = jest.fn()
-      transform(ast3, {
-        nodeTransforms: [transformIf],
-        onError: spy3
-      })
-      expect(spy3.mock.calls[0]).toMatchObject([
-        {
-          code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
-          loc: ast3.children[2].loc.start
-        }
-      ])
+    expect(spy.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+        loc: ast.children[0].loc
+      }
+    ])
+
+    const ast2 = parse(`<div/><div v-else/>`)
+    const spy2 = jest.fn()
+    transform(ast2, {
+      nodeTransforms: [transformIf],
+      onError: spy2
+    })
+    expect(spy2.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+        loc: ast2.children[1].loc
+      }
+    ])
+
+    const ast3 = parse(`<div/>foo<div v-else/>`)
+    const spy3 = jest.fn()
+    transform(ast3, {
+      nodeTransforms: [transformIf],
+      onError: spy3
     })
+    expect(spy3.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_NO_ADJACENT_IF,
+        loc: ast3.children[2].loc
+      }
+    ])
   })
 
-  describe('codegen', () => {
-    // TODO
+  test('error on v-else-if missing adjacent v-if', () => {
+    const ast = parse(`<div v-else-if="foo"/>`)
+    const spy = jest.fn()
+    transform(ast, {
+      nodeTransforms: [transformIf],
+      onError: spy
+    })
+    expect(spy.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+        loc: ast.children[0].loc
+      }
+    ])
+
+    const ast2 = parse(`<div/><div v-else-if="foo"/>`)
+    const spy2 = jest.fn()
+    transform(ast2, {
+      nodeTransforms: [transformIf],
+      onError: spy2
+    })
+    expect(spy2.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+        loc: ast2.children[1].loc
+      }
+    ])
+
+    const ast3 = parse(`<div/>foo<div v-else-if="foo"/>`)
+    const spy3 = jest.fn()
+    transform(ast3, {
+      nodeTransforms: [transformIf],
+      onError: spy3
+    })
+    expect(spy3.mock.calls[0]).toMatchObject([
+      {
+        code: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
+        loc: ast3.children[2].loc
+      }
+    ])
   })
 })
index 296c1e0b60cde7c1bb939e59b8db2cad936bacc2..a495ddcef6d50f5e69b77dd5b96f37bd7affd1b6 100644 (file)
@@ -366,7 +366,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
   const { push, indent, deindent, newline } = context
   const { properties } = node
   const multilines = properties.length > 1
-  push(`{`, node)
+  push(multilines ? `{` : `{ `, node)
   multilines && indent()
   for (let i = 0; i < properties.length; i++) {
     const { key, value } = properties[i]
@@ -385,7 +385,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
     }
   }
   multilines && deindent()
-  push(`}`)
+  push(multilines ? `}` : ` }`)
 }
 
 function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
index 6f6959b6b592282e95fbf89f6bbdd103d3675fb2..431feab4500e7759593ed8779b42914e5195c99a 100644 (file)
@@ -1,8 +1,8 @@
-import { Position } from './ast'
+import { SourceLocation } from './ast'
 
 export interface CompilerError extends SyntaxError {
   code: ErrorCodes
-  loc: Position
+  loc: SourceLocation
 }
 
 export function defaultOnError(error: CompilerError) {
@@ -11,12 +11,12 @@ export function defaultOnError(error: CompilerError) {
 
 export function createCompilerError(
   code: ErrorCodes,
-  loc: Position
+  loc: SourceLocation
 ): CompilerError {
   const error = new SyntaxError(
-    `${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${loc.line}:${
-      loc.column
-    })`
+    `${__DEV__ || !__BROWSER__ ? errorMessages[code] : code} (${
+      loc.start.line
+    }:${loc.start.column})`
   ) as CompilerError
   error.code = code
   error.loc = loc
index fb91b33228eadf20d67950af76f76ed3e34adab4..2409c9200abdc6c0c5735aca8f4b91182b653e3c 100644 (file)
@@ -6,6 +6,8 @@ import { isString } from '@vue/shared'
 import { transformIf } from './transforms/vIf'
 import { transformFor } from './transforms/vFor'
 import { prepareElementForCodegen } from './transforms/element'
+import { transformOn } from './transforms/vOn'
+import { transformBind } from './transforms/vBind'
 
 export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
 
@@ -24,7 +26,8 @@ export function compile(
       ...(options.nodeTransforms || []) // user transforms
     ],
     directiveTransforms: {
-      // TODO include built-in directive transforms
+      on: transformOn,
+      bind: transformBind,
       ...(options.directiveTransforms || {}) // user transforms
     }
   })
index 73aa22f238ade247ddceeec53d341ec77a5ff744..e6f95be7d45226edf6d23c61f648fb48710eb717 100644 (file)
@@ -842,7 +842,13 @@ function emitError(
     loc.offset += offset
     loc.column += offset
   }
-  context.options.onError(createCompilerError(code, loc))
+  context.options.onError(
+    createCompilerError(code, {
+      start: loc,
+      end: loc,
+      source: ''
+    })
+  )
 }
 
 function isEnd(
index 066874c3e519077cc9bdc6f7033cb6ec9259ad76..421942529b3a476a234981b966e630fabd855b36 100644 (file)
@@ -123,10 +123,7 @@ function buildProps(
           mergeArgs.push(prop.exp)
         } else {
           context.onError(
-            createCompilerError(
-              ErrorCodes.X_V_BIND_NO_EXPRESSION,
-              prop.loc.start
-            )
+            createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc)
           )
         }
         continue
index 70b786d12ed055a08b57f5cf47f717bf6a266301..1b131a32fe24e7a69b9f2fef66eb101b495b8778 100644 (file)
@@ -1 +1,24 @@
-// TODO
+import { DirectiveTransform } from '../transform'
+import { createObjectProperty, createExpression } from '../ast'
+import { createCompilerError, ErrorCodes } from '../errors'
+
+// v-bind without arg is handled directly in ./element.ts due to it affecting
+// codegen for the entire props object. This transform here is only for v-bind
+// *with* args.
+export const transformBind: DirectiveTransform = (dir, context) => {
+  if (!dir.exp) {
+    context.onError(
+      createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, dir.loc)
+    )
+  }
+  // TODO handle .prop modifier
+  // TODO handle .sync modifier
+  return {
+    props: createObjectProperty(
+      dir.arg!,
+      dir.exp || createExpression('', true, dir.loc),
+      dir.loc
+    ),
+    needRuntime: false
+  }
+}
index 8e25ab09227da67176850649fc22681824cd9f39..54dd3de0c773ed9cd366335e0f71c2bc14995ed7 100644 (file)
@@ -32,15 +32,12 @@ export const transformFor = createStructuralDirectiveTransform(
         })
       } else {
         context.onError(
-          createCompilerError(
-            ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
-            dir.loc.start
-          )
+          createCompilerError(ErrorCodes.X_FOR_MALFORMED_EXPRESSION, dir.loc)
         )
       }
     } else {
       context.onError(
-        createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
+        createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc)
       )
     }
   }
index 85f2165d6def61a9ab96a9d43b1e224d6a3036b7..69b5db1e863964ad1697433cf3fb705c1c660369 100644 (file)
@@ -49,7 +49,7 @@ export const transformIf = createStructuralDirectiveTransform(
               dir.name === 'else'
                 ? ErrorCodes.X_ELSE_NO_ADJACENT_IF
                 : ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
-              node.loc.start
+              node.loc
             )
           )
         }
index 70b786d12ed055a08b57f5cf47f717bf6a266301..b7eae459ad22bee11ed357f317738633a95f038b 100644 (file)
@@ -1 +1,24 @@
-// TODO
+import { DirectiveTransform } from '../transform'
+import { createObjectProperty, createExpression } from '../ast'
+import { capitalize } from '@vue/shared'
+
+// v-on without arg is handled directly in ./element.ts due to it affecting
+// codegen for the entire props object. This transform here is only for v-on
+// *with* args.
+export const transformOn: DirectiveTransform = (dir, context) => {
+  const arg = dir.arg!
+  const eventName = arg.isStatic
+    ? createExpression(`on${capitalize(arg.content)}`, true, arg.loc)
+    : // TODO inject capitalize helper
+      createExpression(`'on' + capitalize(${arg.content})`, false, arg.loc)
+  // TODO .once modifier handling since it is platform agnostic
+  // other modifiers are handled in compiler-dom
+  return {
+    props: createObjectProperty(
+      eventName,
+      dir.exp || createExpression(`() => {}`, false, dir.loc),
+      dir.loc
+    ),
+    needRuntime: false
+  }
+}