]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: compiler integration tests
authorEvan You <yyx990803@gmail.com>
Thu, 26 Sep 2019 02:29:37 +0000 (22:29 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 26 Sep 2019 02:29:37 +0000 (22:29 -0400)
packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/compile.spec.ts
packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts
packages/compiler-core/__tests__/transforms/vOn.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-core/src/transforms/vOn.ts

index dd8d8311b07b6e9ebb9fbef6a7837bb053eb5be8..8331ad7dcb732769c18b403bc604dac266e6054e 100644 (file)
@@ -154,12 +154,11 @@ return function render() {
 `;
 
 exports[`compiler: codegen module mode preamble 1`] = `
-"import { helperOne, helperTwo } from 'vue'
+"import { helperOne, helperTwo } from \\"vue\\"
 
 export default function render() {
-  with (this) {
-    return null
-  }
+  const _ctx = this
+  return null
 }"
 `;
 
index f7508225d5708d829ac0c59cd3067ebb776c112b..db2bfb17f56fb8c4e40aa2946821220ac7870c79 100644 (file)
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`function mode 1`] = `
+exports[`compiler: integration tests function mode 1`] = `
 "const _Vue = Vue
 return function render() {
   with (this) {
@@ -13,10 +13,50 @@ return function render() {
       ok
         ? _createVNode(\\"div\\", 0, \\"yes\\")
         : \\"no\\",
-      _renderList(list, (i, j) => {
-        return _createVNode(\\"div\\", 0, [_createVNode(\\"span\\", 0, _toString(i + j))])
+      _renderList(list, (value, index) => {
+        return _createVNode(\\"div\\", 0, [_createVNode(\\"span\\", 0, _toString(value + index))])
       })
     ])
   }
 }"
 `;
+
+exports[`compiler: integration tests function mode w/ prefixIdentifiers: true 1`] = `
+"const { createVNode, toString, renderList } = Vue
+
+return function render() {
+  const _ctx = this
+  return createVNode(\\"div\\", {
+    id: \\"foo\\",
+    class: _ctx.bar
+  }, [
+    toString(_ctx.world),
+    (_ctx.ok)
+      ? createVNode(\\"div\\", 0, \\"yes\\")
+      : \\"no\\",
+    renderList(_ctx.list, (value, index) => {
+      return createVNode(\\"div\\", 0, [createVNode(\\"span\\", 0, toString(value + index))])
+    })
+  ])
+}"
+`;
+
+exports[`compiler: integration tests module mode 1`] = `
+"import { createVNode, toString, renderList } from \\"vue\\"
+
+export default function render() {
+  const _ctx = this
+  return createVNode(\\"div\\", {
+    id: \\"foo\\",
+    class: _ctx.bar
+  }, [
+    _toString(_ctx.world),
+    (_ctx.ok)
+      ? createVNode(\\"div\\", 0, \\"yes\\")
+      : \\"no\\",
+    _renderList(_ctx.list, (value, index) => {
+      return createVNode(\\"div\\", 0, [createVNode(\\"span\\", 0, _toString(value + index))])
+    })
+  ])
+}"
+`;
index 88416f067b991c1bb5637dc5c862c63e27952447..edc579f643577b67b8bf366ab69c403b23abbeba 100644 (file)
@@ -51,7 +51,7 @@ describe('compiler: codegen', () => {
       imports: [`helperOne`, `helperTwo`]
     })
     const { code } = generate(root, { mode: 'module' })
-    expect(code).toMatch(`import { helperOne, helperTwo } from 'vue'`)
+    expect(code).toMatch(`import { helperOne, helperTwo } from "vue"`)
     expect(code).toMatchSnapshot()
   })
 
index ceb69aeae289a45e52eb608634b855259b5b0210..1d56e92a62ffcccf5b602343a56479d17c3ab008 100644 (file)
 import { compile } from '../src'
 import { SourceMapConsumer, RawSourceMap } from 'source-map'
 
