diff --git a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
index dd8d8311..8331ad7d 100644
--- a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
+++ b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
@@ -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
}"
`;
diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
index f7508225..db2bfb17 100644
--- a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
+++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap
@@ -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))])
+ })
+ ])
+}"
+`;
diff --git a/packages/compiler-core/__tests__/codegen.spec.ts b/packages/compiler-core/__tests__/codegen.spec.ts
index 88416f06..edc579f6 100644
--- a/packages/compiler-core/__tests__/codegen.spec.ts
+++ b/packages/compiler-core/__tests__/codegen.spec.ts
@@ -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()
})
diff --git a/packages/compiler-core/__tests__/compile.spec.ts b/packages/compiler-core/__tests__/compile.spec.ts
index ceb69aea..1d56e92a 100644
--- a/packages/compiler-core/__tests__/compile.spec.ts
+++ b/packages/compiler-core/__tests__/compile.spec.ts
@@ -1,139 +1,227 @@
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 = `
{{ world }}
yes
no
-
{{ i + j }}
+
{{ value + index }}
`.trim()
- const { code, map } = compile(source, {
- sourceMap: true,
- filename: `foo.vue`
+
+ 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
+ }
+ }
+
+ test('function mode', async () => {
+ const { code, map } = compile(source, {
+ sourceMap: true,
+ filename: `foo.vue`
+ })
+
+ expect(code).toMatch(
+ `const { createVNode: _createVNode, toString: _toString, renderList: _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, `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 }}`))
})
- expect(code).toMatchSnapshot()
- expect(map!.sources).toEqual([`foo.vue`])
- expect(map!.sourcesContent).toEqual([source])
-
- const consumer = await new SourceMapConsumer(map as RawSourceMap)
-
- // id=
- expect(
- consumer.originalPositionFor({
- line: 6,
- column: 6
+ test('function mode w/ prefixIdentifiers: true', async () => {
+ const { code, map } = compile(source, {
+ sourceMap: true,
+ filename: `foo.vue`,
+ prefixIdentifiers: true
})
- ).toMatchObject({
- line: 1,
- column: 5
+
+ 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`))
})
- // "foo"
- expect(
- consumer.originalPositionFor({
- line: 6,
- column: 10
+ test('module mode', async () => {
+ const { code, map } = compile(source, {
+ mode: 'module',
+ sourceMap: true,
+ filename: `foo.vue`
})
- ).toMatchObject({
- line: 1,
- column: 8
- })
- // :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).toMatch(
+ `import { createVNode, toString, renderList } from "vue"`
+ )
- // {{ world }}
- expect(
- consumer.originalPositionFor({
- line: 9,
- column: 16
- })
- ).toMatchObject({
- line: 2,
- column: 2
- })
+ expect(code).toMatchSnapshot()
+ expect(map!.sources).toEqual([`foo.vue`])
+ expect(map!.sourcesContent).toEqual([source])
- // ok
- expect(
- consumer.originalPositionFor({
- line: 10,
- column: 6
- })
- ).toMatchObject({
- line: 3,
- column: 13
- })
+ const consumer = await new SourceMapConsumer(map as RawSourceMap)
- // i
- expect(
- consumer.originalPositionFor({
- line: 13,
- column: 25
- })
- ).toMatchObject({
- line: 5,
- column: 15
- })
+ expect(
+ consumer.originalPositionFor(getPositionInCode(code, `id`))
+ ).toMatchObject(getPositionInCode(source, `id`))
- // j
- expect(
- consumer.originalPositionFor({
- line: 13,
- column: 28
- })
- ).toMatchObject({
- line: 5,
- column: 18
- })
+ expect(
+ consumer.originalPositionFor(getPositionInCode(code, `"foo"`))
+ ).toMatchObject(getPositionInCode(source, `"foo"`))
- // list
- expect(
- consumer.originalPositionFor({
- line: 13,
- column: 18
- })
- ).toMatchObject({
- line: 5,
- column: 24
- })
+ expect(
+ consumer.originalPositionFor(getPositionInCode(code, `class:`))
+ ).toMatchObject(getPositionInCode(source, `class=`))
- // i + j
- expect(
- consumer.originalPositionFor({
- line: 14,
- column: 81
- })
- ).toMatchObject({
- line: 5,
- column: 36
+ 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`))
})
})
-
-test.todo('function mode w/ prefixIdentifiers: true')
-
-test.todo('module mode')
-
-test.todo('module mode w/ prefixIdentifiers: true')
diff --git a/packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts b/packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts
index 088edecf..6129cfdd 100644
--- a/packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts
@@ -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(
`{{ foo }}
`
) 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(``) 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(
``
) 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' },
`]`
])
})
diff --git a/packages/compiler-core/__tests__/transforms/vOn.spec.ts b/packages/compiler-core/__tests__/transforms/vOn.spec.ts
index 2f2f131e..b2b44e7b 100644
--- a/packages/compiler-core/__tests__/transforms/vOn.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vOn.spec.ts
@@ -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(``)
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
}
})
diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts
index c851d956..9e69c62e 100644
--- a/packages/compiler-core/src/codegen.ts
+++ b/packages/compiler-core/src/codegen.ts
@@ -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 {
diff --git a/packages/compiler-core/src/errors.ts b/packages/compiler-core/src/errors.ts
index 08424542..c077c1a8 100644
--- a/packages/compiler-core/src/errors.ts
+++ b/packages/compiler-core/src/errors.ts
@@ -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.`
}
diff --git a/packages/compiler-core/src/index.ts b/packages/compiler-core/src/index.ts
index 572abead..c747c8fe 100644
--- a/packages/compiler-core/src/index.ts
+++ b/packages/compiler-core/src/index.ts
@@ -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,
diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts
index be09c690..533108ac 100644
--- a/packages/compiler-core/src/transforms/transformExpression.ts
+++ b/packages/compiler-core/src/transforms/transformExpression.ts
@@ -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
!(
diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts
index 7d88aa7c..019bd6ed 100644
--- a/packages/compiler-core/src/transforms/vOn.ts
+++ b/packages/compiler-core/src/transforms/vOn.ts
@@ -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