]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler): correct source map generation
authorEvan You <yyx990803@gmail.com>
Thu, 26 Sep 2019 18:55:53 +0000 (14:55 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 26 Sep 2019 18:55:53 +0000 (14:55 -0400)
packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap
packages/compiler-core/__tests__/compile.spec.ts
packages/compiler-core/__tests__/parse.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/parse.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-dom/__tests__/parse.spec.ts

index 91d3bf425d44defbf8926c626dda0be12ac64406..2a0a6c1dce3e117a9990bb9b5c188a7dc30df511 100644 (file)
@@ -7,9 +7,9 @@ return function render() {
     const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue
     return _createVNode(\\"div\\", {
       id: \\"foo\\",
-      class: bar
+      class: bar.baz
     }, [
-      _toString(world),
+      _toString(world.burn()),
       ok
         ? _createVNode(\\"div\\", 0, \\"yes\\")
         : \\"no\\",
@@ -21,25 +21,6 @@ return function render() {
 }"
 `;
 
-exports[`compiler: integration tests function mode 2`] = `
-Object {
-  "mappings": ";;;;WAAA,oBAAA;MAAK,IAAG;MAAO,OAAO;;gBACpB;MACW;UAAX,uBAAe;UACE;MACjB,YAA8B,OAAjB,OAAO;eAApB,wBAAoC,kCAAM",
-  "names": Array [],
-  "sources": Array [
-    "foo.vue",
-  ],
-  "sourcesContent": Array [
-    "<div id=\\"foo\\" :class=\\"bar\\">
-  {{ world }}
-  <div v-if=\\"ok\\">yes</div>
-  <template v-else>no</template>
-  <div v-for=\\"(value, index) in list\\"><span>{{ value + index }}</span></div>
-</div>",
-  ],
-  "version": 3,
-}
-`;
-
 exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
 "const { createVNode, toString, renderList } = Vue
 
@@ -47,9 +28,9 @@ return function render() {
   const _ctx = this
   return createVNode(\\"div\\", {
     id: \\"foo\\",
-    class: _ctx.bar
+    class: _ctx.bar.baz
   }, [
-    toString(_ctx.world),
+    toString(_ctx.world.burn()),
     (_ctx.ok)
       ? createVNode(\\"div\\", 0, \\"yes\\")
       : \\"no\\",
@@ -60,30 +41,6 @@ return function render() {
 }"
 `;
 
-exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 2`] = `
-Object {
-  "mappings": ";;;;SAAA,mBAAA;IAAK,IAAG;IAAO,OAAOA;;aACpBC;KACWC;QAAX,sBAAe;QACE;IACjB,WAA8BC,YAAjB,OAAO;aAApB,uBAAoC,gCAAS,QAAQ",
-  "names": Array [
-    "bar",
-    "world",
-    "ok",
-    "list",
-  ],
-  "sources": Array [
-    "foo.vue",
-  ],
-  "sourcesContent": Array [
-    "<div id=\\"foo\\" :class=\\"bar\\">
-  {{ world }}
-  <div v-if=\\"ok\\">yes</div>
-  <template v-else>no</template>
-  <div v-for=\\"(value, index) in list\\"><span>{{ value + index }}</span></div>
-</div>",
-  ],
-  "version": 3,
-}
-`;
-
 exports[`compiler: integration tests module mode 1`] = `
 "import { createVNode, toString, renderList } from \\"vue\\"
 
@@ -91,9 +48,9 @@ export default function render() {
   const _ctx = this
   return createVNode(\\"div\\", {
     id: \\"foo\\",
-    class: _ctx.bar
+    class: _ctx.bar.baz
   }, [
-    _toString(_ctx.world),
+    _toString(_ctx.world.burn()),
     (_ctx.ok)
       ? createVNode(\\"div\\", 0, \\"yes\\")
       : \\"no\\",
index 694b37348d2dec796aa1723afee6f9172ed78b7e..947bfb04bf3d312e45ae5686ef8c8cddb193e896 100644 (file)
@@ -3889,15 +3889,15 @@ Object {
           "isStatic": false,
           "loc": Object {
             "end": Object {
-              "column": 20,
+              "column": 18,
               "line": 1,
-              "offset": 19,
+              "offset": 17,
             },
-            "source": "{{a < b}}",
+            "source": "a < b",
             "start": Object {
-              "column": 11,
+              "column": 13,
               "line": 1,
-              "offset": 10,
+              "offset": 12,
             },
           },
           "type": 4,
@@ -6965,15 +6965,15 @@ Object {
           "isStatic": false,
           "loc": Object {
             "end": Object {
-              "column": 23,
+              "column": 21,
               "line": 1,
-              "offset": 22,
+              "offset": 20,
             },
-            "source": "{{'</div>'}}",
+            "source": "'</div>'",
             "start": Object {
-              "column": 11,
+              "column": 13,
               "line": 1,
-              "offset": 10,
+              "offset": 12,
             },
           },
           "type": 4,
@@ -7322,15 +7322,15 @@ Object {
       "isStatic": true,
       "loc": Object {
         "end": Object {
-          "column": 5,
+          "column": 3,
           "line": 1,
-          "offset": 4,
+          "offset": 2,
         },
-        "source": "{{}}",
+        "source": "",
         "start": Object {
-          "column": 1,
+          "column": 3,
           "line": 1,
-          "offset": 0,
+          "offset": 2,
         },
       },
       "type": 4,
@@ -7485,7 +7485,7 @@ Object {
                 "line": 1,
                 "offset": 32,
               },
-              "source": "\\"{ some: condition }\\"",
+              "source": "{ some: condition }",
               "start": Object {
                 "column": 14,
                 "line": 1,
@@ -7565,7 +7565,7 @@ Object {
                 "line": 2,
                 "offset": 70,
               },
-              "source": "\\"{ color: 'red' }\\"",
+              "source": "{ color: 'red' }",
               "start": Object {
                 "column": 18,
                 "line": 2,
@@ -7672,7 +7672,7 @@ Object {
                     "line": 2,
                     "offset": 70,
                   },
-                  "source": "\\"{ color: 'red' }\\"",
+                  "source": "{ color: 'red' }",
                   "start": Object {
                     "column": 20,
                     "line": 2,
@@ -7771,7 +7771,7 @@ Object {
                 "line": 1,
                 "offset": 32,
               },
-              "source": "\\"{ some: condition }\\"",
+              "source": "{ some: condition }",
               "start": Object {
                 "column": 14,
                 "line": 1,
index e5302247d69e86072fec75cdddea2cdab0e4b07b..951702ba2aecf01e4140e1b6e67a4e468bcf1d07 100644 (file)
@@ -3,8 +3,8 @@ import { SourceMapConsumer, RawSourceMap } from 'source-map'
 
 describe('compiler: integration tests', () => {
   const source = `
-<div id="foo" :class="bar">
-  {{ world }}
+<div id="foo" :class="bar.baz">
+  {{ world.burn() }}
   <div v-if="ok">yes</div>
   <template v-else>no</template>
   <div v-for="(value, index) in list"><span>{{ value + index }}</span></div>
@@ -55,7 +55,6 @@ describe('compiler: integration tests', () => {
     )
 
     expect(code).toMatchSnapshot()
-    expect(map).toMatchSnapshot()
     expect(map!.sources).toEqual([`foo.vue`])
     expect(map!.sourcesContent).toEqual([source])
 
@@ -77,9 +76,21 @@ describe('compiler: integration tests', () => {
       consumer.originalPositionFor(getPositionInCode(code, `bar`))
     ).toMatchObject(getPositionInCode(source, `bar`))
 
+    // without prefixIdentifiers: true, identifiers inside compound expressions
+    // are mapped to closest parent expression.
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `baz`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `world`))
-    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+    ).toMatchObject(getPositionInCode(source, `world`))
+
+    // without prefixIdentifiers: true, identifiers inside compound expressions
+    // are mapped to closest parent expression.
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `burn()`))
+    ).toMatchObject(getPositionInCode(source, `world`))
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `ok`))
@@ -99,7 +110,7 @@ describe('compiler: integration tests', () => {
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `value + index`))
-    ).toMatchObject(getPositionInCode(source, `{{ value + index }}`))
+    ).toMatchObject(getPositionInCode(source, `value + index`))
   })
 
   test('function mode w/ prefixIdentifiers: true', async () => {
@@ -112,7 +123,6 @@ describe('compiler: integration tests', () => {
     expect(code).toMatch(`const { createVNode, toString, renderList } = Vue`)
 
     expect(code).toMatchSnapshot()
-    expect(map).toMatchSnapshot()
     expect(map!.sources).toEqual([`foo.vue`])
     expect(map!.sourcesContent).toEqual([source])
 
@@ -136,15 +146,22 @@ describe('compiler: integration tests', () => {
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`, `bar`))
     ).toMatchObject(getPositionInCode(source, `bar`, true))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `baz`))
+    ).toMatchObject(getPositionInCode(source, `baz`))
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `world`, true))
-    ).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
+    ).toMatchObject(getPositionInCode(source, `world`, `world`))
     expect(
       consumer.originalPositionFor(
         getPositionInCode(code, `_ctx.world`, `world`)
       )
-    ).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
+    ).toMatchObject(getPositionInCode(source, `world`, `world`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `burn()`))
+    ).toMatchObject(getPositionInCode(source, `burn()`))
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `ok`))
@@ -208,15 +225,22 @@ describe('compiler: integration tests', () => {
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`, `bar`))
     ).toMatchObject(getPositionInCode(source, `bar`, true))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `baz`))
+    ).toMatchObject(getPositionInCode(source, `baz`))
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `world`, true))
-    ).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
+    ).toMatchObject(getPositionInCode(source, `world`, `world`))
     expect(
       consumer.originalPositionFor(
         getPositionInCode(code, `_ctx.world`, `world`)
       )
-    ).toMatchObject(getPositionInCode(source, `{{ world }}`, `world`))
+    ).toMatchObject(getPositionInCode(source, `world`, `world`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `burn()`))
+    ).toMatchObject(getPositionInCode(source, `burn()`))
 
     expect(
       consumer.originalPositionFor(getPositionInCode(code, `ok`))
index d948f9ef90a66c531d76242c4985c7df79e63bd5..cd99f3b95ed21cf3541656bed27595e4849bc276 100644 (file)
@@ -298,9 +298,9 @@ describe('compiler: parse', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 0, line: 1, column: 1 },
-          end: { offset: 11, line: 1, column: 12 },
-          source: '{{message}}'
+          start: { offset: 2, line: 1, column: 3 },
+          end: { offset: 9, line: 1, column: 10 },
+          source: 'message'
         }
       })
     })
@@ -315,9 +315,9 @@ describe('compiler: parse', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 0, line: 1, column: 1 },
-          end: { offset: 9, line: 1, column: 10 },
-          source: '{{ a<b }}'
+          start: { offset: 3, line: 1, column: 4 },
+          end: { offset: 6, line: 1, column: 7 },
+          source: 'a<b'
         }
       })
     })
@@ -333,9 +333,9 @@ describe('compiler: parse', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 0, line: 1, column: 1 },
-          end: { offset: 9, line: 1, column: 10 },
-          source: '{{ a<b }}'
+          start: { offset: 3, line: 1, column: 4 },
+          end: { offset: 6, line: 1, column: 7 },
+          source: 'a<b'
         }
       })
       expect(interpolation2).toStrictEqual({
@@ -344,9 +344,9 @@ describe('compiler: parse', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 9, line: 1, column: 10 },
-          end: { offset: 18, line: 1, column: 19 },
-          source: '{{ c>d }}'
+          start: { offset: 12, line: 1, column: 13 },
+          end: { offset: 15, line: 1, column: 16 },
+          source: 'c>d'
         }
       })
     })
@@ -362,9 +362,9 @@ describe('compiler: parse', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 5, line: 1, column: 6 },
-          end: { offset: 19, line: 1, column: 20 },
-          source: '{{ "</div>" }}'
+          start: { offset: 8, line: 1, column: 9 },
+          end: { offset: 16, line: 1, column: 17 },
+          source: '"</div>"'
         }
       })
     })
@@ -896,7 +896,7 @@ describe('compiler: parse', () => {
           loc: {
             start: { offset: 11, line: 1, column: 12 },
             end: { offset: 12, line: 1, column: 13 },
-            source: '"a"'
+            source: 'a'
           }
         },
         loc: {
@@ -1313,17 +1313,20 @@ 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 })
+    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: 14, offset })
+    expect(bar.loc.end).toEqual({ line: 2, column: 11, offset })
+    offset += 3
 
     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 })
+    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: 28, offset })
+    expect(baz.loc.end).toEqual({ line: 2, column: 25, offset })
   })
 
   describe('namedCharacterReferences option', () => {
index 566a9f71a1ba37b00998435982f3c5b7055870bd..a7e0727b3a70f74c4279128b4183b195e47fc939 100644 (file)
@@ -66,7 +66,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
   indentLevel: number
   map?: SourceMapGenerator
   helper(name: string): string
-  push(code: string, node?: CodegenNode): void
+  push(code: string, node?: CodegenNode, openOnly?: boolean): void
   indent(): void
   deindent(withoutNewLine?: boolean): void
   newline(): void
@@ -102,7 +102,7 @@ function createCodegenContext(
     helper(name) {
       return prefixIdentifiers ? name : `_${name}`
     },
-    push(code, node?: CodegenNode) {
+    push(code, node, openOnly) {
       context.code += code
       if (context.map) {
         if (node) {
@@ -131,6 +131,19 @@ function createCodegenContext(
           })
         }
         advancePositionWithMutation(context, code)
+        if (node && !openOnly) {
+          context.map.addMapping({
+            source: context.filename,
+            original: {
+              line: node.loc.end.line,
+              column: node.loc.end.column - 1
+            },
+            generated: {
+              line: context.line,
+              column: context.column - 1
+            }
+          })
+        }
       }
     },
     indent() {
@@ -453,7 +466,7 @@ function genIfBranch(
 function genFor(node: ForNode, context: CodegenContext) {
   const { push, helper, indent, deindent } = context
   const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
-  push(`${helper(RENDER_LIST)}(`, node)
+  push(`${helper(RENDER_LIST)}(`, node, true)
   genExpression(source, context)
   push(`, (`)
   if (valueAlias) {
@@ -491,7 +504,7 @@ function genCallExpression(
   context: CodegenContext,
   multilines = node.arguments.length > 2
 ) {
-  context.push(node.callee + `(`, node)
+  context.push(node.callee + `(`, node, true)
   multilines && context.indent()
   genNodeList(node.arguments, context, multilines)
   multilines && context.deindent()
@@ -502,7 +515,7 @@ function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
   const { push, indent, deindent, newline } = context
   const { properties } = node
   const multilines = properties.length > 1
-  push(multilines ? `{` : `{ `, node)
+  push(multilines ? `{` : `{ `)
   multilines && indent()
   for (let i = 0; i < properties.length; i++) {
     const { key, value } = properties[i]
index b153bab0f0cd4dbe18a0024ea2d89fbbb9ff99d8..6dfde5a7f843eb1c58cd6e820db1c3b7291b19c6 100644 (file)
@@ -542,6 +542,7 @@ function parseAttribute(
       valueLoc.start.offset++
       valueLoc.start.column++
       valueLoc.end = advancePositionWithClone(valueLoc.start, value.content)
+      valueLoc.source = valueLoc.source.slice(1, -1)
     }
 
     return {
@@ -642,15 +643,26 @@ function parseInterpolation(
     return undefined
   }
 
-  const start = getCursor(context)
   advanceBy(context, open.length)
-  const content = parseTextData(context, closeIndex - open.length, mode).trim()
+  const start = getCursor(context)
+  const end = 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)
+  }
+  const endOffset =
+    rawContentLength - (preTrimContent.length - content.length - startOffset)
+  advancePositionWithMutation(end, rawContent, endOffset)
   advanceBy(context, close.length)
 
   return {
     type: NodeTypes.EXPRESSION,
     content,
-    loc: getSelection(context, start),
+    loc: getSelection(context, start, end),
     isStatic: content === '',
     isInterpolation: true
   }
index 533108acef9ab2298fa3f61272b058c899d2eab7..90aedf060318b488bc4f904df7e786d85f500266 100644 (file)
@@ -157,8 +157,8 @@ export function processExpression(
     children.push(
       createExpression(id.name, false, {
         source,
-        start: advancePositionWithClone(node.loc.start, source, id.start + 2),
-        end: advancePositionWithClone(node.loc.start, source, id.end + 2)
+        start: advancePositionWithClone(node.loc.start, source, id.start - 1),
+        end: advancePositionWithClone(node.loc.start, source, id.end - 1)
       })
     )
     if (i === ids.length - 1 && id.end - 1 < full.length) {
index f9054ab450d0e44dc1c497709a27890abd4684b5..6913514fb4dc0abdda558d94350b970720092034 100644 (file)
@@ -117,9 +117,9 @@ describe('DOM parser', () => {
         isStatic: false,
         isInterpolation: true,
         loc: {
-          start: { offset: 5, line: 1, column: 6 },
-          end: { offset: 19, line: 1, column: 20 },
-          source: '{{ a &lt; b }}'
+          start: { offset: 8, line: 1, column: 9 },
+          end: { offset: 16, line: 1, column: 17 },
+          source: 'a &lt; b'
         }
       })
     })