-// Integration tests for parser + transform + codegen
-test('function mode', async () => {
+describe('compiler: integration tests', () => {
   const source = `
 <div id="foo" :class="bar">
   {{ world }}
   <div v-if="ok">yes</div>
   <template v-else>no</template>
-  <div v-for="(i, j) in list"><span>{{ i + j }}</span></div>
+  <div v-for="(value, index) in list"><span>{{ value + index }}</span></div>
 </div>
 `.trim()
-  const { code, map } = compile(source, {
-    sourceMap: true,
-    filename: `foo.vue`
-  })
-
-  expect(code).toMatchSnapshot()
-  expect(map!.sources).toEqual([`foo.vue`])
-  expect(map!.sourcesContent).toEqual([source])
 
-  const consumer = await new SourceMapConsumer(map as RawSourceMap)
+  function getPositionInCode(code: string, token: string) {
+    const generatedOffset = code.indexOf(token)
+    let line = 1
+    let lastNewLinePos = -1
+    for (let i = 0; i < generatedOffset; i++) {
+      if (code.charCodeAt(i) === 10 /* newline char code */) {
+        line++
+        lastNewLinePos = i
+      }
+    }
+    return {
+      line,
+      column:
+        lastNewLinePos === -1
+          ? generatedOffset
+          : generatedOffset - lastNewLinePos - 1
+    }
+  }
 
-  // id=
-  expect(
-    consumer.originalPositionFor({
-      line: 6,
-      column: 6
+  test('function mode', async () => {
+    const { code, map } = compile(source, {
+      sourceMap: true,
+      filename: `foo.vue`
     })
-  ).toMatchObject({
-    line: 1,
-    column: 5
-  })
 
-  // "foo"
-  expect(
-    consumer.originalPositionFor({
-      line: 6,
-      column: 10
-    })
-  ).toMatchObject({
-    line: 1,
-    column: 8
-  })
+    expect(code).toMatch(
+      `const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue`
+    )
 
-  // :class=
-  expect(
-    consumer.originalPositionFor({
-      line: 7,
-      column: 6
-    })
-  ).toMatchObject({
-    line: 1,
-    column: 15
-  })
-  // bar
-  expect(
-    consumer.originalPositionFor({
-      line: 7,
-      column: 13
-    })
-  ).toMatchObject({
-    line: 1,
-    column: 22
-  })
+    expect(code).toMatchSnapshot()
+    expect(map!.sources).toEqual([`foo.vue`])
+    expect(map!.sourcesContent).toEqual([source])
 
-  // {{ world }}
-  expect(
-    consumer.originalPositionFor({
-      line: 9,
-      column: 16
-    })
-  ).toMatchObject({
-    line: 2,
-    column: 2
-  })
+    const consumer = await new SourceMapConsumer(map as RawSourceMap)
 
-  // ok
-  expect(
-    consumer.originalPositionFor({
-      line: 10,
-      column: 6
-    })
-  ).toMatchObject({
-    line: 3,
-    column: 13
-  })
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `id`))
+    ).toMatchObject(getPositionInCode(source, `id`))
 
-  // i
-  expect(
-    consumer.originalPositionFor({
-      line: 13,
-      column: 25
-    })
-  ).toMatchObject({
-    line: 5,
-    column: 15
-  })
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
+    ).toMatchObject(getPositionInCode(source, `"foo"`))
 
-  // j
-  expect(
-    consumer.originalPositionFor({
-      line: 13,
-      column: 28
-    })
-  ).toMatchObject({
-    line: 5,
-    column: 18
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `class:`))
+    ).toMatchObject(getPositionInCode(source, `class=`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `bar`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `world`))
+    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `ok`))
+    ).toMatchObject(getPositionInCode(source, `ok`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `list`))
+    ).toMatchObject(getPositionInCode(source, `list`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value`))
+    ).toMatchObject(getPositionInCode(source, `value`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `index`))
+    ).toMatchObject(getPositionInCode(source, `index`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value + index`))
+    ).toMatchObject(getPositionInCode(source, `{{ value + index }}`))
   })
 
