]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler): separate Interpolation, SimpleExpression & CompoundExpression...
authorEvan You <yyx990803@gmail.com>
Fri, 27 Sep 2019 15:42:02 +0000 (11:42 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 27 Sep 2019 15:42:02 +0000 (11:42 -0400)
25 files changed:
packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/parse.spec.ts
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts
packages/compiler-core/__tests__/transforms/transformStyle.spec.ts
packages/compiler-core/__tests__/transforms/vBind.spec.ts
packages/compiler-core/__tests__/transforms/vFor.spec.ts
packages/compiler-core/__tests__/transforms/vIf.spec.ts
packages/compiler-core/__tests__/transforms/vOn.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/runtimeConstants.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-core/src/transforms/transformStyle.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
packages/compiler-dom/__tests__/parse.spec.ts
packages/runtime-core/src/index.ts

index 947bfb04bf3d312e45ae5686ef8c8cddb193e896..5b7edf2b33bf3bffd727eaa766e13d4506a1b796 100644 (file)
@@ -232,7 +232,7 @@ Object {
             },
           },
           "name": "attr",
-          "type": 5,
+          "type": 6,
           "value": Object {
             "content": "c",
             "isEmpty": false,
@@ -315,7 +315,7 @@ Object {
             },
           },
           "name": "attr",
-          "type": 5,
+          "type": 6,
           "value": Object {
             "content": "&#a;",
             "isEmpty": false,
@@ -398,7 +398,7 @@ Object {
             },
           },
           "name": "attr",
-          "type": 5,
+          "type": 6,
           "value": Object {
             "content": "ΓΏ",
             "isEmpty": false,
@@ -481,7 +481,7 @@ Object {
             },
           },
           "name": "attr",
-          "type": 5,
+          "type": 6,
           "value": Object {
             "content": "&#xg;",
             "isEmpty": false,
@@ -1183,7 +1183,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "",
                 "isEmpty": true,
@@ -1218,7 +1218,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "",
                 "isEmpty": true,
@@ -2359,7 +2359,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -2449,7 +2449,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -2539,7 +2539,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -2629,7 +2629,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -2736,7 +2736,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -2843,7 +2843,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -2950,7 +2950,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -3057,7 +3057,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -3164,7 +3164,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -3271,7 +3271,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -3378,7 +3378,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "abc",
                 "isEmpty": false,
@@ -3884,23 +3884,38 @@ Object {
     Object {
       "children": Array [
         Object {
-          "content": "a < b",
-          "isInterpolation": true,
-          "isStatic": false,
+          "content": Object {
+            "content": "a < b",
+            "isStatic": false,
+            "loc": Object {
+              "end": Object {
+                "column": 18,
+                "line": 1,
+                "offset": 17,
+              },
+              "source": "a < b",
+              "start": Object {
+                "column": 13,
+                "line": 1,
+                "offset": 12,
+              },
+            },
+            "type": 4,
+          },
           "loc": Object {
             "end": Object {
-              "column": 18,
+              "column": 20,
               "line": 1,
-              "offset": 17,
+              "offset": 19,
             },
-            "source": "a < b",
+            "source": "{{a < b}}",
             "start": Object {
-              "column": 13,
+              "column": 11,
               "line": 1,
-              "offset": 12,
+              "offset": 10,
             },
           },
-          "type": 4,
+          "type": 5,
         },
       ],
       "codegenNode": undefined,
@@ -4133,7 +4148,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "/",
                 "isEmpty": false,
@@ -4240,7 +4255,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -4330,7 +4345,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -4666,7 +4681,7 @@ class=\\"bar\\"></div>",
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "foo",
                 "isEmpty": false,
@@ -4701,7 +4716,7 @@ class=\\"bar\\"></div>",
                 },
               },
               "name": "class",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar",
                 "isEmpty": false,
@@ -4810,7 +4825,7 @@ Object {
                 },
               },
               "name": "id",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "foo",
                 "isEmpty": false,
@@ -4845,7 +4860,7 @@ Object {
                 },
               },
               "name": "class",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar",
                 "isEmpty": false,
@@ -5476,7 +5491,7 @@ Object {
                 },
               },
               "name": "a\\"bc",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "",
                 "isEmpty": true,
@@ -5583,7 +5598,7 @@ Object {
                 },
               },
               "name": "a'bc",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "",
                 "isEmpty": true,
@@ -5690,7 +5705,7 @@ Object {
                 },
               },
               "name": "a<bc",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "",
                 "isEmpty": true,
@@ -5797,7 +5812,7 @@ Object {
                 },
               },
               "name": "foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar\\"",
                 "isEmpty": false,
@@ -5904,7 +5919,7 @@ Object {
                 },
               },
               "name": "foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar'",
                 "isEmpty": false,
@@ -6011,7 +6026,7 @@ Object {
                 },
               },
               "name": "foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar<div",
                 "isEmpty": false,
@@ -6118,7 +6133,7 @@ Object {
                 },
               },
               "name": "foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar=baz",
                 "isEmpty": false,
@@ -6225,7 +6240,7 @@ Object {
                 },
               },
               "name": "foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar\`",
                 "isEmpty": false,
@@ -6332,7 +6347,7 @@ Object {
                 },
               },
               "name": "=",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -6422,7 +6437,7 @@ Object {
                 },
               },
               "name": "=foo",
-              "type": 5,
+              "type": 6,
               "value": Object {
                 "content": "bar",
                 "isEmpty": false,
@@ -6594,7 +6609,7 @@ Object {
                 },
               },
               "name": "a",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
             Object {
@@ -6612,7 +6627,7 @@ Object {
                 },
               },
               "name": "b",
-              "type": 5,
+              "type": 6,
               "value": undefined,
             },
           ],