-  // list
-  expect(
-    consumer.originalPositionFor({
-      line: 13,
-      column: 18
+  test('function mode w/ prefixIdentifiers: true', async () => {
+    const { code, map } = compile(source, {
+      sourceMap: true,
+      filename: `foo.vue`,
+      prefixIdentifiers: true
     })
-  ).toMatchObject({
-    line: 5,
-    column: 24
+
+    expect(code).toMatch(`const { createVNode, toString, renderList } = Vue`)
+
+    expect(code).toMatchSnapshot()
+    expect(map!.sources).toEqual([`foo.vue`])
+    expect(map!.sourcesContent).toEqual([source])
+
+    const consumer = await new SourceMapConsumer(map as RawSourceMap)
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `id`))
+    ).toMatchObject(getPositionInCode(source, `id`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
+    ).toMatchObject(getPositionInCode(source, `"foo"`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `class:`))
+    ).toMatchObject(getPositionInCode(source, `class=`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `bar`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `world`))
+    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.world`))
+    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `ok`))
+    ).toMatchObject(getPositionInCode(source, `ok`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.ok`))
+    ).toMatchObject(getPositionInCode(source, `ok`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `list`))
+    ).toMatchObject(getPositionInCode(source, `list`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.list`))
+    ).toMatchObject(getPositionInCode(source, `list`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value`))
+    ).toMatchObject(getPositionInCode(source, `value`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `index`))
+    ).toMatchObject(getPositionInCode(source, `index`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value + index`))
+    ).toMatchObject(getPositionInCode(source, `value + index`))
   })
 
-  // i + j
-  expect(
-    consumer.originalPositionFor({
-      line: 14,
-      column: 81
+  test('module mode', async () => {
+    const { code, map } = compile(source, {
+      mode: 'module',
+      sourceMap: true,
+      filename: `foo.vue`
     })
-  ).toMatchObject({
-    line: 5,
-    column: 36
-  })
-})
 
-test.todo('function mode w/ prefixIdentifiers: true')
+    expect(code).toMatch(
+      `import { createVNode, toString, renderList } from "vue"`
+    )
 
-test.todo('module mode')
+    expect(code).toMatchSnapshot()
+    expect(map!.sources).toEqual([`foo.vue`])
+    expect(map!.sourcesContent).toEqual([source])
 
-test.todo('module mode w/ prefixIdentifiers: true')
+    const consumer = await new SourceMapConsumer(map as RawSourceMap)
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `id`))
+    ).toMatchObject(getPositionInCode(source, `id`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
+    ).toMatchObject(getPositionInCode(source, `"foo"`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `class:`))
+    ).toMatchObject(getPositionInCode(source, `class=`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `bar`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.bar`))
+    ).toMatchObject(getPositionInCode(source, `bar`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `world`))
+    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.world`))
+    ).toMatchObject(getPositionInCode(source, `{{ world }}`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `ok`))
+    ).toMatchObject(getPositionInCode(source, `ok`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.ok`))
+    ).toMatchObject(getPositionInCode(source, `ok`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `list`))
+    ).toMatchObject(getPositionInCode(source, `list`))
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `_ctx.list`))
+    ).toMatchObject(getPositionInCode(source, `list`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value`))
+    ).toMatchObject(getPositionInCode(source, `value`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `index`))
+    ).toMatchObject(getPositionInCode(source, `index`))
+
+    expect(
+      consumer.originalPositionFor(getPositionInCode(code, `value + index`))
+    ).toMatchObject(getPositionInCode(source, `value + index`))
+  })
+})
index 088edecf874fb5a078f8d7b1f6ee344a7f8cfd6a..6129cfdd413e98991101cb369184fef047cee98d 100644 (file)
@@ -29,26 +29,17 @@ function parseWithExpressionTransform(
 describe('compiler: expression transform', () => {
   test('interpolation (root)', () => {
     const node = parseWithExpressionTransform(`{{ foo }}`) as ExpressionNode
-    expect(node.children).toMatchObject([
-      `_ctx.`,
-      {
-        content: `foo`,
-        loc: node.loc
-      }
-    ])
+    expect(node.children).toBeUndefined()
+    expect(node.content).toBe(`_ctx.foo`)
   })
 
   test('interpolation (children)', () => {
-    const node = parseWithExpressionTransform(
+    const el = parseWithExpressionTransform(
       `<div>{{ foo }}</div>`
     ) as ElementNode
-    expect((node.children[0] as ExpressionNode).children).toMatchObject([
-      `_ctx.`,
-      {
-        content: `foo`,
-        loc: node.children[0].loc
-      }
-    ])
+    const node = el.children[0] as ExpressionNode
+    expect(node.children).toBeUndefined()
+    expect(node.content).toBe(`_ctx.foo`)
   })
 
   test('directive value', () => {
@@ -57,13 +48,8 @@ describe('compiler: expression transform', () => {
     ) as ElementNode
     expect((node.props[0] as DirectiveNode).arg!.children).toBeUndefined()
     const exp = (node.props[0] as DirectiveNode).exp!
-    expect(exp.children).toMatchObject([
-      `_ctx.`,
-      {
-        content: `baz`,
-        loc: exp.loc
-      }
-    ])
+    expect(exp.children).toBeUndefined()
+    expect(exp.content).toBe(`_ctx.baz`)
   })
 
   test('dynamic directive arg', () => {
@@ -72,20 +58,10 @@ describe('compiler: expression transform', () => {
     ) as ElementNode
     const arg = (node.props[0] as DirectiveNode).arg!
     const exp = (node.props[0] as DirectiveNode).exp!
-    expect(arg.children).toMatchObject([
-      `_ctx.`,
-      {
-        content: `arg`,
-        loc: arg.loc
-      }
-    ])
-    expect(exp.children).toMatchObject([
-      `_ctx.`,
-      {
-        content: `baz`,
-        loc: exp.loc
-      }
-    ])
+    expect(arg.children).toBeUndefined()
+    expect(arg.content).toBe(`_ctx.arg`)
+    expect(exp.children).toBeUndefined()
+    expect(exp.content).toBe(`_ctx.baz`)
   })
 
   test('should prefix complex expressions', () => {
@@ -94,9 +70,8 @@ describe('compiler: expression transform', () => {
     ) as ExpressionNode
     // should parse into compound expression
     expect(node.children).toMatchObject([
-      `_ctx.`,
       {
-        content: `foo`,
+        content: `_ctx.foo`,
         loc: {
           source: `foo`,
           start: {
@@ -111,9 +86,9 @@ describe('compiler: expression transform', () => {
           }
         }
       },
-      `(_ctx.`,
+      `(`,
       {
-        content: `baz`,
+        content: `_ctx.baz`,
         loc: {
           source: `baz`,
           start: {
@@ -128,9 +103,9 @@ describe('compiler: expression transform', () => {
           }
         }
       },
-      ` + 1, { key: _ctx.`,
+      ` + 1, { key: `,
       {
-        content: `kuz`,
+        content: `_ctx.kuz`,
         loc: {
           source: `kuz`,
           start: {
@@ -151,17 +126,16 @@ describe('compiler: expression transform', () => {
 
   test('should prefix v-if condition', () => {
     const node = parseWithExpressionTransform(`<div v-if="ok"/>`) as IfNode
-    expect(node.branches[0].condition!.children).toMatchObject([
-      `_ctx.`,
-      { content: `ok` }
-    ])
+    expect(node.branches[0].condition!.children).toBeUndefined()
+    expect(node.branches[0].condition!.content).toBe(`_ctx.ok`)
   })
 
   test('should prefix v-for source', () => {
     const node = parseWithExpressionTransform(
       `<div v-for="i in list"/>`
     ) as ForNode
-    expect(node.source.children).toMatchObject([`_ctx.`, { content: `list` }])
+    expect(node.source.children).toBeUndefined()
+    expect(node.source.content).toBe(`_ctx.list`)
   })
 
   test('should not prefix v-for alias', () => {
@@ -177,7 +151,8 @@ describe('compiler: expression transform', () => {
 
     const j = div.children[1] as ExpressionNode
     expect(j.type).toBe(NodeTypes.EXPRESSION)
-    expect(j.children).toMatchObject([`_ctx.`, { content: `j` }])
+    expect(j.children).toBeUndefined()
+    expect(j.content).toBe(`_ctx.j`)
   })
 
   test('should not prefix v-for aliases (multiple)', () => {
@@ -188,12 +163,19 @@ describe('compiler: expression transform', () => {
 
     const exp = div.children[0] as ExpressionNode
     expect(exp.type).toBe(NodeTypes.EXPRESSION)
-    expect(exp.content).toBe(`i + j + k`)
-    expect(exp.children).toBeUndefined()
+    // 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).toMatchObject([`_ctx.`, { content: `l` }])
+    expect(l.children).toBeUndefined()
+    expect(l.content).toBe(`_ctx.l`)
   })
 
   test('should prefix id outside of v-for', () => {
@@ -202,8 +184,8 @@ describe('compiler: expression transform', () => {
     ) as ElementNode
     const exp = node.children[1] as ExpressionNode
     expect(exp.type).toBe(NodeTypes.EXPRESSION)
-    expect(exp.content).toBe(`i`)
-    expect(exp.children).toMatchObject([`_ctx.`, { content: `i` }])
+    expect(exp.children).toBeUndefined()
+    expect(exp.content).toBe(`_ctx.i`)
   })
 
   test('nested v-for', () => {
@@ -217,7 +199,11 @@ describe('compiler: expression transform', () => {
     const innerExp = (innerFor.children[0] as ElementNode)
       .children[0] as ExpressionNode
     expect(innerExp.type).toBe(NodeTypes.EXPRESSION)
-    expect(innerExp.children).toMatchObject([`i + _ctx.`, { content: `j` }])
+    expect(innerExp.children).toMatchObject([
+      { 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
@@ -232,8 +218,12 @@ describe('compiler: expression transform', () => {
       `{{ Math.max(1, 2) }}`
     ) as ExpressionNode
     expect(node.type).toBe(NodeTypes.EXPRESSION)
-    expect(node.content).toBe(`Math.max(1, 2)`)
-    expect(node.children).toBeUndefined()
+    expect(node.children).toMatchObject([
+      { content: `Math` },
+      `.`,
+      { content: `max` },
+      `(1, 2)`
+    ])
   })
 
   test('should not prefix id of a function declaration', () => {
@@ -242,8 +232,10 @@ describe('compiler: expression transform', () => {
     ) as ExpressionNode
     expect(node.type).toBe(NodeTypes.EXPRESSION)
     expect(node.children).toMatchObject([
-      `function foo() { return _ctx.`,
-      { content: `bar` },
+      `function `,
+      { content: `foo` },
+      `() { return `,
+      { content: `_ctx.bar` },
       ` }`
     ])
   })
@@ -254,8 +246,11 @@ describe('compiler: expression transform', () => {
     ) as ExpressionNode
     expect(node.type).toBe(NodeTypes.EXPRESSION)
     expect(node.children).toMatchObject([
-      `foo => foo + _ctx.`,
-      { content: `bar` }
+      { content: `foo` },
+      ` => `,
+      { content: `foo` },
+      ` + `,
+      { content: `_ctx.bar` }
     ])
   })
 
@@ -265,8 +260,8 @@ describe('compiler: expression transform', () => {
     ) as ExpressionNode
     expect(node.type).toBe(NodeTypes.EXPRESSION)
     expect(node.children).toMatchObject([
-      `{ foo: _ctx.`,
-      { content: `bar` },
+      `{ foo: `,
+      { content: `_ctx.bar` },
       ` }`
     ])
   })
@@ -277,10 +272,10 @@ describe('compiler: expression transform', () => {
     ) as ExpressionNode
     expect(node.type).toBe(NodeTypes.EXPRESSION)
     expect(node.children).toMatchObject([
-      `{ [_ctx.`,
-      { content: `foo` },
-      `]: _ctx.`,
-      { content: `bar` },
+      `{ [`,
+      { content: `_ctx.foo` },
+      `]: `,
+      { content: `_ctx.bar` },
       ` }`
     ])
   })
@@ -288,8 +283,8 @@ describe('compiler: expression transform', () => {
   test('should prefix object property shorthand value', () => {
     const node = parseWithExpressionTransform(`{{ { foo } }}`) as ExpressionNode
     expect(node.children).toMatchObject([
-      `{ foo: _ctx.`,
-      { content: `foo` },
+      `{ foo: `,
+      { content: `_ctx.foo` },
       ` }`
     ])
   })
@@ -299,9 +294,11 @@ describe('compiler: expression transform', () => {
       `{{ foo.bar.baz }}`
     ) as ExpressionNode
     expect(node.children).toMatchObject([
-      `_ctx.`,
-      { content: `foo` },
-      `.bar.baz`
+      { content: `_ctx.foo` },
+      `.`,
+      { content: `bar` },
+      `.`,
+      { content: `baz` }
     ])
   })
 
@@ -310,12 +307,11 @@ describe('compiler: expression transform', () => {
       `{{ foo[bar][baz] }}`
     ) as ExpressionNode
     expect(node.children).toMatchObject([
-      `_ctx.`,
-      { content: `foo` },
-      `[_ctx.`,
-      { content: `bar` },
-      `][_ctx.`,
-      { content: 'baz' },
+      { content: `_ctx.foo` },
+      `[`,
+      { content: `_ctx.bar` },
+      `][`,
+      { content: '_ctx.baz' },
       `]`
     ])
   })
index 2f2f131e6b4f48d262384145f3b8b171ec37b4f5..b2b44e7b8266a9c5970600eebc4a9422e34d1aa9 100644 (file)
@@ -25,7 +25,7 @@ function parseWithVOn(
   return ast.children[0] as ElementNode
 }
 
-describe('compiler: transform v-bind', () => {
+describe('compiler: transform v-on', () => {
   test('basic', () => {
     const node = parseWithVOn(`<div v-on:click="onClick"/>`)
     const props = node.codegenNode!.arguments[1] as ObjectExpression
@@ -76,8 +76,8 @@ describe('compiler: transform v-bind', () => {
     const props = node.codegenNode!.arguments[1] as ObjectExpression
     expect(props.properties[0]).toMatchObject({
       key: {
-        content: `"on" + event`,
-        isStatic: false
+        isStatic: false,
+        children: [`"on" + `, { content: `event` }]
       },
       value: {
         content: `handler`,
@@ -94,10 +94,10 @@ describe('compiler: transform v-bind', () => {
     expect(props.properties[0]).toMatchObject({
       key: {
         isStatic: false,
-        children: [`"on" + `, `_ctx.`, { content: `event` }]
+        children: [`"on" + `, { content: `_ctx.event` }]
       },
       value: {
-        content: `handler`,
+        content: `_ctx.handler`,
         isStatic: false
       }
     })
index c851d9567880a6d21b210ff3b639b5f5490247fc..9e69c62eb49cd1b73e73455c28cdbf42f8bf44bc 100644 (file)
@@ -150,6 +150,7 @@ export function generate(
   const context = createCodegenContext(ast, options)
   const { mode, push, prefixIdentifiers, indent, deindent, newline } = context
   const hasImports = ast.imports.length
+  const useWithBlock = !prefixIdentifiers && mode !== 'module'
 
   // preambles
   if (mode === 'function') {
@@ -170,7 +171,7 @@ export function generate(
   } else {
     // generate import statements for helpers
     if (hasImports) {
-      push(`import { ${ast.imports.join(', ')} } from 'vue'\n`)
+      push(`import { ${ast.imports.join(', ')} } from "vue"\n`)
     }
     genHoists(ast.hoists, context)
     push(`export default `)
@@ -180,12 +181,12 @@ export function generate(
   push(`function render() {`)
   indent()
 
-  if (!prefixIdentifiers) {
+  if (useWithBlock) {
     push(`with (this) {`)
     indent()
     // function mode const declarations should be inside with block
     // also they should be renamed to avoid collision with user properties
-    if (mode === 'function' && hasImports) {
+    if (hasImports) {
       push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`)
       newline()
     }
@@ -206,10 +207,12 @@ export function generate(
   // generate the VNode tree expression
   push(`return `)
   genChildren(ast.children, context, true)
-  if (!prefixIdentifiers) {
+
+  if (useWithBlock) {
     deindent()
     push(`}`)
   }
+
   deindent()
   push(`}`)
   return {
index 08424542771ba72b716bd48c3af791dfee5ae325..c077c1a8065e4c98122b848da9d234eb59e627a2 100644 (file)
@@ -70,7 +70,8 @@ export const enum ErrorCodes {
   X_V_ON_NO_EXPRESSION,
 
   // generic errors
-  X_PREFIX_ID_NOT_SUPPORTED
+  X_PREFIX_ID_NOT_SUPPORTED,
+  X_MODULE_MODE_NOT_SUPPORTED
 }
 
 export const errorMessages: { [code: number]: string } = {
@@ -138,5 +139,6 @@ export const errorMessages: { [code: number]: string } = {
   [ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression`,
 
   // generic errors
-  [ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler because it is optimized for payload size.`
+  [ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler.`,
+  [ErrorCodes.X_MODULE_MODE_NOT_SUPPORTED]: `ES module mode is not supported in this build of compiler.`
 }
index 572abeadf86188d565dd0398506ad6a9ef86bdc1..c747c8fec8c362082e4953ec46ba786aacdd219f 100644 (file)
@@ -18,15 +18,21 @@ export function compile(
   template: string | RootNode,
   options: CompilerOptions = {}
 ): CodegenResult {
-  if (__BROWSER__ && options.prefixIdentifiers === false) {
-    ;(options.onError || defaultOnError)(
-      createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED)
-    )
+  if (__BROWSER__) {
+    const onError = options.onError || defaultOnError
+    if (options.prefixIdentifiers === true) {
+      onError(createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED))
+    } else if (options.mode === 'module') {
+      onError(createCompilerError(ErrorCodes.X_MODULE_MODE_NOT_SUPPORTED))
+    }
   }
 
   const ast = isString(template) ? parse(template, options) : template
 
-  const prefixIdentifiers = !__BROWSER__ && options.prefixIdentifiers === true
+  const prefixIdentifiers =
+    !__BROWSER__ &&
+    (options.prefixIdentifiers === true || options.mode === 'module')
+
   transform(ast, {
     ...options,
     prefixIdentifiers,
index be09c6904301c9dbcb9339e04a20950da54da1e3..533108acef9ab2298fa3f61272b058c899d2eab7 100644 (file)
@@ -12,7 +12,7 @@ import { parseScript } from 'meriyah'
 import { walk } from 'estree-walker'
 import { NodeTransform, TransformContext } from '../transform'
 import { NodeTypes, createExpression, ExpressionNode } from '../ast'
-import { Node, Function, Identifier } from 'estree'
+import { Node, Function, Identifier, Property } from 'estree'
 import { advancePositionWithClone } from '../utils'
 export const transformExpression: NodeTransform = (node, context) => {
   if (node.type === NodeTypes.EXPRESSION && !node.isStatic) {
@@ -43,12 +43,15 @@ const simpleIdRE = /^[a-zA-Z$_][\w$]*$/
 const isFunction = (node: Node): node is Function =>
   /Function(Expression|Declaration)$/.test(node.type)
 
+const isPropertyKey = (node: Node, parent: Node) =>
+  parent.type === 'Property' && parent.key === node && !parent.computed
+
 // cache node requires
 let _parseScript: typeof parseScript
 let _walk: typeof walk
 
 interface PrefixMeta {
-  prefix: string
+  prefix?: string
   start: number
   end: number
 }
@@ -72,7 +75,7 @@ export function processExpression(
   // fast path if expression is a simple identifier.
   if (simpleIdRE.test(node.content)) {
     if (!context.identifiers[node.content]) {
-      node.children = [`_ctx.`, createExpression(node.content, false, node.loc)]
+      node.content = `_ctx.${node.content}`
     }
     return
   }
@@ -92,23 +95,23 @@ export function processExpression(
   walk(ast, {
     enter(node: Node & PrefixMeta, parent) {
       if (node.type === 'Identifier') {
-        if (
-          !ids.includes(node) &&
-          !knownIds[node.name] &&
-          shouldPrefix(node, parent)
-        ) {
-          if (
-            parent.type === 'Property' &&
-            parent.value === node &&
-            parent.key === node
-          ) {
-            // property shorthand like { foo }, we need to add the key since we
-            // rewrite the value
-            node.prefix = `${node.name}: _ctx.`
-          } else {
-            node.prefix = `_ctx.`
+        if (!ids.includes(node)) {
+          if (!knownIds[node.name] && shouldPrefix(node, parent)) {
+            if (
+              isPropertyKey(node, parent) &&
+              (parent as Property).value === node
+            ) {
+              // property shorthand like { foo }, we need to add the key since we
+              // rewrite the value
+              node.prefix = `${node.name}: `
+            }
+            node.name = `_ctx.${node.name}`
+            ids.push(node)
+          } else if (!isPropertyKey(node, parent)) {
+            // also generate sub-expressioms for other identifiers for better
+            // source map support. (except for property keys which are static)
+            ids.push(node)
           }
-          ids.push(node)
         }
       } else if (isFunction(node)) {
         // walk function expressions and add its arguments to known identifiers
@@ -147,7 +150,9 @@ export function processExpression(
   ids.forEach((id, i) => {
     const last = ids[i - 1] as any
     const leadingText = full.slice(last ? last.end - 1 : 0, id.start - 1)
-    children.push(leadingText + id.prefix)
+    if (leadingText.length || id.prefix) {
+      children.push(leadingText + (id.prefix || ``))
+    }
     const source = full.slice(id.start - 1, id.end - 1)
     children.push(
       createExpression(id.name, false, {
@@ -191,12 +196,9 @@ function shouldPrefix(identifier: Identifier, parent: Node) {
     ) &&
     // not a key of Property
     !(
-      parent.type === 'Property' &&
-      parent.key === identifier &&
-      // computed keys should be prefixed
-      !parent.computed &&
+      isPropertyKey(identifier, parent) &&
       // shorthand keys should be prefixed
-      !(parent.value === identifier)
+      !((parent as Property).value === identifier)
     ) &&
     // not a property of a MemberExpression
     !(
index 7d88aa7c3c9a09e0d45c9da19453ee710f11a84e..019bd6ed8bf37e8b93de595cda62e43d1c69791b 100644 (file)
@@ -2,7 +2,6 @@ import { DirectiveTransform } from '../transform'
 import { createObjectProperty, createExpression, ExpressionNode } from '../ast'
 import { capitalize } from '@vue/shared'
 import { createCompilerError, ErrorCodes } from '../errors'
-import { isSimpleIdentifier } from '../utils'
 
 // 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
@@ -14,22 +13,18 @@ export const transformOn: DirectiveTransform = (
   if (!exp && !modifiers.length) {
     context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
   }
-  const { content, children, isStatic, loc: argLoc } = arg!
+  const { content, isStatic, loc: argLoc } = arg!
   let eventName: ExpressionNode
   if (isStatic) {
     // static arg
     eventName = createExpression(`on${capitalize(content)}`, true, argLoc)
-  } else if (!children) {
-    // dynamic arg with no rewrite
-    eventName = createExpression(
-      `"on" + ${isSimpleIdentifier(content) ? content : `(${content})`}`,
-      false,
-      argLoc
-    )
   } else {
-    // dynamic arg with ctx prefixing
+    // dynamic arg. turn it into a compound expression.
     eventName = arg!
-    children.unshift(`"on" + `)
+    ;(
+      eventName.children ||
+      (eventName.children = [{ ...eventName, children: undefined }])
+    ).unshift(`"on" + `)
   }
   // TODO .once modifier handling since it is platform agnostic
   // other modifiers are handled in compiler-dom