@@ -6960,23 +6975,38 @@ Object {
     Object {
       "children": Array [
         Object {
-          "content": "'</div>'",
-          "isInterpolation": true,
-          "isStatic": false,
+          "content": Object {
+            "content": "'</div>'",
+            "isStatic": false,
+            "loc": Object {
+              "end": Object {
+                "column": 21,
+                "line": 1,
+                "offset": 20,
+              },
+              "source": "'</div>'",
+              "start": Object {
+                "column": 13,
+                "line": 1,
+                "offset": 12,
+              },
+            },
+            "type": 4,
+          },
           "loc": Object {
             "end": Object {
-              "column": 21,
+              "column": 23,
               "line": 1,
-              "offset": 20,
+              "offset": 22,
             },
-            "source": "'</div>'",
+            "source": "{{'</div>'}}",
             "start": Object {
-              "column": 13,
+              "column": 11,
               "line": 1,
-              "offset": 12,
+              "offset": 10,
             },
           },
-          "type": 4,
+          "type": 5,
         },
       ],
       "codegenNode": undefined,
@@ -7317,23 +7347,38 @@ exports[`compiler: parse Errors X_MISSING_INTERPOLATION_END {{}} 1`] = `
 Object {
   "children": Array [
     Object {
-      "content": "",
-      "isInterpolation": true,
-      "isStatic": true,
+      "content": Object {
+        "content": "",
+        "isStatic": false,
+        "loc": Object {
+          "end": Object {
+            "column": 3,
+            "line": 1,
+            "offset": 2,
+          },
+          "source": "",
+          "start": Object {
+            "column": 3,
+            "line": 1,
+            "offset": 2,
+          },
+        },
+        "type": 4,
+      },
       "loc": Object {
         "end": Object {
-          "column": 3,
+          "column": 5,
           "line": 1,
-          "offset": 2,
+          "offset": 4,
         },
-        "source": "",
+        "source": "{{}}",
         "start": Object {
-          "column": 3,
+          "column": 1,
           "line": 1,
-          "offset": 2,
+          "offset": 0,
         },
       },
-      "type": 4,
+      "type": 5,
     },
   ],
   "hoists": Array [],
@@ -7458,7 +7503,6 @@ Object {
         Object {
           "arg": Object {
             "content": "class",
-            "isInterpolation": false,
             "isStatic": true,
             "loc": Object {
               "end": Object {
@@ -7477,7 +7521,6 @@ Object {
           },
           "exp": Object {
             "content": "{ some: condition }",
-            "isInterpolation": false,
             "isStatic": false,
             "loc": Object {
               "end": Object {
@@ -7509,7 +7552,7 @@ Object {
           },
           "modifiers": Array [],
           "name": "bind",
-          "type": 6,
+          "type": 7,
         },
       ],
       "tag": "div",
@@ -7538,7 +7581,6 @@ Object {
         Object {
           "arg": Object {
             "content": "style",
-            "isInterpolation": false,
             "isStatic": true,
             "loc": Object {
               "end": Object {
@@ -7557,7 +7599,6 @@ Object {
           },
           "exp": Object {
             "content": "{ color: 'red' }",
-            "isInterpolation": false,
             "isStatic": false,
             "loc": Object {
               "end": Object {
@@ -7589,7 +7630,7 @@ Object {
           },
           "modifiers": Array [],
           "name": "bind",
-          "type": 6,
+          "type": 7,
         },
       ],
       "tag": "p",
@@ -7645,7 +7686,6 @@ Object {
             Object {
               "arg": Object {
                 "content": "style",
-                "isInterpolation": false,
                 "isStatic": true,
                 "loc": Object {
                   "end": Object {
@@ -7664,7 +7704,6 @@ Object {
               },
               "exp": Object {
                 "content": "{ color: 'red' }",
-                "isInterpolation": false,
                 "isStatic": false,
                 "loc": Object {
                   "end": Object {
@@ -7696,7 +7735,7 @@ Object {
               },
               "modifiers": Array [],
               "name": "bind",
-              "type": 6,
+              "type": 7,
             },
           ],
           "tag": "p",
@@ -7744,7 +7783,6 @@ Object {
         Object {
           "arg": Object {
             "content": "class",
-            "isInterpolation": false,
             "isStatic": true,
             "loc": Object {
               "end": Object {
@@ -7763,7 +7801,6 @@ Object {
           },
           "exp": Object {
             "content": "{ some: condition }",
-            "isInterpolation": false,
             "isStatic": false,
             "loc": Object {
               "end": Object {
@@ -7795,7 +7832,7 @@ Object {
           },
           "modifiers": Array [],
           "name": "bind",
-          "type": 6,
+          "type": 7,
         },
       ],
       "tag": "div",
index edc579f643577b67b8bf366ab69c403b23abbeba..c4be89a1f0ae8c23e02d20709015a9c5705b8256 100644 (file)
@@ -3,14 +3,16 @@ import {
   NodeTypes,
   RootNode,
   SourceLocation,
-  createExpression,
+  createSimpleExpression,
   Namespaces,
   ElementTypes,
   CallExpression,
   createObjectExpression,
   createObjectProperty,
   createArrayExpression,
-  ElementNode
+  ElementNode,
+  createCompoundExpression,
+  createInterpolation
 } from '../src'
 import {
   CREATE_VNODE,
@@ -93,12 +95,12 @@ describe('compiler: codegen', () => {
   test('hoists', () => {
     const root = createRoot({
       hoists: [
-        createExpression(`hello`, false, mockLoc),
+        createSimpleExpression(`hello`, false, mockLoc),
         createObjectExpression(
           [
             createObjectProperty(
-              createExpression(`id`, true, mockLoc),
-              createExpression(`foo`, true, mockLoc),
+              createSimpleExpression(`id`, true, mockLoc),
+              createSimpleExpression(`foo`, true, mockLoc),
               mockLoc
             )
           ],
@@ -138,7 +140,7 @@ describe('compiler: codegen', () => {
   test('interpolation', () => {
     const { code } = generate(
       createRoot({
-        children: [createExpression(`hello`, false, mockLoc, true)]
+        children: [createInterpolation(`hello`, mockLoc)]
       })
     )
     expect(code).toMatch(`return _${TO_STRING}(hello)`)
@@ -171,7 +173,7 @@ describe('compiler: codegen', () => {
             isEmpty: false,
             loc: mockLoc
           },
-          createExpression(`hello`, false, mockLoc, true),
+          createInterpolation(`hello`, mockLoc),
           {
             type: NodeTypes.COMMENT,
             content: 'foo',
@@ -199,7 +201,7 @@ describe('compiler: codegen', () => {
             isEmpty: false,
             loc: mockLoc
           },
-          createExpression(`hello`, false, mockLoc, true),
+          createInterpolation(`hello`, mockLoc),
           {
             type: NodeTypes.COMMENT,
             content: 'foo',
@@ -224,14 +226,13 @@ describe('compiler: codegen', () => {
     const { code } = generate(
       createRoot({
         children: [
-          {
-            type: NodeTypes.EXPRESSION,
-            content: 'foo',
-            isStatic: false,
-            isInterpolation: true,
-            loc: mockLoc,
-            children: [`_ctx.`, createExpression(`foo`, false, mockLoc)]
-          }
+          createInterpolation(
+            createCompoundExpression(
+              [`_ctx.`, createSimpleExpression(`foo`, false, mockLoc)],
+              mockLoc
+            ),
+            mockLoc
+          )
         ]
       })
     )
@@ -249,7 +250,7 @@ describe('compiler: codegen', () => {
             branches: [
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('foo', false, mockLoc),
+                condition: createSimpleExpression('foo', false, mockLoc),
                 loc: mockLoc,
                 children: [
                   {
@@ -262,9 +263,9 @@ describe('compiler: codegen', () => {
               },
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('a + b', false, mockLoc),
+                condition: createSimpleExpression('a + b', false, mockLoc),
                 loc: mockLoc,
-                children: [createExpression(`bye`, false, mockLoc, true)]
+                children: [createInterpolation(`bye`, mockLoc)]
               },
               {
                 type: NodeTypes.IF_BRANCH,
@@ -302,7 +303,7 @@ describe('compiler: codegen', () => {
             branches: [
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('foo', false, mockLoc),
+                condition: createSimpleExpression('foo', false, mockLoc),
                 loc: mockLoc,
                 children: [
                   {
@@ -315,9 +316,9 @@ describe('compiler: codegen', () => {
               },
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('a + b', false, mockLoc),
+                condition: createSimpleExpression('a + b', false, mockLoc),
                 loc: mockLoc,
-                children: [createExpression(`bye`, false, mockLoc, true)]
+                children: [createInterpolation(`bye`, mockLoc)]
               }
             ]
           }
@@ -340,11 +341,11 @@ describe('compiler: codegen', () => {
           {
             type: NodeTypes.FOR,
             loc: mockLoc,
-            source: createExpression(`list`, false, mockLoc),
-            valueAlias: createExpression(`v`, false, mockLoc),
-            keyAlias: createExpression(`k`, false, mockLoc),
-            objectIndexAlias: createExpression(`i`, false, mockLoc),
-            children: [createExpression(`v`, false, mockLoc, true)]
+            source: createSimpleExpression(`list`, false, mockLoc),
+            valueAlias: createSimpleExpression(`v`, false, mockLoc),
+            keyAlias: createSimpleExpression(`k`, false, mockLoc),
+            objectIndexAlias: createSimpleExpression(`i`, false, mockLoc),
+            children: [createInterpolation(`v`, mockLoc)]
           }
         ]
       })
@@ -364,11 +365,11 @@ describe('compiler: codegen', () => {
           {
             type: NodeTypes.FOR,
             loc: mockLoc,
-            source: createExpression(`list`, false, mockLoc),
-            valueAlias: createExpression(`v`, false, mockLoc),
-            keyAlias: createExpression(`k`, false, mockLoc),
-            objectIndexAlias: createExpression(`i`, false, mockLoc),
-            children: [createExpression(`v`, false, mockLoc, true)]
+            source: createSimpleExpression(`list`, false, mockLoc),
+            valueAlias: createSimpleExpression(`v`, false, mockLoc),
+            keyAlias: createSimpleExpression(`k`, false, mockLoc),
+            objectIndexAlias: createSimpleExpression(`i`, false, mockLoc),
+            children: [createInterpolation(`v`, mockLoc)]
           }
         ]
       }),
@@ -391,11 +392,11 @@ describe('compiler: codegen', () => {
           {
             type: NodeTypes.FOR,
             loc: mockLoc,
-            source: createExpression(`list`, false, mockLoc),
+            source: createSimpleExpression(`list`, false, mockLoc),
             valueAlias: undefined,
-            keyAlias: createExpression(`k`, false, mockLoc),
-            objectIndexAlias: createExpression(`i`, false, mockLoc),
-            children: [createExpression(`v`, false, mockLoc, true)]
+            keyAlias: createSimpleExpression(`k`, false, mockLoc),
+            objectIndexAlias: createSimpleExpression(`i`, false, mockLoc),
+            children: [createInterpolation(`v`, mockLoc)]
           }
         ]
       })
@@ -415,11 +416,11 @@ describe('compiler: codegen', () => {
           {
             type: NodeTypes.FOR,
             loc: mockLoc,
-            source: createExpression(`list`, false, mockLoc),
-            valueAlias: createExpression(`v`, false, mockLoc),
+            source: createSimpleExpression(`list`, false, mockLoc),
+            valueAlias: createSimpleExpression(`v`, false, mockLoc),
             keyAlias: undefined,
-            objectIndexAlias: createExpression(`i`, false, mockLoc),
-            children: [createExpression(`v`, false, mockLoc, true)]
+            objectIndexAlias: createSimpleExpression(`i`, false, mockLoc),
+            children: [createInterpolation(`v`, mockLoc)]
           }
         ]
       })
@@ -439,11 +440,11 @@ describe('compiler: codegen', () => {
           {
             type: NodeTypes.FOR,
             loc: mockLoc,
-            source: createExpression(`list`, false, mockLoc),
+            source: createSimpleExpression(`list`, false, mockLoc),
             valueAlias: undefined,
             keyAlias: undefined,
-            objectIndexAlias: createExpression(`i`, false, mockLoc),
-            children: [createExpression(`v`, false, mockLoc, true)]
+            objectIndexAlias: createSimpleExpression(`i`, false, mockLoc),
+            children: [createInterpolation(`v`, mockLoc)]
           }
         ]
       })
@@ -488,29 +489,26 @@ describe('compiler: codegen', () => {
             createObjectExpression(
               [
                 createObjectProperty(
-                  createExpression(`id`, true, mockLoc),
-                  createExpression(`foo`, true, mockLoc),
+                  createSimpleExpression(`id`, true, mockLoc),
+                  createSimpleExpression(`foo`, true, mockLoc),
                   mockLoc
                 ),
                 createObjectProperty(
-                  createExpression(`prop`, false, mockLoc),
-                  createExpression(`bar`, false, mockLoc),
+                  createSimpleExpression(`prop`, false, mockLoc),
+                  createSimpleExpression(`bar`, false, mockLoc),
                   mockLoc
                 ),
                 // compound expression as computed key
                 createObjectProperty(
                   {
-                    type: NodeTypes.EXPRESSION,
-                    content: ``,
+                    type: NodeTypes.COMPOUND_EXPRESSION,
                     loc: mockLoc,
-                    isStatic: false,
-                    isInterpolation: false,
                     children: [
                       `foo + `,
-                      createExpression(`bar`, false, mockLoc)
+                      createSimpleExpression(`bar`, false, mockLoc)
                     ]
                   },
-                  createExpression(`bar`, false, mockLoc),
+                  createSimpleExpression(`bar`, false, mockLoc),
                   mockLoc
                 )
               ],
@@ -524,8 +522,8 @@ describe('compiler: codegen', () => {
                   [
                     createObjectProperty(
                       // should quote the key!
-                      createExpression(`some-key`, true, mockLoc),
-                      createExpression(`foo`, true, mockLoc),
+                      createSimpleExpression(`some-key`, true, mockLoc),
+                      createSimpleExpression(`foo`, true, mockLoc),
                       mockLoc
                     )
                   ],
index cd99f3b95ed21cf3541656bed27595e4849bc276..08cc2260a5a8225f6ebb5e93b4832ed57f72b801 100644 (file)
@@ -4,12 +4,12 @@ import {
   CommentNode,
   ElementNode,
   ElementTypes,
-  ExpressionNode,
   Namespaces,
   NodeTypes,
   Position,
   TextNode,
-  AttributeNode
+  AttributeNode,
+  InterpolationNode
 } from '../src/ast'
 
 describe('compiler: parse', () => {
@@ -290,63 +290,92 @@ describe('compiler: parse', () => {
   describe('Interpolation', () => {
     test('simple interpolation', () => {
       const ast = parse('{{message}}')
-      const interpolation = ast.children[0] as ExpressionNode
+      const interpolation = ast.children[0] as InterpolationNode
 
       expect(interpolation).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: 'message',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: `message`,
+          isStatic: false,
+          loc: {
+            start: { offset: 2, line: 1, column: 3 },
+            end: { offset: 9, line: 1, column: 10 },
+            source: `message`
+          }
+        },
         loc: {
-          start: { offset: 2, line: 1, column: 3 },
-          end: { offset: 9, line: 1, column: 10 },
-          source: 'message'
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 11, line: 1, column: 12 },
+          source: '{{message}}'
         }
       })
     })
 
     test('it can have tag-like notation', () => {
       const ast = parse('{{ a<b }}')
-      const interpolation = ast.children[0] as ExpressionNode
+      const interpolation = ast.children[0] as InterpolationNode
 
       expect(interpolation).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: 'a<b',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: `a<b`,
+          isStatic: false,
+          loc: {
+            start: { offset: 3, line: 1, column: 4 },
+            end: { offset: 6, line: 1, column: 7 },
+            source: 'a<b'
+          }
+        },
         loc: {
-          start: { offset: 3, line: 1, column: 4 },
-          end: { offset: 6, line: 1, column: 7 },
-          source: 'a<b'
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: '{{ a<b }}'
         }
       })
     })
 
     test('it can have tag-like notation (2)', () => {
       const ast = parse('{{ a<b }}{{ c>d }}')
-      const interpolation1 = ast.children[0] as ExpressionNode
-      const interpolation2 = ast.children[1] as ExpressionNode
+      const interpolation1 = ast.children[0] as InterpolationNode
+      const interpolation2 = ast.children[1] as InterpolationNode
 
       expect(interpolation1).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: 'a<b',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: `a<b`,
+          isStatic: false,
+          loc: {
+            start: { offset: 3, line: 1, column: 4 },
+            end: { offset: 6, line: 1, column: 7 },
+            source: 'a<b'
+          }
+        },
         loc: {
-          start: { offset: 3, line: 1, column: 4 },
-          end: { offset: 6, line: 1, column: 7 },
-          source: 'a<b'
+          start: { offset: 0, line: 1, column: 1 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: '{{ a<b }}'
         }
       })
+
       expect(interpolation2).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: 'c>d',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          isStatic: false,
+          content: 'c>d',
+          loc: {
+            start: { offset: 12, line: 1, column: 13 },
+            end: { offset: 15, line: 1, column: 16 },
+            source: 'c>d'
+          }
+        },
         loc: {
-          start: { offset: 12, line: 1, column: 13 },
-          end: { offset: 15, line: 1, column: 16 },
-          source: 'c>d'
+          start: { offset: 9, line: 1, column: 10 },
+          end: { offset: 18, line: 1, column: 19 },
+          source: '{{ c>d }}'
         }
       })
     })
@@ -354,17 +383,24 @@ describe('compiler: parse', () => {
     test('it can have tag-like notation (3)', () => {
       const ast = parse('<div>{{ "</div>" }}</div>')
       const element = ast.children[0] as ElementNode
-      const interpolation = element.children[0] as ExpressionNode
+      const interpolation = element.children[0] as InterpolationNode
 
       expect(interpolation).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: '"</div>"',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          isStatic: false,
+          content: '"</div>"',
+          loc: {
+            start: { offset: 8, line: 1, column: 9 },
+            end: { offset: 16, line: 1, column: 17 },
+            source: '"</div>"'
+          }
+        },
         loc: {
-          start: { offset: 8, line: 1, column: 9 },
-          end: { offset: 16, line: 1, column: 17 },
-          source: '"</div>"'
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: '{{ "</div>" }}'
         }
       })
     })
@@ -889,10 +925,9 @@ describe('compiler: parse', () => {
         arg: undefined,
         modifiers: [],
         exp: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'a',
           isStatic: false,
-          isInterpolation: false,
           loc: {
             start: { offset: 11, line: 1, column: 12 },
             end: { offset: 12, line: 1, column: 13 },
@@ -915,10 +950,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'on',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'click',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'click',
             start: {
@@ -987,10 +1022,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'on',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'click',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'click',
             start: {
@@ -1023,10 +1058,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'bind',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'a',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'a',
             start: {
@@ -1043,10 +1078,10 @@ describe('compiler: parse', () => {
         },
         modifiers: [],
         exp: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'b',
           isStatic: false,
-          isInterpolation: false,
+
           loc: {
             start: { offset: 8, line: 1, column: 9 },
             end: { offset: 9, line: 1, column: 10 },
@@ -1069,10 +1104,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'bind',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'a',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'a',
             start: {
@@ -1089,10 +1124,10 @@ describe('compiler: parse', () => {
         },
         modifiers: ['sync'],
         exp: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'b',
           isStatic: false,
-          isInterpolation: false,
+
           loc: {
             start: { offset: 13, line: 1, column: 14 },
             end: { offset: 14, line: 1, column: 15 },
@@ -1115,10 +1150,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'on',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'a',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'a',
             start: {
@@ -1135,10 +1170,10 @@ describe('compiler: parse', () => {
         },
         modifiers: [],
         exp: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'b',
           isStatic: false,
-          isInterpolation: false,
+
           loc: {
             start: { offset: 8, line: 1, column: 9 },
             end: { offset: 9, line: 1, column: 10 },
@@ -1161,10 +1196,10 @@ describe('compiler: parse', () => {
         type: NodeTypes.DIRECTIVE,
         name: 'on',
         arg: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'a',
           isStatic: true,
-          isInterpolation: false,
+
           loc: {
             source: 'a',
             start: {
@@ -1181,10 +1216,10 @@ describe('compiler: parse', () => {
         },
         modifiers: ['enter'],
         exp: {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: 'b',
           isStatic: false,
-          isInterpolation: false,
+
           loc: {
             start: { offset: 14, line: 1, column: 15 },
             end: { offset: 15, line: 1, column: 16 },
@@ -1313,20 +1348,27 @@ foo
     offset += foo.loc.source.length
     expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
 
+    expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
+    const barInner = (bar as InterpolationNode).content
     offset += 3
-    expect(bar.loc.start).toEqual({ line: 2, column: 8, offset })
-    offset += bar.loc.source.length
-    expect(bar.loc.end).toEqual({ line: 2, column: 11, offset })
+    expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
+    offset += barInner.loc.source.length
+    expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
     offset += 3
+    expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
 
     expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
     offset += but.loc.source.length
     expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
 
+    expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
+    const bazInner = (baz as InterpolationNode).content
+    offset += 3
+    expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
+    offset += bazInner.loc.source.length
+    expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
     offset += 3
-    expect(baz.loc.start).toEqual({ line: 2, column: 22, offset })
-    offset += baz.loc.source.length
-    expect(baz.loc.end).toEqual({ line: 2, column: 25, offset })
+    expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
   })
 
   describe('namedCharacterReferences option', () => {
index 138676b929f9b243c534ef12b67c8de119960e20..f35092e5150425954f7b5d9c00aab3a67203de4d 100644 (file)
@@ -52,12 +52,12 @@ function createStaticObjectMatcher(obj: any) {
     properties: Object.keys(obj).map(key => ({
       type: NodeTypes.JS_PROPERTY,
       key: {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: key,
         isStatic: true
       },
       value: {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: obj[key],
         isStatic: true
       }
@@ -87,7 +87,7 @@ describe('compiler: element transform', () => {
     expect(node.arguments).toMatchObject([
       `"div"`,
       {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `_hoisted_1`
       }
     ])
@@ -106,7 +106,7 @@ describe('compiler: element transform', () => {
     expect(node.arguments).toMatchObject([
       `"div"`,
       {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `_hoisted_1`
       },
       [
@@ -148,7 +148,7 @@ describe('compiler: element transform', () => {
     expect(node.callee).toBe(`_${CREATE_VNODE}`)
     // should directly use `obj` in props position
     expect(node.arguments[1]).toMatchObject({
-      type: NodeTypes.EXPRESSION,
+      type: NodeTypes.SIMPLE_EXPRESSION,
       content: `obj`
     })
   })
@@ -167,7 +167,7 @@ describe('compiler: element transform', () => {
           id: 'foo'
         }),
         {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: `obj`
         }
       ]
@@ -185,7 +185,7 @@ describe('compiler: element transform', () => {
       callee: `_${MERGE_PROPS}`,
       arguments: [
         {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: `obj`
         },
         createStaticObjectMatcher({
@@ -209,7 +209,7 @@ describe('compiler: element transform', () => {
           id: 'foo'
         }),
         {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: `obj`
         },
         createStaticObjectMatcher({
@@ -237,7 +237,7 @@ describe('compiler: element transform', () => {
           callee: `_${TO_HANDLERS}`,
           arguments: [
             {
-              type: NodeTypes.EXPRESSION,
+              type: NodeTypes.SIMPLE_EXPRESSION,
               content: `obj`
             }
           ]
@@ -267,13 +267,13 @@ describe('compiler: element transform', () => {
           callee: `_${TO_HANDLERS}`,
           arguments: [
             {
-              type: NodeTypes.EXPRESSION,
+              type: NodeTypes.SIMPLE_EXPRESSION,
               content: `handlers`
             }
           ]
         },
         {
-          type: NodeTypes.EXPRESSION,
+          type: NodeTypes.SIMPLE_EXPRESSION,
           content: `obj`
         }
       ]
@@ -363,14 +363,13 @@ describe('compiler: element transform', () => {
               `_directive_foo`,
               // exp
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `hello`,
-                isStatic: false,
-                isInterpolation: false
+                isStatic: false
               },
               // arg
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `bar`,
                 isStatic: true
               }
@@ -408,7 +407,7 @@ describe('compiler: element transform', () => {
               `_directive_bar`,
               // exp
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `x`
               }
             ]
@@ -419,14 +418,13 @@ describe('compiler: element transform', () => {
               `_directive_baz`,
               // exp
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `y`,
-                isStatic: false,
-                isInterpolation: false
+                isStatic: false
               },
               // arg
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `arg`,
                 isStatic: false
               },
@@ -437,12 +435,12 @@ describe('compiler: element transform', () => {
                   {
                     type: NodeTypes.JS_PROPERTY,
                     key: {
-                      type: NodeTypes.EXPRESSION,
+                      type: NodeTypes.SIMPLE_EXPRESSION,
                       content: `mod`,
                       isStatic: true
                     },
                     value: {
-                      type: NodeTypes.EXPRESSION,
+                      type: NodeTypes.SIMPLE_EXPRESSION,
                       content: `true`,
                       isStatic: false
                     }
@@ -450,12 +448,12 @@ describe('compiler: element transform', () => {
                   {
                     type: NodeTypes.JS_PROPERTY,
                     key: {
-                      type: NodeTypes.EXPRESSION,
+                      type: NodeTypes.SIMPLE_EXPRESSION,
                       content: `mad`,
                       isStatic: true
                     },
                     value: {
-                      type: NodeTypes.EXPRESSION,
+                      type: NodeTypes.SIMPLE_EXPRESSION,
                       content: `true`,
                       isStatic: false
                     }
@@ -484,7 +482,7 @@ describe('compiler: element transform', () => {
         {
           type: NodeTypes.JS_PROPERTY,
           key: {
-            type: NodeTypes.EXPRESSION,
+            type: NodeTypes.SIMPLE_EXPRESSION,
             content: `onClick`,
             isStatic: true
           },
@@ -492,12 +490,12 @@ describe('compiler: element transform', () => {
             type: NodeTypes.JS_ARRAY_EXPRESSION,
             elements: [
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `a`,
                 isStatic: false
               },
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `b`,
                 isStatic: false
               }
@@ -524,7 +522,7 @@ describe('compiler: element transform', () => {
         {
           type: NodeTypes.JS_PROPERTY,
           key: {
-            type: NodeTypes.EXPRESSION,
+            type: NodeTypes.SIMPLE_EXPRESSION,
             content: `style`,
             isStatic: true
           },
@@ -532,12 +530,12 @@ describe('compiler: element transform', () => {
             type: NodeTypes.JS_ARRAY_EXPRESSION,
             elements: [
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `_hoisted_1`,
                 isStatic: false
               },
               {
-                type: NodeTypes.EXPRESSION,
+                type: NodeTypes.SIMPLE_EXPRESSION,
                 content: `{ color: 'red' }`,
                 isStatic: false
               }
index 6129cfdd413e98991101cb369184fef047cee98d..2c9809a2b02ddd3f7cddd5d28e963b465d331977 100644 (file)
@@ -1,13 +1,13 @@
 import {
   parse,
   transform,
-  ExpressionNode,
   ElementNode,
   DirectiveNode,
   NodeTypes,
   ForNode,
   CompilerOptions,
-  IfNode
+  IfNode,
+  InterpolationNode
 } from '../../src'
 import { transformIf } from '../../src/transforms/vIf'
 import { transformFor } from '../../src/transforms/vFor'
@@ -28,28 +28,58 @@ function parseWithExpressionTransform(
 
 describe('compiler: expression transform', () => {
   test('interpolation (root)', () => {
-    const node = parseWithExpressionTransform(`{{ foo }}`) as ExpressionNode
-    expect(node.children).toBeUndefined()
-    expect(node.content).toBe(`_ctx.foo`)
+    const node = parseWithExpressionTransform(`{{ foo }}`) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.foo`
+    })
   })
 
   test('interpolation (children)', () => {
     const el = parseWithExpressionTransform(
       `<div>{{ foo }}</div>`
     ) as ElementNode
-    const node = el.children[0] as ExpressionNode
-    expect(node.children).toBeUndefined()
-    expect(node.content).toBe(`_ctx.foo`)
+    const node = el.children[0] as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.foo`
+    })
+  })
+
+  test('interpolation (complex)', () => {
+    const el = parseWithExpressionTransform(
+      `<div>{{ foo + bar(baz.qux) }}</div>`
+    ) as ElementNode
+    const node = el.children[0] as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `_ctx.foo` },
+        ` + `,
+        { content: `_ctx.bar` },
+        `(`,
+        { content: `_ctx.baz` },
+        `.`,
+        { content: `qux` },
+        `)`
+      ]
+    })
   })
 
   test('directive value', () => {
     const node = parseWithExpressionTransform(
       `<div v-foo:arg="baz"/>`
     ) as ElementNode
-    expect((node.props[0] as DirectiveNode).arg!.children).toBeUndefined()
+    const arg = (node.props[0] as DirectiveNode).arg!
+    expect(arg).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `arg`
+    })
     const exp = (node.props[0] as DirectiveNode).exp!
-    expect(exp.children).toBeUndefined()
-    expect(exp.content).toBe(`_ctx.baz`)
+    expect(exp).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.baz`
+    })
   })
 
   test('dynamic directive arg', () => {
@@ -57,85 +87,113 @@ describe('compiler: expression transform', () => {
       `<div v-foo:[arg]="baz"/>`
     ) as ElementNode
     const arg = (node.props[0] as DirectiveNode).arg!
+    expect(arg).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.arg`
+    })
     const exp = (node.props[0] as DirectiveNode).exp!
-    expect(arg.children).toBeUndefined()
-    expect(arg.content).toBe(`_ctx.arg`)
-    expect(exp.children).toBeUndefined()
-    expect(exp.content).toBe(`_ctx.baz`)
+    expect(exp).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.baz`
+    })
   })
 
   test('should prefix complex expressions', () => {
     const node = parseWithExpressionTransform(
       `{{ foo(baz + 1, { key: kuz }) }}`
-    ) as ExpressionNode
+    ) as InterpolationNode
     // should parse into compound expression
-    expect(node.children).toMatchObject([
-      {
-        content: `_ctx.foo`,
-        loc: {
-          source: `foo`,
-          start: {
-            offset: 3,
-            line: 1,
-            column: 4
-          },
-          end: {
-            offset: 6,
-            line: 1,
-            column: 7
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        {
+          content: `_ctx.foo`,
+          loc: {
+            source: `foo`,
+            start: {
+              offset: 3,
+              line: 1,
+              column: 4
+            },
+            end: {
+              offset: 6,
+              line: 1,
+              column: 7
+            }
           }
-        }
-      },
-      `(`,
-      {
-        content: `_ctx.baz`,
-        loc: {
-          source: `baz`,
-          start: {
-            offset: 7,
-            line: 1,
-            column: 8
-          },
-          end: {
-            offset: 10,
-            line: 1,
-            column: 11
+        },
+        `(`,
+        {
+          content: `_ctx.baz`,
+          loc: {
+            source: `baz`,
+            start: {
+              offset: 7,
+              line: 1,
+              column: 8
+            },
+            end: {
+              offset: 10,
+              line: 1,
+              column: 11
+            }
           }
-        }
-      },
-      ` + 1, { key: `,
-      {
-        content: `_ctx.kuz`,
-        loc: {
-          source: `kuz`,
-          start: {
-            offset: 23,
-            line: 1,
-            column: 24
-          },
-          end: {
-            offset: 26,
-            line: 1,
-            column: 27
+        },
+        ` + 1, { key: `,
+        {
+          content: `_ctx.kuz`,
+          loc: {
+            source: `kuz`,
+            start: {
+              offset: 23,
+              line: 1,
+              column: 24
+            },
+            end: {
+              offset: 26,
+              line: 1,
+              column: 27
+            }
           }
-        }
-      },
-      ` })`
-    ])
+        },
+        ` })`
+      ]
+    })
   })
 
   test('should prefix v-if condition', () => {
     const node = parseWithExpressionTransform(`<div v-if="ok"/>`) as IfNode
-    expect(node.branches[0].condition!.children).toBeUndefined()
-    expect(node.branches[0].condition!.content).toBe(`_ctx.ok`)
+    expect(node.branches[0].condition).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.ok`
+    })
   })
 
   test('should prefix v-for source', () => {
     const node = parseWithExpressionTransform(
       `<div v-for="i in list"/>`
     ) as ForNode
-    expect(node.source.children).toBeUndefined()
-    expect(node.source.content).toBe(`_ctx.list`)
+    expect(node.source).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.list`
+    })
+  })
+
+  test('should prefix v-for source w/ complex expression', () => {
+    const node = parseWithExpressionTransform(
+      `<div v-for="i in list.concat([foo])"/>`
+    ) as ForNode
+    expect(node.source).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `_ctx.list` },
+        `.`,
+        { content: `concat` },
+        `([`,
+        { content: `_ctx.foo` },
+        `])`
+      ]
+    })
   })
 
   test('should not prefix v-for alias', () => {
@@ -143,16 +201,14 @@ describe('compiler: expression transform', () => {
       `<div v-for="i in list">{{ i }}{{ j }}</div>`
     ) as ForNode
     const div = node.children[0] as ElementNode
-
-    const i = div.children[0] as ExpressionNode
-    expect(i.type).toBe(NodeTypes.EXPRESSION)
-    expect(i.content).toBe(`i`)
-    expect(i.children).toBeUndefined()
-
-    const j = div.children[1] as ExpressionNode
-    expect(j.type).toBe(NodeTypes.EXPRESSION)
-    expect(j.children).toBeUndefined()
-    expect(j.content).toBe(`_ctx.j`)
+    expect((div.children[0] as InterpolationNode).content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `i`
+    })
+    expect((div.children[1] as InterpolationNode).content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.j`
+    })
   })
 
   test('should not prefix v-for aliases (multiple)', () => {
@@ -160,32 +216,30 @@ describe('compiler: expression transform', () => {
       `<div v-for="(i, j, k) in list">{{ i + j + k }}{{ l }}</div>`
     ) as ForNode
     const div = node.children[0] as ElementNode
-
-    const exp = div.children[0] as ExpressionNode
-    expect(exp.type).toBe(NodeTypes.EXPRESSION)
-    // parsed for better source-map support
-    expect(exp.children).toMatchObject([
-      { content: `i` },
-      ` + `,
-      { content: `j` },
-      ` + `,
-      { content: `k` }
-    ])
-
-    const l = div.children[1] as ExpressionNode
-    expect(l.type).toBe(NodeTypes.EXPRESSION)
-    expect(l.children).toBeUndefined()
-    expect(l.content).toBe(`_ctx.l`)
+    expect((div.children[0] as InterpolationNode).content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `i` },
+        ` + `,
+        { content: `j` },
+        ` + `,
+        { content: `k` }
+      ]
+    })
+    expect((div.children[1] as InterpolationNode).content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.l`
+    })
   })
 
   test('should prefix id outside of v-for', () => {
     const node = parseWithExpressionTransform(
       `<div><div v-for="i in list" />{{ i }}</div>`
     ) as ElementNode
-    const exp = node.children[1] as ExpressionNode
-    expect(exp.type).toBe(NodeTypes.EXPRESSION)
-    expect(exp.children).toBeUndefined()
-    expect(exp.content).toBe(`_ctx.i`)
+    expect((node.children[1] as InterpolationNode).content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `_ctx.i`
+    })
   })
 
   test('nested v-for', () => {
@@ -197,123 +251,130 @@ describe('compiler: expression transform', () => {
     const outerDiv = node.children[0] as ElementNode
     const innerFor = outerDiv.children[0] as ForNode
     const innerExp = (innerFor.children[0] as ElementNode)
-      .children[0] as ExpressionNode
-    expect(innerExp.type).toBe(NodeTypes.EXPRESSION)
-    expect(innerExp.children).toMatchObject([
-      { content: 'i' },
-      ` + `,
-      { content: `_ctx.j` }
-    ])
+      .children[0] as InterpolationNode
+    expect(innerExp.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [{ content: 'i' }, ` + `, { content: `_ctx.j` }]
+    })
 
     // when an inner v-for shadows a variable of an outer v-for and exit,
     // it should not cause the outer v-for's alias to be removed from known ids
-    const outerExp = outerDiv.children[1] as ExpressionNode
-    expect(outerExp.type).toBe(NodeTypes.EXPRESSION)
-    expect(outerExp.content).toBe(`i`)
-    expect(outerExp.children).toBeUndefined()
+    const outerExp = outerDiv.children[1] as InterpolationNode
+    expect(outerExp.content).toMatchObject({
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      content: `i`
+    })
   })
 
   test('should not prefix whitelisted globals', () => {
     const node = parseWithExpressionTransform(
       `{{ Math.max(1, 2) }}`
-    ) as ExpressionNode
-    expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.children).toMatchObject([
-      { content: `Math` },
-      `.`,
-      { content: `max` },
-      `(1, 2)`
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [{ content: `Math` }, `.`, { content: `max` }, `(1, 2)`]
+    })
   })
 
   test('should not prefix id of a function declaration', () => {
     const node = parseWithExpressionTransform(
       `{{ function foo() { return bar } }}`
-    ) as ExpressionNode
-    expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.children).toMatchObject([
-      `function `,
-      { content: `foo` },
-      `() { return `,
-      { content: `_ctx.bar` },
-      ` }`
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        `function `,
+        { content: `foo` },
+        `() { return `,
+        { content: `_ctx.bar` },
+        ` }`
+      ]
+    })
   })
 
   test('should not prefix params of a function expression', () => {
     const node = parseWithExpressionTransform(
       `{{ foo => foo + bar }}`
-    ) as ExpressionNode
-    expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.children).toMatchObject([
-      { content: `foo` },
-      ` => `,
-      { content: `foo` },
-      ` + `,
-      { content: `_ctx.bar` }
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `foo` },
+        ` => `,
+        { content: `foo` },
+        ` + `,
+        { content: `_ctx.bar` }
+      ]
+    })
   })
 
   test('should not prefix an object property key', () => {
     const node = parseWithExpressionTransform(
       `{{ { foo: bar } }}`
-    ) as ExpressionNode
-    expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.children).toMatchObject([
-      `{ foo: `,
-      { content: `_ctx.bar` },
-      ` }`
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [`{ foo: `, { content: `_ctx.bar` }, ` }`]
+    })
   })
 
   test('should prefix a computed object property key', () => {
     const node = parseWithExpressionTransform(
       `{{ { [foo]: bar } }}`
-    ) as ExpressionNode
-    expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.children).toMatchObject([
-      `{ [`,
-      { content: `_ctx.foo` },
-      `]: `,
-      { content: `_ctx.bar` },
-      ` }`
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        `{ [`,
+        { content: `_ctx.foo` },
+        `]: `,
+        { content: `_ctx.bar` },
+        ` }`
+      ]
+    })
   })
 
   test('should prefix object property shorthand value', () => {
-    const node = parseWithExpressionTransform(`{{ { foo } }}`) as ExpressionNode
-    expect(node.children).toMatchObject([
-      `{ foo: `,
-      { content: `_ctx.foo` },
-      ` }`
-    ])
+    const node = parseWithExpressionTransform(
+      `{{ { foo } }}`
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [`{ foo: `, { content: `_ctx.foo` }, ` }`]
+    })
   })
 
   test('should not prefix id in a member expression', () => {
     const node = parseWithExpressionTransform(
       `{{ foo.bar.baz }}`
-    ) as ExpressionNode
-    expect(node.children).toMatchObject([
-      { content: `_ctx.foo` },
-      `.`,
-      { content: `bar` },
-      `.`,
-      { content: `baz` }
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `_ctx.foo` },
+        `.`,
+        { content: `bar` },
+        `.`,
+        { content: `baz` }
+      ]
+    })
   })
 
   test('should prefix computed id in a member expression', () => {
     const node = parseWithExpressionTransform(
       `{{ foo[bar][baz] }}`
-    ) as ExpressionNode
-    expect(node.children).toMatchObject([
-      { content: `_ctx.foo` },
-      `[`,
-      { content: `_ctx.bar` },
-      `][`,
-      { content: '_ctx.baz' },
-      `]`
-    ])
+    ) as InterpolationNode
+    expect(node.content).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: [
+        { content: `_ctx.foo` },
+        `[`,
+        { content: `_ctx.bar` },
+        `][`,
+        { content: '_ctx.baz' },
+        `]`
+      ]
+    })
   })
 
   test('should handle parse error', () => {
index 1981277ca5c4926077b3aaa2010a48d0708d9b2f..aeba73ff691ec7d411937f6640bdfaf21c83238e 100644 (file)
@@ -31,7 +31,7 @@ describe('compiler: style transform', () => {
     )
     expect(root.hoists).toMatchObject([
       {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `{"color":"red"}`,
         isStatic: false
       }
@@ -40,12 +40,12 @@ describe('compiler: style transform', () => {
       type: NodeTypes.DIRECTIVE,
       name: `bind`,
       arg: {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `style`,
         isStatic: true
       },
       exp: {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `_hoisted_1`,
         isStatic: false
       }
@@ -64,12 +64,12 @@ describe('compiler: style transform', () => {
       properties: [
         {
           key: {
-            type: NodeTypes.EXPRESSION,
+            type: NodeTypes.SIMPLE_EXPRESSION,
             content: `style`,
             isStatic: true
           },
           value: {
-            type: NodeTypes.EXPRESSION,
+            type: NodeTypes.SIMPLE_EXPRESSION,
             content: `_hoisted_1`,
             isStatic: false
           }
index fb12b20a5270068816a92c0e02212999a37698fb..5c3f252c5e16748796ffdaf2a6f87303e9f7ac83 100644 (file)
@@ -8,6 +8,8 @@ import {
 } from '../../src'
 import { transformBind } from '../../src/transforms/vBind'
 import { transformElement } from '../../src/transforms/transformElement'
+import { CAMELIZE } from '../../src/runtimeConstants'
+import { transformExpression } from '../../src/transforms/transformExpression'
 
 function parseWithVBind(
   template: string,
@@ -15,7 +17,10 @@ function parseWithVBind(
 ): ElementNode {
   const ast = parse(template)
   transform(ast, {
-    nodeTransforms: [transformElement],
+    nodeTransforms: [
+      ...(options.prefixIdentifiers ? [transformExpression] : []),
+      transformElement
+    ],
     directiveTransforms: {
       bind: transformBind
     },
@@ -117,4 +122,42 @@ describe('compiler: transform v-bind', () => {
       }
     })
   })
+
+  test('.camel modifier w/ dynamic arg', () => {
+    const node = parseWithVBind(`<div v-bind:[foo].camel="id"/>`)
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        content: `_${CAMELIZE}(foo)`,
+        isStatic: false
+      },
+      value: {
+        content: `id`,
+        isStatic: false
+      }
+    })
+  })
+
+  test('.camel modifier w/ dynamic arg + prefixIdentifiers', () => {
+    const node = parseWithVBind(`<div v-bind:[foo(bar)].camel="id"/>`, {
+      prefixIdentifiers: true
+    })
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        children: [
+          `${CAMELIZE}(`,
+          { content: `_ctx.foo` },
+          `(`,
+          { content: `_ctx.bar` },
+          `)`,
+          `)`
+        ]
+      },
+      value: {
+        content: `_ctx.id`,
+        isStatic: false
+      }
+    })
+  })
 })
index 5dfcc0682ce24c1e2a78a2a25f6a48c477fec931..255e6604e51a0687d5e5de97263aeec69475a0ee 100644 (file)
@@ -1,7 +1,7 @@
 import { parse } from '../../src/parse'
 import { transform } from '../../src/transform'
 import { transformFor } from '../../src/transforms/vFor'
-import { ForNode, NodeTypes } from '../../src/ast'
+import { ForNode, NodeTypes, SimpleExpressionNode } from '../../src/ast'
 import { ErrorCodes } from '../../src/errors'
 import { CompilerOptions } from '../../src'
 
@@ -24,7 +24,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias).toBeUndefined()
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('index')
-    expect(forNode.source.content).toBe('5')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('5')
   })
 
   test('value', () => {
@@ -32,7 +32,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias).toBeUndefined()
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('item')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('object de-structured value', () => {
@@ -42,7 +42,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias).toBeUndefined()
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('{ id, value }')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('array de-structured value', () => {
@@ -52,7 +52,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias).toBeUndefined()
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('[ id, value ]')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('value and key', () => {
@@ -63,7 +63,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias!.content).toBe('key')
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('item')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('value, key and index', () => {
@@ -75,7 +75,7 @@ describe('compiler: transform v-for', () => {
     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.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('skipped key', () => {
@@ -86,7 +86,7 @@ describe('compiler: transform v-for', () => {
     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.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('skipped value and key', () => {
@@ -95,7 +95,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.objectIndexAlias).not.toBeUndefined()
     expect(forNode.objectIndexAlias!.content).toBe('index')
     expect(forNode.valueAlias).toBeUndefined()
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('unbracketed value', () => {
@@ -103,7 +103,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias).toBeUndefined()
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('item')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('unbracketed value and key', () => {
@@ -112,7 +112,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.keyAlias!.content).toBe('key')
     expect(forNode.objectIndexAlias).toBeUndefined()
     expect(forNode.valueAlias!.content).toBe('item')
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('unbracketed value, key and index', () => {
@@ -124,7 +124,7 @@ describe('compiler: transform v-for', () => {
     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.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('unbracketed skipped key', () => {
@@ -135,7 +135,7 @@ describe('compiler: transform v-for', () => {
     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.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('unbracketed skipped value and key', () => {
@@ -144,7 +144,7 @@ describe('compiler: transform v-for', () => {
     expect(forNode.objectIndexAlias).not.toBeUndefined()
     expect(forNode.objectIndexAlias!.content).toBe('index')
     expect(forNode.valueAlias).toBeUndefined()
-    expect(forNode.source.content).toBe('items')
+    expect((forNode.source as SimpleExpressionNode).content).toBe('items')
   })
 
   test('missing expression', () => {
@@ -223,7 +223,7 @@ describe('compiler: transform v-for', () => {
       )
 
       const itemsOffset = source.indexOf('items')
-      expect(forNode.source.content).toBe('items')
+      expect((forNode.source as SimpleExpressionNode).content).toBe('items')
       expect(forNode.source.loc.start.offset).toBe(itemsOffset)
       expect(forNode.source.loc.start.line).toBe(1)
       expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
@@ -248,7 +248,7 @@ describe('compiler: transform v-for', () => {
       )
 
       const itemsOffset = source.indexOf('items')
-      expect(forNode.source.content).toBe('items')
+      expect((forNode.source as SimpleExpressionNode).content).toBe('items')
       expect(forNode.source.loc.start.offset).toBe(itemsOffset)
       expect(forNode.source.loc.start.line).toBe(1)
       expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
@@ -273,7 +273,7 @@ describe('compiler: transform v-for', () => {
       )
 
       const itemsOffset = source.indexOf('items')
-      expect(forNode.source.content).toBe('items')
+      expect((forNode.source as SimpleExpressionNode).content).toBe('items')
       expect(forNode.source.loc.start.offset).toBe(itemsOffset)
       expect(forNode.source.loc.start.line).toBe(1)
       expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
@@ -318,7 +318,7 @@ describe('compiler: transform v-for', () => {
       )
 
       const itemsOffset = source.indexOf('items')
-      expect(forNode.source.content).toBe('items')
+      expect((forNode.source as SimpleExpressionNode).content).toBe('items')
       expect(forNode.source.loc.start.offset).toBe(itemsOffset)
       expect(forNode.source.loc.start.line).toBe(1)
       expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
@@ -353,7 +353,7 @@ describe('compiler: transform v-for', () => {
       )
 
       const itemsOffset = source.indexOf('items')
-      expect(forNode.source.content).toBe('items')
+      expect((forNode.source as SimpleExpressionNode).content).toBe('items')
       expect(forNode.source.loc.start.offset).toBe(itemsOffset)
       expect(forNode.source.loc.start.line).toBe(1)
       expect(forNode.source.loc.start.column).toBe(itemsOffset + 1)
index a9da468896da29d07766f401b57fcb0739db49fe..dcac2812c1ae45151b2e375305239fafcfd48180 100644 (file)
@@ -6,7 +6,8 @@ import {
   NodeTypes,
   ElementNode,
   TextNode,
-  CommentNode
+  CommentNode,
+  SimpleExpressionNode
 } from '../../src/ast'
 import { ErrorCodes } from '../../src/errors'
 import { CompilerOptions } from '../../src'
@@ -30,7 +31,9 @@ describe('compiler: transform v-if', () => {
     const node = parseWithIfTransform(`<div v-if="ok"/>`)
     expect(node.type).toBe(NodeTypes.IF)
     expect(node.branches.length).toBe(1)
-    expect(node.branches[0].condition!.content).toBe(`ok`)
+    expect((node.branches[0].condition as SimpleExpressionNode).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`)
@@ -42,7 +45,9 @@ describe('compiler: transform v-if', () => {
     )
     expect(node.type).toBe(NodeTypes.IF)
     expect(node.branches.length).toBe(1)
-    expect(node.branches[0].condition!.content).toBe(`ok`)
+    expect((node.branches[0].condition as SimpleExpressionNode).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`)
@@ -58,7 +63,7 @@ describe('compiler: transform v-if', () => {
     expect(node.branches.length).toBe(2)
 
     const b1 = node.branches[0]
-    expect(b1.condition!.content).toBe(`ok`)
+    expect((b1.condition as SimpleExpressionNode).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`)
@@ -76,13 +81,13 @@ describe('compiler: transform v-if', () => {
     expect(node.branches.length).toBe(2)
 
     const b1 = node.branches[0]
-    expect(b1.condition!.content).toBe(`ok`)
+    expect((b1.condition as SimpleExpressionNode).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.condition as SimpleExpressionNode).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`)
@@ -96,13 +101,13 @@ describe('compiler: transform v-if', () => {
     expect(node.branches.length).toBe(3)
 
     const b1 = node.branches[0]
-    expect(b1.condition!.content).toBe(`ok`)
+    expect((b1.condition as SimpleExpressionNode).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.condition as SimpleExpressionNode).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`)
@@ -126,13 +131,13 @@ describe('compiler: transform v-if', () => {
     expect(node.branches.length).toBe(3)
 
     const b1 = node.branches[0]
-    expect(b1.condition!.content).toBe(`ok`)
+    expect((b1.condition as SimpleExpressionNode).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.condition as SimpleExpressionNode).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`)
index b2b44e7b8266a9c5970600eebc4a9422e34d1aa9..c0cffa668a55be744c511edb987dd29ad0d43995 100644 (file)
@@ -4,7 +4,8 @@ import {
   ElementNode,
   ObjectExpression,
   CompilerOptions,
-  ErrorCodes
+  ErrorCodes,
+  NodeTypes
 } from '../../src'
 import { transformOn } from '../../src/transforms/vOn'
 import { transformElement } from '../../src/transforms/transformElement'
@@ -76,10 +77,11 @@ describe('compiler: transform v-on', () => {
     const props = node.codegenNode!.arguments[1] as ObjectExpression
     expect(props.properties[0]).toMatchObject({
       key: {
-        isStatic: false,
-        children: [`"on" + `, { content: `event` }]
+        type: NodeTypes.COMPOUND_EXPRESSION,
+        children: [`"on" + (`, { content: `event` }, `)`]
       },
       value: {
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `handler`,
         isStatic: false
       }
@@ -93,10 +95,36 @@ describe('compiler: transform v-on', () => {
     const props = node.codegenNode!.arguments[1] as ObjectExpression
     expect(props.properties[0]).toMatchObject({
       key: {
-        isStatic: false,
-        children: [`"on" + `, { content: `_ctx.event` }]
+        type: NodeTypes.COMPOUND_EXPRESSION,
+        children: [`"on" + (`, { content: `_ctx.event` }, `)`]
+      },
+      value: {
+        type: NodeTypes.SIMPLE_EXPRESSION,
+        content: `_ctx.handler`,
+        isStatic: false
+      }
+    })
+  })
+
+  test('dynamic arg with complex exp prefixing', () => {
+    const node = parseWithVOn(`<div v-on:[event(foo)]="handler"/>`, {
+      prefixIdentifiers: true
+    })
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        type: NodeTypes.COMPOUND_EXPRESSION,
+        children: [
+          `"on" + (`,
+          { content: `_ctx.event` },
+          `(`,
+          { content: `_ctx.foo` },
+          `)`,
+          `)`
+        ]
       },
       value: {
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: `_ctx.handler`,
         isStatic: false
       }
index d9b3cbc4c455fd90a47e19710d1b2d591acc2ec6..90817339e51e966951327d5c976e8a252cf802e0 100644 (file)
@@ -1,3 +1,5 @@
+import { isString } from '@vue/shared'
+
 // Vue template is a platform-agnostic superset of HTML (syntax only).
 // More namespaces like SVG and MathML are declared by platform specific
 // compilers.
@@ -12,7 +14,8 @@ export const enum NodeTypes {
   ELEMENT,
   TEXT,
   COMMENT,
-  EXPRESSION,
+  SIMPLE_EXPRESSION,
+  INTERPOLATION,
   ATTRIBUTE,
   DIRECTIVE,
   // containers
@@ -55,9 +58,11 @@ export interface Position {
 
 export type ParentNode = RootNode | ElementNode | IfBranchNode | ForNode
 
+export type ExpressionNode = SimpleExpressionNode | CompoundExpressionNode
+
 export type ChildNode =
   | ElementNode
-  | ExpressionNode
+  | InterpolationNode
   | TextNode
   | CommentNode
   | IfNode
@@ -107,12 +112,21 @@ export interface DirectiveNode extends Node {
   modifiers: string[]
 }
 
-export interface ExpressionNode extends Node {
-  type: NodeTypes.EXPRESSION
+export interface SimpleExpressionNode extends Node {
+  type: NodeTypes.SIMPLE_EXPRESSION
   content: string
   isStatic: boolean
-  isInterpolation: boolean
-  children?: (ExpressionNode | string)[]
+}
+
+export interface InterpolationNode extends Node {
+  type: NodeTypes.INTERPOLATION
+  content: ExpressionNode
+}
+
+// always dynamic
+export interface CompoundExpressionNode extends Node {
+  type: NodeTypes.COMPOUND_EXPRESSION
+  children: (SimpleExpressionNode | string)[]
 }
 
 export interface IfNode extends Node {
@@ -129,9 +143,9 @@ export interface IfBranchNode extends Node {
 export interface ForNode extends Node {
   type: NodeTypes.FOR
   source: ExpressionNode
-  valueAlias: ExpressionNode | undefined
-  keyAlias: ExpressionNode | undefined
-  objectIndexAlias: ExpressionNode | undefined
+  valueAlias: SimpleExpressionNode | undefined
+  keyAlias: SimpleExpressionNode | undefined
+  objectIndexAlias: SimpleExpressionNode | undefined
   children: ChildNode[]
 }
 
@@ -190,7 +204,7 @@ export function createObjectExpression(
 
 export function createObjectProperty(
   key: ExpressionNode,
-  value: ExpressionNode,
+  value: JSChildNode,
   loc: SourceLocation
 ): Property {
   return {
@@ -201,18 +215,40 @@ export function createObjectProperty(
   }
 }
 
-export function createExpression(
+export function createSimpleExpression(
   content: string,
   isStatic: boolean,
-  loc: SourceLocation,
-  isInterpolation = false
-): ExpressionNode {
+  loc: SourceLocation
+): SimpleExpressionNode {
   return {
-    type: NodeTypes.EXPRESSION,
+    type: NodeTypes.SIMPLE_EXPRESSION,
     loc,
     content,
-    isStatic,
-    isInterpolation
+    isStatic
+  }
+}
+
+export function createInterpolation(
+  content: string | CompoundExpressionNode,
+  loc: SourceLocation
+): InterpolationNode {
+  return {
+    type: NodeTypes.INTERPOLATION,
+    loc,
+    content: isString(content)
+      ? createSimpleExpression(content, false, loc)
+      : content
+  }
+}
+
+export function createCompoundExpression(
+  children: CompoundExpressionNode['children'],
+  loc: SourceLocation
+): CompoundExpressionNode {
+  return {
+    type: NodeTypes.COMPOUND_EXPRESSION,
+    loc,
+    children
   }
 }
 
index 8db313743480db940c6939ba429f5b40ac2fca73..6dc09119fb5c0debc50d32a56dde11b2cf74346c 100644 (file)
@@ -14,7 +14,10 @@ import {
   ObjectExpression,
   IfBranchNode,
   SourceLocation,
-  Position
+  Position,
+  InterpolationNode,
+  CompoundExpressionNode,
+  SimpleExpressionNode
 } from './ast'
 import { SourceMapGenerator, RawSourceMap } from 'source-map'
 import {
@@ -110,11 +113,7 @@ function createCodegenContext(
       if (!__BROWSER__ && context.map) {
         if (node) {
           let name
-          if (
-            node.type === NodeTypes.EXPRESSION &&
-            !node.children &&
-            !node.isStatic
-          ) {
+          if (node.type === NodeTypes.SIMPLE_EXPRESSION && !node.isStatic) {
             const content = node.content.replace(/^_ctx\./, '')
             if (content !== node.content && isSimpleIdentifier(content)) {
               name = content
@@ -263,7 +262,7 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
 
 // This will generate a single vnode call if:
 // - The target position explicitly allows a single node (root, if, for)
-// - The list has length === 1, AND The only child is a text or expression.
+// - The list has length === 1, AND The only child is a text, expression or comment.
 function genChildren(
   children: ChildNode[],
   context: CodegenContext,
@@ -277,7 +276,8 @@ function genChildren(
     children.length === 1 &&
     (allowSingle ||
       child.type === NodeTypes.TEXT ||
-      child.type == NodeTypes.EXPRESSION)
+      child.type === NodeTypes.INTERPOLATION ||
+      child.type === NodeTypes.COMMENT)
   ) {
     genNode(child, context)
   } else {
@@ -331,9 +331,15 @@ function genNode(node: CodegenNode, context: CodegenContext) {
     case NodeTypes.TEXT:
       genText(node, context)
       break
-    case NodeTypes.EXPRESSION:
+    case NodeTypes.SIMPLE_EXPRESSION:
       genExpression(node, context)
       break
+    case NodeTypes.INTERPOLATION:
+      genInterpolation(node, context)
+      break
+    case NodeTypes.COMPOUND_EXPRESSION:
+      genCompoundExpression(node, context)
+      break
     case NodeTypes.COMMENT:
       genComment(node, context)
       break
@@ -369,23 +375,36 @@ function genElement(node: ElementNode, context: CodegenContext) {
   genCallExpression(node.codegenNode!, context, false)
 }
 
-function genText(node: TextNode | ExpressionNode, context: CodegenContext) {
+function genText(
+  node: TextNode | SimpleExpressionNode,
+  context: CodegenContext
+) {
   context.push(JSON.stringify(node.content), node)
 }
 
-function genExpression(node: ExpressionNode, context: CodegenContext) {
+function genExpression(node: SimpleExpressionNode, context: CodegenContext) {
+  const { content, isStatic } = node
+  context.push(isStatic ? JSON.stringify(content) : content, node)
+}
+
+function genInterpolation(node: InterpolationNode, context: CodegenContext) {
   const { push, helper } = context
-  const { content, children, isStatic, isInterpolation } = node
-  if (isInterpolation) {
-    push(`${helper(TO_STRING)}(`)
-  }
-  if (children) {
-    genCompoundExpression(node, context)
-  } else {
-    push(isStatic ? JSON.stringify(content) : content, node)
-  }
-  if (isInterpolation) {
-    push(`)`)
+  push(`${helper(TO_STRING)}(`)
+  genNode(node.content, context)
+  push(`)`)
+}
+
+function genCompoundExpression(
+  node: CompoundExpressionNode,
+  context: CodegenContext
+) {
+  for (let i = 0; i < node.children!.length; i++) {
+    const child = node.children![i]
+    if (isString(child)) {
+      context.push(child)
+    } else {
+      genExpression(child, context)
+    }
   }
 }
 
@@ -394,28 +413,18 @@ function genExpressionAsPropertyKey(
   context: CodegenContext
 ) {
   const { push } = context
-  const { content, children, isStatic } = node
-  if (children) {
+  if (node.type === NodeTypes.COMPOUND_EXPRESSION) {
     push(`[`)
     genCompoundExpression(node, context)
     push(`]`)
-  } else if (isStatic) {
+  } else if (node.isStatic) {
     // only quote keys if necessary
-    const text = isSimpleIdentifier(content) ? content : JSON.stringify(content)
+    const text = isSimpleIdentifier(node.content)
+      ? node.content
+      : JSON.stringify(node.content)
     push(text, node)
   } else {
-    push(`[${content}]`, node)
-  }
-}
-
-function genCompoundExpression(node: ExpressionNode, context: CodegenContext) {
-  for (let i = 0; i < node.children!.length; i++) {
-    const child = node.children![i]
-    if (isString(child)) {
-      context.push(child)
-    } else {
-      genExpression(child, context)
-    }
+    push(`[${node.content}]`, node)
   }
 }
 
@@ -445,10 +454,14 @@ function genIfBranch(
   if (condition) {
     // v-if or v-else-if
     const { push, indent, deindent, newline } = context
-    const needsQuote = !isSimpleIdentifier(condition.content)
-    needsQuote && push(`(`)
-    genExpression(condition, context)
-    needsQuote && push(`)`)
+    if (condition.type === NodeTypes.SIMPLE_EXPRESSION) {
+      const needsQuote = !isSimpleIdentifier(condition.content)
+      needsQuote && push(`(`)
+      genExpression(condition, context)
+      needsQuote && push(`)`)
+    } else {
+      genCompoundExpression(condition, context)
+    }
     indent()
     context.indentLevel++
     push(`? `)
@@ -473,7 +486,7 @@ function genFor(node: ForNode, context: CodegenContext) {
   const { push, helper, indent, deindent } = context
   const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
   push(`${helper(RENDER_LIST)}(`, node, true)
-  genExpression(source, context)
+  genNode(source, context)
   push(`, (`)
   if (valueAlias) {
     genExpression(valueAlias, context)
index c077c1a8065e4c98122b848da9d234eb59e627a2..28a982c75b50b56a237b3f6e8dea88a7e03370cf 100644 (file)
@@ -128,7 +128,8 @@ export const errorMessages: { [code: number]: string } = {
   [ErrorCodes.X_MISSING_INTERPOLATION_END]:
     'Interpolation end sign was not found.',
   [ErrorCodes.X_MISSING_DYNAMIC_DIRECTIVE_ARGUMENT_END]:
-    'End bracket for dynamic directive argument was not found.',
+    'End bracket for dynamic directive argument was not found. ' +
+    'Note that dynamic directive argument connot contain spaces.',
 
   // transform errors
   [ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF]: `v-else-if has no adjacent v-if`,
index 6dfde5a7f843eb1c58cd6e820db1c3b7291b19c6..7343950c4cd851326101ba2e07583add1d5c07c3 100644 (file)
@@ -23,7 +23,8 @@ import {
   RootNode,
   SourceLocation,
   TextNode,
-  ChildNode
+  ChildNode,
+  InterpolationNode
 } from './ast'
 
 export interface ParserOptions {
@@ -122,7 +123,7 @@ function parseChildren(
   while (!isEnd(context, mode, ancestors)) {
     __DEV__ && assert(context.source.length > 0)
     const s = context.source
-    let node: any = null
+    let node: ChildNode | ChildNode[] | undefined = undefined
 
     if (startsWith(s, context.options.delimiters[0])) {
       // '{{'
@@ -529,10 +530,9 @@ function parseAttribute(
       }
 
       arg = {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content,
         isStatic,
-        isInterpolation: false,
         loc
       }
     }
@@ -555,10 +555,9 @@ function parseAttribute(
             ? 'on'
             : 'slot'),
       exp: value && {
-        type: NodeTypes.EXPRESSION,
+        type: NodeTypes.SIMPLE_EXPRESSION,
         content: value.content,
         isStatic: false,
-        isInterpolation: false,
         loc: value.loc
       },
       arg,
@@ -633,7 +632,7 @@ function parseAttributeValue(
 function parseInterpolation(
   context: ParserContext,
   mode: TextModes
-): ExpressionNode | undefined {
+): InterpolationNode | undefined {
   const [open, close] = context.options.delimiters
   __DEV__ && assert(startsWith(context.source, open))
 
@@ -643,28 +642,32 @@ function parseInterpolation(
     return undefined
   }
 
-  advanceBy(context, open.length)
   const start = getCursor(context)
-  const end = getCursor(context)
+  advanceBy(context, open.length)
+  const innerStart = getCursor(context)
+  const innerEnd = getCursor(context)
   const rawContentLength = closeIndex - open.length
   const rawContent = context.source.slice(0, rawContentLength)
   const preTrimContent = parseTextData(context, rawContentLength, mode)
   const content = preTrimContent.trim()
   const startOffset = preTrimContent.indexOf(content)
   if (startOffset > 0) {
-    advancePositionWithMutation(start, rawContent, startOffset)
+    advancePositionWithMutation(innerStart, rawContent, startOffset)
   }
   const endOffset =
     rawContentLength - (preTrimContent.length - content.length - startOffset)
-  advancePositionWithMutation(end, rawContent, endOffset)
+  advancePositionWithMutation(innerEnd, rawContent, endOffset)
   advanceBy(context, close.length)
 
   return {
-    type: NodeTypes.EXPRESSION,
-    content,
-    loc: getSelection(context, start, end),
-    isStatic: content === '',
-    isInterpolation: true
+    type: NodeTypes.INTERPOLATION,
+    content: {
+      type: NodeTypes.SIMPLE_EXPRESSION,
+      isStatic: false,
+      content,
+      loc: getSelection(context, innerStart, innerEnd)
+    },
+    loc: getSelection(context, start)
   }
 }
 
index a253b09667f045311dfa7f42cef19b68ddcf796b..6510963f7ffdab34e02541845e3b63e71234c2e6 100644 (file)
@@ -13,3 +13,4 @@ export const RENDER_LIST = `renderList`
 export const TO_STRING = `toString`
 export const MERGE_PROPS = `mergeProps`
 export const TO_HANDLERS = `toHandlers`
+export const CAMELIZE = `camelize`
index a7714b5c31fe35c8096a7be7d32630faefc3e0b8..d5a45776155cb04f3fca5139187cb75fcb4dca5c 100644 (file)
@@ -7,8 +7,9 @@ import {
   DirectiveNode,
   Property,
   ExpressionNode,
-  createExpression,
-  JSChildNode
+  createSimpleExpression,
+  JSChildNode,
+  SimpleExpressionNode
 } from './ast'
 import { isString, isArray } from '@vue/shared'
 import { CompilerError, defaultOnError } from './errors'
@@ -63,8 +64,8 @@ export interface TransformContext extends Required<TransformOptions> {
   replaceNode(node: ChildNode): void
   removeNode(node?: ChildNode): void
   onNodeRemoved: () => void
-  addIdentifier(exp: ExpressionNode): void
-  removeIdentifier(exp: ExpressionNode): void
+  addIdentifier(exp: SimpleExpressionNode): void
+  removeIdentifier(exp: SimpleExpressionNode): void
   hoist(exp: JSChildNode): ExpressionNode
 }
 
@@ -138,7 +139,7 @@ function createTransformContext(
     },
     hoist(exp) {
       context.hoists.push(exp)
-      return createExpression(
+      return createSimpleExpression(
         `_hoisted_${context.hoists.length}`,
         false,
         exp.loc
@@ -205,11 +206,9 @@ export function traverseNode(node: ChildNode, context: TransformContext) {
       // comment nodes with `createVNode`
       context.helper(COMMENT)
       break
-    case NodeTypes.EXPRESSION:
+    case NodeTypes.INTERPOLATION:
       // no need to traverse, but we need to inject toString helper
-      if (node.isInterpolation) {
-        context.helper(TO_STRING)
-      }
+      context.helper(TO_STRING)
       break
 
     // for container types, further traverse downwards
index 0d5cdd049df1237eb6b51807f4e557bcbf32e2bd..9024534236f2c3128e3a2a985e3aaad18f38588c 100644 (file)
@@ -11,7 +11,7 @@ import {
   createCallExpression,
   createArrayExpression,
   createObjectProperty,
-  createExpression,
+  createSimpleExpression,
   createObjectExpression,
   Property
 } from '../ast'
@@ -122,8 +122,12 @@ function buildProps(
       const { loc, name, value } = prop
       properties.push(
         createObjectProperty(
-          createExpression(name, true, getInnerRange(loc, 0, name.length)),
-          createExpression(
+          createSimpleExpression(
+            name,
+            true,
+            getInnerRange(loc, 0, name.length)
+          ),
+          createSimpleExpression(
             value ? value.content : '',
             true,
             value ? value.loc : loc
@@ -236,8 +240,8 @@ function dedupeProperties(properties: Property[]): Property[] {
   const deduped: Property[] = []
   for (let i = 0; i < properties.length; i++) {
     const prop = properties[i]
-    // dynamic key named are always allowed
-    if (!prop.key.isStatic) {
+    // dynamic keys are always allowed
+    if (prop.key.type === NodeTypes.COMPOUND_EXPRESSION || !prop.key.isStatic) {
       deduped.push(prop)
       continue
     }
@@ -273,16 +277,7 @@ function mergeAsArray(existing: Property, incoming: Property) {
 // :class="expression" class="string"
 // -> class: expression + "string"
 function mergeClasses(existing: Property, incoming: Property) {
-  const e = existing.value as ExpressionNode
-  const children =
-    e.children ||
-    (e.children = [
-      {
-        ...e,
-        children: undefined
-      }
-    ])
-  children.push(` + " " + `, incoming.value as ExpressionNode)
+  // TODO
 }
 
 function createDirectiveArgs(
@@ -305,8 +300,8 @@ function createDirectiveArgs(
       createObjectExpression(
         dir.modifiers.map(modifier =>
           createObjectProperty(
-            createExpression(modifier, true, loc),
-            createExpression(`true`, false, loc),
+            createSimpleExpression(modifier, true, loc),
+            createSimpleExpression(`true`, false, loc),
             loc
           )
         ),
index 90aedf060318b488bc4f904df7e786d85f500266..7555807050c0002a5ce2ab433654d0c27adfb671 100644 (file)
 import { parseScript } from 'meriyah'
 import { walk } from 'estree-walker'
 import { NodeTransform, TransformContext } from '../transform'
-import { NodeTypes, createExpression, ExpressionNode } from '../ast'
+import {
+  NodeTypes,
+  createSimpleExpression,
+  ExpressionNode,
+  SimpleExpressionNode,
+  CompoundExpressionNode,
+  createCompoundExpression
+} from '../ast'
 import { Node, Function, Identifier, Property } from 'estree'
 import { advancePositionWithClone } from '../utils'
+
 export const transformExpression: NodeTransform = (node, context) => {
-  if (node.type === NodeTypes.EXPRESSION && !node.isStatic) {
-    processExpression(node, context)
+  if (node.type === NodeTypes.INTERPOLATION) {
+    node.content = processExpression(
+      node.content as SimpleExpressionNode,
+      context
+    )
   } else if (node.type === NodeTypes.ELEMENT) {
     // handle directives on element
     for (let i = 0; i < node.props.length; i++) {
       const prop = node.props[i]
       if (prop.type === NodeTypes.DIRECTIVE) {
-        if (prop.exp) {
-          processExpression(prop.exp, context)
+        const exp = prop.exp as SimpleExpressionNode | undefined
+        const arg = prop.arg as SimpleExpressionNode | undefined
+        if (exp) {
+          prop.exp = processExpression(exp, context)
         }
-        if (prop.arg && !prop.arg.isStatic) {
+        if (arg && !arg.isStatic) {
           if (prop.name === 'class') {
             // TODO special expression optimization for classes
-            processExpression(prop.arg, context)
+            prop.arg = processExpression(arg, context)
           } else {
-            processExpression(prop.arg, context)
+            prop.arg = processExpression(arg, context)
           }
         }
       }
@@ -60,32 +73,33 @@ interface PrefixMeta {
 // always be used with a leading !__BROWSER__ check so that it can be
 // tree-shaken from the browser build.
 export function processExpression(
-  node: ExpressionNode,
+  node: SimpleExpressionNode,
   context: TransformContext
-) {
+): ExpressionNode {
   if (!context.prefixIdentifiers) {
-    return
+    return node
   }
-  // lazy require dependencies so that they don't end up in rollup's dep graph
-  // and thus can be tree-shaken in browser builds.
-  const parseScript =
-    _parseScript || (_parseScript = require('meriyah').parseScript)
-  const walk = _walk || (_walk = require('estree-walker').walk)
 
   // fast path if expression is a simple identifier.
   if (simpleIdRE.test(node.content)) {
     if (!context.identifiers[node.content]) {
       node.content = `_ctx.${node.content}`
     }
-    return
+    return node
   }
 
+  // lazy require dependencies so that they don't end up in rollup's dep graph
+  // and thus can be tree-shaken in browser builds.
+  const parseScript =
+    _parseScript || (_parseScript = require('meriyah').parseScript)
+  const walk = _walk || (_walk = require('estree-walker').walk)
+
   let ast
   try {
     ast = parseScript(`(${node.content})`, { ranges: true }) as any
   } catch (e) {
     context.onError(e)
-    return
+    return node
   }
 
   const ids: (Identifier & PrefixMeta)[] = []
@@ -145,7 +159,7 @@ export function processExpression(
   // an ExpressionNode has the `.children` property, it will be used instead of
   // `.content`.
   const full = node.content
-  const children: ExpressionNode['children'] = []
+  const children: CompoundExpressionNode['children'] = []
   ids.sort((a, b) => a.start - b.start)
   ids.forEach((id, i) => {
     const last = ids[i - 1] as any
@@ -155,7 +169,7 @@ export function processExpression(
     }
     const source = full.slice(id.start - 1, id.end - 1)
     children.push(
-      createExpression(id.name, false, {
+      createSimpleExpression(id.name, false, {
         source,
         start: advancePositionWithClone(node.loc.start, source, id.start - 1),
         end: advancePositionWithClone(node.loc.start, source, id.end - 1)
@@ -167,10 +181,9 @@ export function processExpression(
   })
 
   if (children.length) {
-    // mark it empty so that it's more noticeable in case another transform or
-    // codegen forget to handle `.children` first.
-    node.content = __DEV__ ? `[[REMOVED]]` : ``
-    node.children = children
+    return createCompoundExpression(children, node.loc)
+  } else {
+    return node
   }
 }
 
index 297b5e7264e0d2e3faca7b4317526503ae0266d7..47ce0b2a4430994aa405f16fcd71e977284eedf7 100644 (file)
@@ -1,5 +1,5 @@
 import { NodeTransform } from '../transform'
-import { NodeTypes, createExpression } from '../ast'
+import { NodeTypes, createSimpleExpression } from '../ast'
 
 // Parse inline CSS strings for static style attributes into an object.
 // This is a NodeTransform since it works on the static `style` attribute and
@@ -13,11 +13,11 @@ export const transformStyle: NodeTransform = (node, context) => {
       if (p.type === NodeTypes.ATTRIBUTE && p.name === 'style' && p.value) {
         // replace p with an expression node
         const parsed = JSON.stringify(parseInlineCSS(p.value.content))
-        const exp = context.hoist(createExpression(parsed, false, p.loc))
+        const exp = context.hoist(createSimpleExpression(parsed, false, p.loc))
         node.props[i] = {
           type: NodeTypes.DIRECTIVE,
           name: `bind`,
-          arg: createExpression(`style`, true, p.loc),
+          arg: createSimpleExpression(`style`, true, p.loc),
           exp,
           modifiers: [],
           loc: p.loc
index acfff0f48d7353083b4eda4fdd6d9d42e64c68d7..b8a71c4b0b5cf8627b769296a6743250f4dc4ff2 100644 (file)
@@ -1,27 +1,36 @@
 import { DirectiveTransform } from '../transform'
-import { createObjectProperty, createExpression } from '../ast'
+import { createObjectProperty, createSimpleExpression, NodeTypes } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { camelize } from '@vue/shared'
+import { CAMELIZE } from '../runtimeConstants'
 
 // 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 = (
-  { exp, arg, modifiers, loc },
-  context
-) => {
+export const transformBind: DirectiveTransform = (dir, context) => {
+  const { exp, modifiers, loc } = dir
+  const arg = dir.arg!
   if (!exp) {
     context.onError(createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc))
   }
   // .prop is no longer necessary due to new patch behavior
   // .sync is replced by v-model:arg
   if (modifiers.includes('camel')) {
-    arg!.content = camelize(arg!.content)
+    if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
+      if (arg.isStatic) {
+        arg.content = camelize(arg.content)
+      } else {
+        arg.content = `${context.helper(CAMELIZE)}(${arg.content})`
+      }
+    } else {
+      arg.children.unshift(`${context.helper(CAMELIZE)}(`)
+      arg.children.push(`)`)
+    }
   }
   return {
     props: createObjectProperty(
       arg!,
-      exp || createExpression('', true, loc),
+      exp || createSimpleExpression('', true, loc),
       loc
     ),
     needRuntime: false
index 8e436dc4311951f60cd2bfa0e2f98a2fa4dc2aec..a045f2270af364e67935975d36e8494bb6564aa0 100644 (file)
@@ -5,8 +5,9 @@ import {
 import {
   NodeTypes,
   ExpressionNode,
-  createExpression,
-  SourceLocation
+  createSimpleExpression,
+  SourceLocation,
+  SimpleExpressionNode
 } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { getInnerRange } from '../utils'
@@ -17,7 +18,12 @@ export const transformFor = createStructuralDirectiveTransform(
   'for',
   (node, dir, context) => {
     if (dir.exp) {
-      const parseResult = parseForExpression(dir.exp, context)
+      const parseResult = parseForExpression(
+        // can only be simple expression because vFor transform is applied
+        // before expression transform.
+        dir.exp as SimpleExpressionNode,
+        context
+      )
 
       if (parseResult) {
         context.helper(RENDER_LIST)
@@ -66,29 +72,33 @@ const stripParensRE = /^\(|\)$/g
 
 interface ForParseResult {
   source: ExpressionNode
-  value: ExpressionNode | undefined
-  key: ExpressionNode | undefined
-  index: ExpressionNode | undefined
+  value: SimpleExpressionNode | undefined
+  key: SimpleExpressionNode | undefined
+  index: SimpleExpressionNode | undefined
 }
 
 function parseForExpression(
-  input: ExpressionNode,
+  input: SimpleExpressionNode,
   context: TransformContext
 ): ForParseResult | null {
   const loc = input.loc
-  const source = input.content
-  const inMatch = source.match(forAliasRE)
+  const exp = input.content
+  const inMatch = exp.match(forAliasRE)
   if (!inMatch) return null
 
   const [, LHS, RHS] = inMatch
+
+  let source: ExpressionNode = createAliasExpression(
+    loc,
+    RHS.trim(),
+    exp.indexOf(RHS, LHS.length)
+  )
+  if (!__BROWSER__ && context.prefixIdentifiers) {
+    source = processExpression(source, context)
+  }
+
   const result: ForParseResult = {
-    source: createAliasExpression(
-      loc,
-      RHS.trim(),
-      source.indexOf(RHS, LHS.length),
-      context,
-      context.prefixIdentifiers
-    ),
+    source,
     value: undefined,
     key: undefined,
     index: undefined
@@ -106,11 +116,8 @@ function parseForExpression(
     const keyContent = iteratorMatch[1].trim()
     let keyOffset: number | undefined
     if (keyContent) {
-      keyOffset = source.indexOf(
-        keyContent,
-        trimmedOffset + valueContent.length
-      )
-      result.key = createAliasExpression(loc, keyContent, keyOffset, context)
+      keyOffset = exp.indexOf(keyContent, trimmedOffset + valueContent.length)
+      result.key = createAliasExpression(loc, keyContent, keyOffset)
     }
 
     if (iteratorMatch[2]) {
@@ -120,25 +127,19 @@ function parseForExpression(
         result.index = createAliasExpression(
           loc,
           indexContent,
-          source.indexOf(
+          exp.indexOf(
             indexContent,
             result.key
               ? keyOffset! + keyContent.length
               : trimmedOffset + valueContent.length
-          ),
-          context
+          )
         )
       }
     }
   }
 
   if (valueContent) {
-    result.value = createAliasExpression(
-      loc,
-      valueContent,
-      trimmedOffset,
-      context
-    )
+    result.value = createAliasExpression(loc, valueContent, trimmedOffset)
   }
 
   return result
@@ -147,17 +148,11 @@ function parseForExpression(
 function createAliasExpression(
   range: SourceLocation,
   content: string,
-  offset: number,
-  context: TransformContext,
-  process: boolean = false
-): ExpressionNode {
-  const exp = createExpression(
+  offset: number
+): SimpleExpressionNode {
+  return createSimpleExpression(
     content,
     false,
     getInnerRange(range, offset, content.length)
   )
-  if (!__BROWSER__ && process) {
-    processExpression(exp, context)
-  }
-  return exp
 }
index 1d8fe08aaf6db6aa08c9a42f9f27b45a754aab9a..96d340f332bc0dabdf9f35a6f60ba3454957a45c 100644 (file)
@@ -7,7 +7,8 @@ import {
   ElementTypes,
   ElementNode,
   DirectiveNode,
-  IfBranchNode
+  IfBranchNode,
+  SimpleExpressionNode
 } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { processExpression } from './transformExpression'
@@ -16,7 +17,9 @@ export const transformIf = createStructuralDirectiveTransform(
   /^(if|else|else-if)$/,
   (node, dir, context) => {
     if (!__BROWSER__ && context.prefixIdentifiers && dir.exp) {
-      processExpression(dir.exp, context)
+      // dir.exp can only be simple expression because vIf transform is applied
+      // before expression transform.
+      processExpression(dir.exp as SimpleExpressionNode, context)
     }
     if (dir.name === 'if') {
       context.replaceNode({
index 019bd6ed8bf37e8b93de595cda62e43d1c69791b..9cc2f63c8322ccb805fdf1e45a6b47b412a3071b 100644 (file)
@@ -1,37 +1,46 @@
 import { DirectiveTransform } from '../transform'
-import { createObjectProperty, createExpression, ExpressionNode } from '../ast'
+import {
+  createObjectProperty,
+  createSimpleExpression,
+  ExpressionNode,
+  NodeTypes,
+  createCompoundExpression
+} from '../ast'
 import { capitalize } from '@vue/shared'
 import { createCompilerError, ErrorCodes } from '../errors'
 
 // 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 = (
-  { arg, exp, loc, modifiers },
-  context
-) => {
+export const transformOn: DirectiveTransform = (dir, context) => {
+  const { exp, loc, modifiers } = dir
+  const arg = dir.arg!
   if (!exp && !modifiers.length) {
     context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
   }
-  const { content, isStatic, loc: argLoc } = arg!
   let eventName: ExpressionNode
-  if (isStatic) {
-    // static arg
-    eventName = createExpression(`on${capitalize(content)}`, true, argLoc)
+  if (arg.type === NodeTypes.SIMPLE_EXPRESSION) {
+    if (arg.isStatic) {
+      eventName = createSimpleExpression(
+        `on${capitalize(arg.content)}`,
+        true,
+        arg.loc
+      )
+    } else {
+      eventName = createCompoundExpression([`"on" + (`, arg, `)`], arg.loc)
+    }
   } else {
-    // dynamic arg. turn it into a compound expression.
-    eventName = arg!
-    ;(
-      eventName.children ||
-      (eventName.children = [{ ...eventName, children: undefined }])
-    ).unshift(`"on" + `)
+    // already a compound epxression.
+    eventName = arg
+    eventName.children.unshift(`"on" + (`)
+    eventName.children.push(`)`)
   }
   // TODO .once modifier handling since it is platform agnostic
   // other modifiers are handled in compiler-dom
   return {
     props: createObjectProperty(
       eventName,
-      exp || createExpression(`() => {}`, false, loc),
+      exp || createSimpleExpression(`() => {}`, false, loc),
       loc
     ),
     needRuntime: false
index 6913514fb4dc0abdda558d94350b970720092034..fc7c53529e10adae050ba5e5d4cd74ba9c8e260d 100644 (file)
@@ -4,8 +4,8 @@ import {
   ElementNode,
   TextNode,
   ErrorCodes,
-  ExpressionNode,
-  ElementTypes
+  ElementTypes,
+  InterpolationNode
 } from '@vue/compiler-core'
 import {
   parserOptionsMinimal as parserOptions,
@@ -109,17 +109,24 @@ describe('DOM parser', () => {
     test('HTML entities in interpolation should be translated for backward compatibility.', () => {
       const ast = parse('<div>{{ a &lt; b }}</div>', parserOptions)
       const element = ast.children[0] as ElementNode
-      const interpolation = element.children[0] as ExpressionNode
+      const interpolation = element.children[0] as InterpolationNode
 
       expect(interpolation).toStrictEqual({
-        type: NodeTypes.EXPRESSION,
-        content: 'a < b',
-        isStatic: false,
-        isInterpolation: true,
+        type: NodeTypes.INTERPOLATION,
+        content: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: `a < b`,
+          isStatic: false,
+          loc: {
+            start: { offset: 8, line: 1, column: 9 },
+            end: { offset: 16, line: 1, column: 17 },
+            source: 'a &lt; b'
+          }
+        },
         loc: {
-          start: { offset: 8, line: 1, column: 9 },
-          end: { offset: 16, line: 1, column: 17 },
-          source: 'a &lt; b'
+          start: { offset: 5, line: 1, column: 6 },
+          end: { offset: 19, line: 1, column: 20 },
+          source: '{{ a &lt; b }}'
         }
       })
     })
index 0844627bac1d3ef51dce30146596964dde4df121..9082b85906965c93412d8034e829f9400b89c6b8 100644 (file)
@@ -42,6 +42,7 @@ export { resolveComponent, resolveDirective } from './helpers/resolveAssets'
 export { renderList } from './helpers/renderList'
 export { toString } from './helpers/toString'
 export { toHandlers } from './helpers/toHandlers'
+export { capitalize, camelize } from '@vue/shared'
 
 // Internal, for integration with runtime compiler
 export { registerRuntimeCompiler } from './component'