diff --git a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap index d5e65e6a7..dd8d8311b 100644 --- a/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap @@ -20,7 +20,7 @@ exports[`compiler: codegen comment 1`] = ` " return function render() { with (this) { - return createVNode(Comment, 0, \\"foo\\") + return _createVNode(_Comment, 0, \\"foo\\") } }" `; @@ -29,7 +29,7 @@ exports[`compiler: codegen compound expression 1`] = ` " return function render() { with (this) { - return toString(_ctx.foo) + return _toString(_ctx.foo) } }" `; @@ -38,16 +38,30 @@ exports[`compiler: codegen forNode 1`] = ` " return function render() { with (this) { - return renderList(list, (v, k, i) => toString(v)) + return _renderList(list, (v, k, i) => { + return _toString(v) + }) } }" `; +exports[`compiler: codegen forNode w/ prefixIdentifiers: true 1`] = ` +" +return function render() { + const _ctx = this + return renderList(list, (v, k, i) => { + return toString(v) + }) +}" +`; + exports[`compiler: codegen forNode w/ skipped key alias 1`] = ` " return function render() { with (this) { - return renderList(list, (v, __key, i) => toString(v)) + return _renderList(list, (v, __key, i) => { + return _toString(v) + }) } }" `; @@ -56,7 +70,9 @@ exports[`compiler: codegen forNode w/ skipped value alias 1`] = ` " return function render() { with (this) { - return renderList(list, (__value, k, i) => toString(v)) + return _renderList(list, (__value, k, i) => { + return _toString(v) + }) } }" `; @@ -65,7 +81,9 @@ exports[`compiler: codegen forNode w/ skipped value and key aliases 1`] = ` " return function render() { with (this) { - return renderList(list, (__value, __key, i) => toString(v)) + return _renderList(list, (__value, __key, i) => { + return _toString(v) + }) } }" `; @@ -74,12 +92,21 @@ exports[`compiler: codegen function mode preamble 1`] = ` "const _Vue = Vue return function render() { with (this) { - const { helperOne, helperTwo } = _Vue + const { helperOne: _helperOne, helperTwo: _helperTwo } = _Vue return null } }" `; +exports[`compiler: codegen function mode preamble w/ prefixIdentifiers: true 1`] = ` +"const { helperOne, helperTwo } = Vue + +return function render() { + const _ctx = this + return null +}" +`; + exports[`compiler: codegen hoists 1`] = ` "const _hoisted_1 = hello const _hoisted_2 = { id: \\"foo\\" } @@ -98,8 +125,8 @@ return function render() { return foo ? \\"foo\\" : (a + b) - ? toString(bye) - : createVNode(Comment, 0, \\"foo\\") + ? _toString(bye) + : _createVNode(_Comment, 0, \\"foo\\") } }" `; @@ -111,7 +138,7 @@ return function render() { return foo ? \\"foo\\" : (a + b) - ? toString(bye) + ? _toString(bye) : null } }" @@ -121,7 +148,7 @@ exports[`compiler: codegen interpolation 1`] = ` " return function render() { with (this) { - return toString(hello) + return _toString(hello) } }" `; @@ -171,9 +198,21 @@ return function render() { with (this) { return [ \\"foo\\", - toString(hello), - createVNode(Comment, 0, \\"foo\\") + _toString(hello), + _createVNode(_Comment, 0, \\"foo\\") ] } }" `; + +exports[`compiler: codegen text + comment + interpolation w/ prefixIdentifiers: true 1`] = ` +" +return function render() { + const _ctx = this + return [ + \\"foo\\", + toString(hello), + createVNode(Comment, 0, \\"foo\\") + ] +}" +`; diff --git a/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap new file mode 100644 index 000000000..f7508225d --- /dev/null +++ b/packages/compiler-core/__tests__/__snapshots__/compile.spec.ts.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`function mode 1`] = ` +"const _Vue = Vue +return function render() { + with (this) { + const { createVNode: _createVNode, toString: _toString, renderList: _renderList } = _Vue + return _createVNode(\\"div\\", { + id: \\"foo\\", + class: bar + }, [ + _toString(world), + ok + ? _createVNode(\\"div\\", 0, \\"yes\\") + : \\"no\\", + _renderList(list, (i, j) => { + return _createVNode(\\"div\\", 0, [_createVNode(\\"span\\", 0, _toString(i + j))]) + }) + ]) + } +}" +`; diff --git a/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap b/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap index 250b91ae0..694b37348 100644 --- a/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap +++ b/packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap @@ -7481,15 +7481,15 @@ Object { "isStatic": false, "loc": Object { "end": Object { - "column": 34, + "column": 33, "line": 1, - "offset": 33, + "offset": 32, }, "source": "\\"{ some: condition }\\"", "start": Object { - "column": 13, + "column": 14, "line": 1, - "offset": 12, + "offset": 13, }, }, "type": 4, @@ -7561,15 +7561,15 @@ Object { "isStatic": false, "loc": Object { "end": Object { - "column": 35, + "column": 34, "line": 2, - "offset": 71, + "offset": 70, }, "source": "\\"{ color: 'red' }\\"", "start": Object { - "column": 17, + "column": 18, "line": 2, - "offset": 53, + "offset": 54, }, }, "type": 4, @@ -7629,13 +7629,13 @@ Object { "isSelfClosing": true, "loc": Object { "end": Object { - "column": 38, + "column": 39, "line": 2, "offset": 73, }, "source": "

", "start": Object { - "column": 2, + "column": 3, "line": 2, "offset": 37, }, @@ -7649,13 +7649,13 @@ Object { "isStatic": true, "loc": Object { "end": Object { - "column": 17, + "column": 18, "line": 2, "offset": 52, }, "source": "style", "start": Object { - "column": 12, + "column": 13, "line": 2, "offset": 47, }, @@ -7670,26 +7670,26 @@ Object { "end": Object { "column": 36, "line": 2, - "offset": 71, + "offset": 70, }, "source": "\\"{ color: 'red' }\\"", "start": Object { - "column": 18, + "column": 20, "line": 2, - "offset": 53, + "offset": 54, }, }, "type": 4, }, "loc": Object { "end": Object { - "column": 36, + "column": 37, "line": 2, "offset": 71, }, "source": "v-bind:style=\\"{ color: 'red' }\\"", "start": Object { - "column": 5, + "column": 6, "line": 2, "offset": 40, }, @@ -7707,13 +7707,13 @@ Object { "content": " a comment with inside it ", "loc": Object { "end": Object { - "column": 42, + "column": 43, "line": 3, "offset": 116, }, "source": "", "start": Object { - "column": 2, + "column": 3, "line": 3, "offset": 76, }, @@ -7767,15 +7767,15 @@ Object { "isStatic": false, "loc": Object { "end": Object { - "column": 34, + "column": 33, "line": 1, - "offset": 33, + "offset": 32, }, "source": "\\"{ some: condition }\\"", "start": Object { - "column": 13, + "column": 14, "line": 1, - "offset": 12, + "offset": 13, }, }, "type": 4, diff --git a/packages/compiler-core/__tests__/codegen.spec.ts b/packages/compiler-core/__tests__/codegen.spec.ts index 5e76e6258..88416f067 100644 --- a/packages/compiler-core/__tests__/codegen.spec.ts +++ b/packages/compiler-core/__tests__/codegen.spec.ts @@ -12,7 +12,12 @@ import { createArrayExpression, ElementNode } from '../src' -import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants' +import { + CREATE_VNODE, + COMMENT, + TO_STRING, + RENDER_LIST +} from '../src/runtimeConstants' const mockLoc: SourceLocation = { source: ``, @@ -56,7 +61,22 @@ describe('compiler: codegen', () => { }) const { code } = generate(root, { mode: 'function' }) expect(code).toMatch(`const _Vue = Vue`) - expect(code).toMatch(`const { helperOne, helperTwo } = _Vue`) + expect(code).toMatch( + `const { helperOne: _helperOne, helperTwo: _helperTwo } = _Vue` + ) + expect(code).toMatchSnapshot() + }) + + test('function mode preamble w/ prefixIdentifiers: true', () => { + const root = createRoot({ + imports: [`helperOne`, `helperTwo`] + }) + const { code } = generate(root, { + mode: 'function', + prefixIdentifiers: true + }) + expect(code).not.toMatch(`const _Vue = Vue`) + expect(code).toMatch(`const { helperOne, helperTwo } = Vue`) expect(code).toMatchSnapshot() }) @@ -121,7 +141,7 @@ describe('compiler: codegen', () => { children: [createExpression(`hello`, false, mockLoc, true)] }) ) - expect(code).toMatch(`return toString(hello)`) + expect(code).toMatch(`return _${TO_STRING}(hello)`) expect(code).toMatchSnapshot() }) @@ -137,7 +157,7 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`return ${CREATE_VNODE}(${COMMENT}, 0, "foo")`) + expect(code).toMatch(`return _${CREATE_VNODE}(_${COMMENT}, 0, "foo")`) expect(code).toMatchSnapshot() }) @@ -163,12 +183,43 @@ describe('compiler: codegen', () => { expect(code).toMatch(` return [ "foo", - toString(hello), - ${CREATE_VNODE}(${COMMENT}, 0, "foo") + _${TO_STRING}(hello), + _${CREATE_VNODE}(_${COMMENT}, 0, "foo") ]`) expect(code).toMatchSnapshot() }) + test('text + comment + interpolation w/ prefixIdentifiers: true', () => { + const { code } = generate( + createRoot({ + children: [ + { + type: NodeTypes.TEXT, + content: 'foo', + isEmpty: false, + loc: mockLoc + }, + createExpression(`hello`, false, mockLoc, true), + { + type: NodeTypes.COMMENT, + content: 'foo', + loc: mockLoc + } + ] + }), + { + prefixIdentifiers: true + } + ) + expect(code).toMatch(` + return [ + "foo", + ${TO_STRING}(hello), + ${CREATE_VNODE}(${COMMENT}, 0, "foo") + ]`) + expect(code).toMatchSnapshot() + }) + test('compound expression', () => { const { code } = generate( createRoot({ @@ -184,7 +235,7 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`return toString(_ctx.foo)`) + expect(code).toMatch(`return _${TO_STRING}(_ctx.foo)`) expect(code).toMatchSnapshot() }) @@ -195,13 +246,11 @@ describe('compiler: codegen', () => { { type: NodeTypes.IF, loc: mockLoc, - isRoot: true, branches: [ { type: NodeTypes.IF_BRANCH, condition: createExpression('foo', false, mockLoc), loc: mockLoc, - isRoot: true, children: [ { type: NodeTypes.TEXT, @@ -215,14 +264,12 @@ describe('compiler: codegen', () => { type: NodeTypes.IF_BRANCH, condition: createExpression('a + b', false, mockLoc), loc: mockLoc, - isRoot: true, children: [createExpression(`bye`, false, mockLoc, true)] }, { type: NodeTypes.IF_BRANCH, condition: undefined, loc: mockLoc, - isRoot: true, children: [ { type: NodeTypes.COMMENT, @@ -240,8 +287,8 @@ describe('compiler: codegen', () => { return foo ? "foo" : (a + b) - ? ${TO_STRING}(bye) - : ${CREATE_VNODE}(${COMMENT}, 0, "foo")`) + ? _${TO_STRING}(bye) + : _${CREATE_VNODE}(_${COMMENT}, 0, "foo")`) expect(code).toMatchSnapshot() }) @@ -252,13 +299,11 @@ describe('compiler: codegen', () => { { type: NodeTypes.IF, loc: mockLoc, - isRoot: true, branches: [ { type: NodeTypes.IF_BRANCH, condition: createExpression('foo', false, mockLoc), loc: mockLoc, - isRoot: true, children: [ { type: NodeTypes.TEXT, @@ -272,7 +317,6 @@ describe('compiler: codegen', () => { type: NodeTypes.IF_BRANCH, condition: createExpression('a + b', false, mockLoc), loc: mockLoc, - isRoot: true, children: [createExpression(`bye`, false, mockLoc, true)] } ] @@ -284,7 +328,7 @@ describe('compiler: codegen', () => { return foo ? "foo" : (a + b) - ? ${TO_STRING}(bye) + ? _${TO_STRING}(bye) : null`) expect(code).toMatchSnapshot() }) @@ -305,7 +349,38 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`renderList(list, (v, k, i) => toString(v))`) + expect(code).toMatch( + `return _${RENDER_LIST}(list, (v, k, i) => { + return _${TO_STRING}(v) + })` + ) + expect(code).toMatchSnapshot() + }) + + test('forNode w/ prefixIdentifiers: true', () => { + const { code } = generate( + createRoot({ + children: [ + { + 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)] + } + ] + }), + { + prefixIdentifiers: true + } + ) + expect(code).toMatch( + `return ${RENDER_LIST}(list, (v, k, i) => { + return ${TO_STRING}(v) + })` + ) expect(code).toMatchSnapshot() }) @@ -325,7 +400,11 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`renderList(list, (__value, k, i) => toString(v))`) + expect(code).toMatch( + `return _${RENDER_LIST}(list, (__value, k, i) => { + return _${TO_STRING}(v) + })` + ) expect(code).toMatchSnapshot() }) @@ -345,7 +424,11 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`renderList(list, (v, __key, i) => toString(v))`) + expect(code).toMatch( + `return _${RENDER_LIST}(list, (v, __key, i) => { + return _${TO_STRING}(v) + })` + ) expect(code).toMatchSnapshot() }) @@ -365,7 +448,11 @@ describe('compiler: codegen', () => { ] }) ) - expect(code).toMatch(`renderList(list, (__value, __key, i) => toString(v))`) + expect(code).toMatch( + `return _${RENDER_LIST}(list, (__value, __key, i) => { + return _${TO_STRING}(v) + })` + ) expect(code).toMatchSnapshot() }) diff --git a/packages/compiler-core/__tests__/compile.spec.ts b/packages/compiler-core/__tests__/compile.spec.ts index d961d0693..ceb69aeae 100644 --- a/packages/compiler-core/__tests__/compile.spec.ts +++ b/packages/compiler-core/__tests__/compile.spec.ts @@ -2,35 +2,138 @@ import { compile } from '../src' import { SourceMapConsumer, RawSourceMap } from 'source-map' // Integration tests for parser + transform + codegen -test('basic source map support', async () => { - const source = `hello {{ world }}` +test('function mode', async () => { + const source = ` +

+ {{ world }} +
yes
+ +
{{ i + j }}
+
+`.trim() const { code, map } = compile(source, { sourceMap: true, filename: `foo.vue` }) - expect(code).toMatch( - `const _Vue = Vue -return function render() { - with (this) { - const { toString } = _Vue - return [ - "hello ", - toString(world) - ] - } -}` - ) + expect(code).toMatchSnapshot() expect(map!.sources).toEqual([`foo.vue`]) expect(map!.sourcesContent).toEqual([source]) const consumer = await new SourceMapConsumer(map as RawSourceMap) - const pos = consumer.originalPositionFor({ - line: 7, - column: 16 - }) - expect(pos).toMatchObject({ + + // id= + expect( + consumer.originalPositionFor({ + line: 6, + column: 6 + }) + ).toMatchObject({ line: 1, - column: 6 + column: 5 + }) + + // "foo" + expect( + consumer.originalPositionFor({ + line: 6, + column: 10 + }) + ).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 + }) + + // {{ world }} + expect( + consumer.originalPositionFor({ + line: 9, + column: 16 + }) + ).toMatchObject({ + line: 2, + column: 2 + }) + + // ok + expect( + consumer.originalPositionFor({ + line: 10, + column: 6 + }) + ).toMatchObject({ + line: 3, + column: 13 + }) + + // i + expect( + consumer.originalPositionFor({ + line: 13, + column: 25 + }) + ).toMatchObject({ + line: 5, + column: 15 + }) + + // j + expect( + consumer.originalPositionFor({ + line: 13, + column: 28 + }) + ).toMatchObject({ + line: 5, + column: 18 + }) + + // list + expect( + consumer.originalPositionFor({ + line: 13, + column: 18 + }) + ).toMatchObject({ + line: 5, + column: 24 + }) + + // i + j + expect( + consumer.originalPositionFor({ + line: 14, + column: 81 + }) + ).toMatchObject({ + line: 5, + column: 36 }) }) + +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__/parse.spec.ts b/packages/compiler-core/__tests__/parse.spec.ts index 692f89bd8..d948f9ef9 100644 --- a/packages/compiler-core/__tests__/parse.spec.ts +++ b/packages/compiler-core/__tests__/parse.spec.ts @@ -894,8 +894,8 @@ describe('compiler: parse', () => { isStatic: false, isInterpolation: false, loc: { - start: { offset: 10, line: 1, column: 11 }, - end: { offset: 13, line: 1, column: 14 }, + start: { offset: 11, line: 1, column: 12 }, + end: { offset: 12, line: 1, column: 13 }, source: '"a"' } }, @@ -1303,25 +1303,27 @@ describe('compiler: parse', () => { test('parse with correct location info', () => { const [foo, bar, but, baz] = parse( - 'foo \n is {{ bar }} but {{ baz }}' + ` +foo + is {{ bar }} but {{ baz }}`.trim() ).children let offset = 0 expect(foo.loc.start).toEqual({ line: 1, column: 1, offset }) offset += foo.loc.source.length - expect(foo.loc.end).toEqual({ line: 2, column: 4, offset }) + expect(foo.loc.end).toEqual({ line: 2, column: 5, offset }) - expect(bar.loc.start).toEqual({ line: 2, column: 4, offset }) + expect(bar.loc.start).toEqual({ line: 2, column: 5, offset }) offset += bar.loc.source.length - expect(bar.loc.end).toEqual({ line: 2, column: 13, offset }) + expect(bar.loc.end).toEqual({ line: 2, column: 14, offset }) - expect(but.loc.start).toEqual({ line: 2, column: 13, offset }) + expect(but.loc.start).toEqual({ line: 2, column: 14, offset }) offset += but.loc.source.length - expect(but.loc.end).toEqual({ line: 2, column: 18, offset }) + expect(but.loc.end).toEqual({ line: 2, column: 19, offset }) - expect(baz.loc.start).toEqual({ line: 2, column: 18, offset }) + expect(baz.loc.start).toEqual({ line: 2, column: 19, offset }) offset += baz.loc.source.length - expect(baz.loc.end).toEqual({ line: 2, column: 27, offset }) + expect(baz.loc.end).toEqual({ line: 2, column: 28, offset }) }) describe('namedCharacterReferences option', () => { diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts index 1723d0f62..eb11cda61 100644 --- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts @@ -3,8 +3,7 @@ import { CompilerOptions, parse, transform, - ErrorCodes, - compile + ErrorCodes } from '../../src' import { transformElement } from '../../src/transforms/transformElement' import { @@ -74,7 +73,7 @@ describe('compiler: element transform', () => { const { root, node } = parseWithElementTransform( `
` ) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) // should hoist the static object expect(root.hoists).toMatchObject([ createStaticObjectMatcher({ @@ -95,7 +94,7 @@ describe('compiler: element transform', () => { const { root, node } = parseWithElementTransform( `
` ) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(root.hoists).toMatchObject([ createStaticObjectMatcher({ id: 'foo' @@ -112,7 +111,7 @@ describe('compiler: element transform', () => { type: NodeTypes.ELEMENT, tag: 'span', codegenNode: { - callee: CREATE_VNODE, + callee: `_${CREATE_VNODE}`, arguments: [`"span"`] } } @@ -122,7 +121,7 @@ describe('compiler: element transform', () => { test('0 placeholder for children with no props', () => { const { node } = parseWithElementTransform(`
`) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments).toMatchObject([ `"div"`, `0`, @@ -131,7 +130,7 @@ describe('compiler: element transform', () => { type: NodeTypes.ELEMENT, tag: 'span', codegenNode: { - callee: CREATE_VNODE, + callee: `_${CREATE_VNODE}`, arguments: [`"span"`] } } @@ -143,7 +142,7 @@ describe('compiler: element transform', () => { const { root, node } = parseWithElementTransform(`
`) // single v-bind doesn't need mergeProps expect(root.imports).not.toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) // should directly use `obj` in props position expect(node.arguments[1]).toMatchObject({ type: NodeTypes.EXPRESSION, @@ -156,10 +155,10 @@ describe('compiler: element transform', () => { `
` ) expect(root.imports).toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: `_${MERGE_PROPS}`, arguments: [ createStaticObjectMatcher({ id: 'foo' @@ -177,10 +176,10 @@ describe('compiler: element transform', () => { `
` ) expect(root.imports).toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: `_${MERGE_PROPS}`, arguments: [ { type: NodeTypes.EXPRESSION, @@ -198,10 +197,10 @@ describe('compiler: element transform', () => { `
` ) expect(root.imports).toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: `_${MERGE_PROPS}`, arguments: [ createStaticObjectMatcher({ id: 'foo' @@ -222,17 +221,17 @@ describe('compiler: element transform', () => { `
` ) expect(root.imports).toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: `_${MERGE_PROPS}`, arguments: [ createStaticObjectMatcher({ id: 'foo' }), { type: NodeTypes.JS_CALL_EXPRESSION, - callee: TO_HANDLERS, + callee: `_${TO_HANDLERS}`, arguments: [ { type: NodeTypes.EXPRESSION, @@ -252,17 +251,17 @@ describe('compiler: element transform', () => { `
` ) expect(root.imports).toContain(MERGE_PROPS) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_CALL_EXPRESSION, - callee: MERGE_PROPS, + callee: `_${MERGE_PROPS}`, arguments: [ createStaticObjectMatcher({ id: 'foo' }), { type: NodeTypes.JS_CALL_EXPRESSION, - callee: TO_HANDLERS, + callee: `_${TO_HANDLERS}`, arguments: [ { type: NodeTypes.EXPRESSION, @@ -301,7 +300,7 @@ describe('compiler: element transform', () => { } } }) - expect(node.callee).toBe(CREATE_VNODE) + expect(node.callee).toBe(`_${CREATE_VNODE}`) expect(node.arguments[1]).toMatchObject({ type: NodeTypes.JS_OBJECT_EXPRESSION, properties: [ @@ -333,11 +332,11 @@ describe('compiler: element transform', () => { expect(root.imports).toContain(RESOLVE_DIRECTIVE) expect(root.statements[0]).toMatch(`${RESOLVE_DIRECTIVE}("foo")`) - expect(node.callee).toBe(APPLY_DIRECTIVES) + expect(node.callee).toBe(`_${APPLY_DIRECTIVES}`) expect(node.arguments).toMatchObject([ { type: NodeTypes.JS_CALL_EXPRESSION, - callee: CREATE_VNODE, + callee: `_${CREATE_VNODE}`, arguments: [ `"div"`, { @@ -388,7 +387,7 @@ describe('compiler: element transform', () => { expect(root.statements[1]).toMatch(`${RESOLVE_DIRECTIVE}("bar")`) expect(root.statements[2]).toMatch(`${RESOLVE_DIRECTIVE}("baz")`) - expect(node.callee).toBe(APPLY_DIRECTIVES) + expect(node.callee).toBe(`_${APPLY_DIRECTIVES}`) expect(node.arguments).toMatchObject([ { type: NodeTypes.JS_CALL_EXPRESSION @@ -467,13 +466,7 @@ describe('compiler: element transform', () => { ]) }) - test('props dedupe', () => { - const { code } = compile( - `
-
` - ) - console.log(code) - }) + test.todo(`props dedupe`) test.todo('slot outlets') }) diff --git a/packages/compiler-core/__tests__/transforms/vBind.spec.ts b/packages/compiler-core/__tests__/transforms/vBind.spec.ts index 016efeedd..fb12b20a5 100644 --- a/packages/compiler-core/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vBind.spec.ts @@ -49,11 +49,11 @@ describe('compiler: transform v-bind', () => { loc: { start: { line: 1, - column: 16 + column: 17 }, end: { line: 1, - column: 20 + column: 19 } } }, diff --git a/packages/compiler-core/__tests__/transforms/vFor.spec.ts b/packages/compiler-core/__tests__/transforms/vFor.spec.ts index 76100fbeb..30f54dce4 100644 --- a/packages/compiler-core/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vFor.spec.ts @@ -212,150 +212,155 @@ describe('compiler: transform v-for', () => { const source = '' const forNode = parseWithForTransform(source) + const itemOffset = source.indexOf('item') expect(forNode.valueAlias!.content).toBe('item') - expect(forNode.valueAlias!.loc.start.offset).toBe( - source.indexOf('item') - 1 - ) + expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset) expect(forNode.valueAlias!.loc.start.line).toBe(1) - expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item')) + expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1) expect(forNode.valueAlias!.loc.end.line).toBe(1) expect(forNode.valueAlias!.loc.end.column).toBe( - source.indexOf('item') + 4 + itemOffset + 1 + `item`.length ) + const itemsOffset = source.indexOf('items') expect(forNode.source.content).toBe('items') - expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1) + expect(forNode.source.loc.start.offset).toBe(itemsOffset) expect(forNode.source.loc.start.line).toBe(1) - expect(forNode.source.loc.start.column).toBe(source.indexOf('items')) + expect(forNode.source.loc.start.column).toBe(itemsOffset + 1) expect(forNode.source.loc.end.line).toBe(1) - expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5) + expect(forNode.source.loc.end.column).toBe( + itemsOffset + 1 + `items`.length + ) }) test('bracketed value', () => { const source = '' const forNode = parseWithForTransform(source) + const itemOffset = source.indexOf('item') expect(forNode.valueAlias!.content).toBe('item') - expect(forNode.valueAlias!.loc.start.offset).toBe( - source.indexOf('item') - 1 - ) + expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset) expect(forNode.valueAlias!.loc.start.line).toBe(1) - expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item')) + expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1) expect(forNode.valueAlias!.loc.end.line).toBe(1) expect(forNode.valueAlias!.loc.end.column).toBe( - source.indexOf('item') + 4 + itemOffset + 1 + `item`.length ) + const itemsOffset = source.indexOf('items') expect(forNode.source.content).toBe('items') - expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1) + expect(forNode.source.loc.start.offset).toBe(itemsOffset) expect(forNode.source.loc.start.line).toBe(1) - expect(forNode.source.loc.start.column).toBe(source.indexOf('items')) + expect(forNode.source.loc.start.column).toBe(itemsOffset + 1) expect(forNode.source.loc.end.line).toBe(1) - expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5) + expect(forNode.source.loc.end.column).toBe( + itemsOffset + 1 + `items`.length + ) }) test('de-structured value', () => { const source = '' const forNode = parseWithForTransform(source) + const valueIndex = source.indexOf('{ id, key }') expect(forNode.valueAlias!.content).toBe('{ id, key }') - expect(forNode.valueAlias!.loc.start.offset).toBe( - source.indexOf('{ id, key }') - 1 - ) + expect(forNode.valueAlias!.loc.start.offset).toBe(valueIndex) expect(forNode.valueAlias!.loc.start.line).toBe(1) - expect(forNode.valueAlias!.loc.start.column).toBe( - source.indexOf('{ id, key }') - ) + expect(forNode.valueAlias!.loc.start.column).toBe(valueIndex + 1) expect(forNode.valueAlias!.loc.end.line).toBe(1) expect(forNode.valueAlias!.loc.end.column).toBe( - source.indexOf('{ id, key }') + '{ id, key }'.length + valueIndex + 1 + '{ id, key }'.length ) + const itemsOffset = source.indexOf('items') expect(forNode.source.content).toBe('items') - expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1) + expect(forNode.source.loc.start.offset).toBe(itemsOffset) expect(forNode.source.loc.start.line).toBe(1) - expect(forNode.source.loc.start.column).toBe(source.indexOf('items')) + expect(forNode.source.loc.start.column).toBe(itemsOffset + 1) expect(forNode.source.loc.end.line).toBe(1) - expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5) + expect(forNode.source.loc.end.column).toBe( + itemsOffset + 1 + `items`.length + ) }) test('bracketed value, key, index', () => { const source = '' const forNode = parseWithForTransform(source) + const itemOffset = source.indexOf('item') expect(forNode.valueAlias!.content).toBe('item') - expect(forNode.valueAlias!.loc.start.offset).toBe( - source.indexOf('item') - 1 - ) + expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset) expect(forNode.valueAlias!.loc.start.line).toBe(1) - expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item')) + expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1) expect(forNode.valueAlias!.loc.end.line).toBe(1) expect(forNode.valueAlias!.loc.end.column).toBe( - source.indexOf('item') + 4 + itemOffset + 1 + `item`.length ) + const keyOffset = source.indexOf('key') expect(forNode.keyAlias!.content).toBe('key') - expect(forNode.keyAlias!.loc.start.offset).toBe(source.indexOf('key') - 1) + expect(forNode.keyAlias!.loc.start.offset).toBe(keyOffset) expect(forNode.keyAlias!.loc.start.line).toBe(1) - expect(forNode.keyAlias!.loc.start.column).toBe(source.indexOf('key')) + expect(forNode.keyAlias!.loc.start.column).toBe(keyOffset + 1) expect(forNode.keyAlias!.loc.end.line).toBe(1) - expect(forNode.keyAlias!.loc.end.column).toBe(source.indexOf('key') + 3) + expect(forNode.keyAlias!.loc.end.column).toBe( + keyOffset + 1 + `key`.length + ) + const indexOffset = source.indexOf('index') expect(forNode.objectIndexAlias!.content).toBe('index') - expect(forNode.objectIndexAlias!.loc.start.offset).toBe( - source.indexOf('index') - 1 - ) + expect(forNode.objectIndexAlias!.loc.start.offset).toBe(indexOffset) expect(forNode.objectIndexAlias!.loc.start.line).toBe(1) - expect(forNode.objectIndexAlias!.loc.start.column).toBe( - source.indexOf('index') - ) + expect(forNode.objectIndexAlias!.loc.start.column).toBe(indexOffset + 1) expect(forNode.objectIndexAlias!.loc.end.line).toBe(1) expect(forNode.objectIndexAlias!.loc.end.column).toBe( - source.indexOf('index') + 5 + indexOffset + 1 + `index`.length ) + const itemsOffset = source.indexOf('items') expect(forNode.source.content).toBe('items') - expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1) + expect(forNode.source.loc.start.offset).toBe(itemsOffset) expect(forNode.source.loc.start.line).toBe(1) - expect(forNode.source.loc.start.column).toBe(source.indexOf('items')) + expect(forNode.source.loc.start.column).toBe(itemsOffset + 1) expect(forNode.source.loc.end.line).toBe(1) - expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5) + expect(forNode.source.loc.end.column).toBe( + itemsOffset + 1 + `items`.length + ) }) test('skipped key', () => { const source = '' const forNode = parseWithForTransform(source) + const itemOffset = source.indexOf('item') expect(forNode.valueAlias!.content).toBe('item') - expect(forNode.valueAlias!.loc.start.offset).toBe( - source.indexOf('item') - 1 - ) + expect(forNode.valueAlias!.loc.start.offset).toBe(itemOffset) expect(forNode.valueAlias!.loc.start.line).toBe(1) - expect(forNode.valueAlias!.loc.start.column).toBe(source.indexOf('item')) + expect(forNode.valueAlias!.loc.start.column).toBe(itemOffset + 1) expect(forNode.valueAlias!.loc.end.line).toBe(1) expect(forNode.valueAlias!.loc.end.column).toBe( - source.indexOf('item') + 4 + itemOffset + 1 + `item`.length ) + const indexOffset = source.indexOf('index') expect(forNode.objectIndexAlias!.content).toBe('index') - expect(forNode.objectIndexAlias!.loc.start.offset).toBe( - source.indexOf('index') - 1 - ) + expect(forNode.objectIndexAlias!.loc.start.offset).toBe(indexOffset) expect(forNode.objectIndexAlias!.loc.start.line).toBe(1) - expect(forNode.objectIndexAlias!.loc.start.column).toBe( - source.indexOf('index') - ) + expect(forNode.objectIndexAlias!.loc.start.column).toBe(indexOffset + 1) expect(forNode.objectIndexAlias!.loc.end.line).toBe(1) expect(forNode.objectIndexAlias!.loc.end.column).toBe( - source.indexOf('index') + 5 + indexOffset + 1 + `index`.length ) + const itemsOffset = source.indexOf('items') expect(forNode.source.content).toBe('items') - expect(forNode.source.loc.start.offset).toBe(source.indexOf('items') - 1) + expect(forNode.source.loc.start.offset).toBe(itemsOffset) expect(forNode.source.loc.start.line).toBe(1) - expect(forNode.source.loc.start.column).toBe(source.indexOf('items')) + expect(forNode.source.loc.start.column).toBe(itemsOffset + 1) expect(forNode.source.loc.end.line).toBe(1) - expect(forNode.source.loc.end.column).toBe(source.indexOf('items') + 5) + expect(forNode.source.loc.end.column).toBe( + itemsOffset + 1 + `items`.length + ) }) }) }) diff --git a/packages/compiler-core/__tests__/transforms/vIf.spec.ts b/packages/compiler-core/__tests__/transforms/vIf.spec.ts index 2c6ddc463..a9da46889 100644 --- a/packages/compiler-core/__tests__/transforms/vIf.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vIf.spec.ts @@ -29,9 +29,7 @@ describe('compiler: transform v-if', () => { test('basic v-if', () => { const node = parseWithIfTransform(`
`) expect(node.type).toBe(NodeTypes.IF) - expect(node.isRoot).toBe(true) expect(node.branches.length).toBe(1) - expect(node.branches[0].isRoot).toBe(true) expect(node.branches[0].condition!.content).toBe(`ok`) expect(node.branches[0].children.length).toBe(1) expect(node.branches[0].children[0].type).toBe(NodeTypes.ELEMENT) diff --git a/packages/compiler-core/__tests__/transforms/vOn.spec.ts b/packages/compiler-core/__tests__/transforms/vOn.spec.ts index 713b34be5..2f2f131e6 100644 --- a/packages/compiler-core/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vOn.spec.ts @@ -50,11 +50,11 @@ describe('compiler: transform v-bind', () => { loc: { start: { line: 1, - column: 17 + column: 18 }, end: { line: 1, - column: 26 + column: 25 } } }, diff --git a/packages/compiler-core/__tests__/utils.spec.ts b/packages/compiler-core/__tests__/utils.spec.ts index c83ce2261..b5d1bea04 100644 --- a/packages/compiler-core/__tests__/utils.spec.ts +++ b/packages/compiler-core/__tests__/utils.spec.ts @@ -28,7 +28,7 @@ describe('advancePositionWithClone', () => { const pos = p(1, 1, 0) const newPos = advancePositionWithClone(pos, 'foo\nbar\nbaz', 10) - expect(newPos.column).toBe(2) + expect(newPos.column).toBe(3) expect(newPos.line).toBe(3) expect(newPos.offset).toBe(10) }) @@ -62,7 +62,7 @@ describe('getInnerRange', () => { expect(loc2.start.column).toBe(1) expect(loc2.start.line).toBe(2) expect(loc2.start.offset).toBe(4) - expect(loc2.end.column).toBe(3) + expect(loc2.end.column).toBe(4) expect(loc2.end.line).toBe(2) expect(loc2.end.offset).toBe(7) }) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 786cdfd4d..d9b3cbc4c 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -118,14 +118,12 @@ export interface ExpressionNode extends Node { export interface IfNode extends Node { type: NodeTypes.IF branches: IfBranchNode[] - isRoot: boolean } export interface IfBranchNode extends Node { type: NodeTypes.IF_BRANCH condition: ExpressionNode | undefined // else children: ChildNode[] - isRoot: boolean } export interface ForNode extends Node { diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 84e5a5d74..c851d9567 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -65,6 +65,7 @@ export interface CodegenContext extends Required { offset: number indentLevel: number map?: SourceMapGenerator + helper(name: string): string push(code: string, node?: CodegenNode): void indent(): void deindent(withoutNewLine?: boolean): void @@ -98,11 +99,14 @@ function createCodegenContext( ? undefined : new (require('source-map')).SourceMapGenerator(), + helper(name) { + return prefixIdentifiers ? name : `_${name}` + }, push(code, node?: CodegenNode) { context.code += code if (context.map) { if (node) { - context.map.addMapping({ + const mapping = { source: context.filename, original: { line: node.loc.start.line, @@ -112,9 +116,10 @@ function createCodegenContext( line: context.line, column: context.column - 1 } - }) + } + context.map.addMapping(mapping) } - advancePositionWithMutation(context, code, code.length) + advancePositionWithMutation(context, code) } }, indent() { @@ -144,7 +149,7 @@ export function generate( ): CodegenResult { const context = createCodegenContext(ast, options) const { mode, push, prefixIdentifiers, indent, deindent, newline } = context - const imports = ast.imports.join(', ') + const hasImports = ast.imports.length // preambles if (mode === 'function') { @@ -152,9 +157,9 @@ export function generate( // In prefix mode, we place the const declaration at top so it's done // only once; But if we not prefixing, we place the decalration inside the // with block so it doesn't incur the `in` check cost for every helper access. - if (imports) { + if (hasImports) { if (prefixIdentifiers) { - push(`const { ${imports} } = Vue\n`) + push(`const { ${ast.imports.join(', ')} } = Vue\n`) } else { // save Vue in a separate variable to avoid collision push(`const _Vue = Vue`) @@ -164,8 +169,8 @@ export function generate( push(`return `) } else { // generate import statements for helpers - if (imports) { - push(`import { ${imports} } from 'vue'\n`) + if (hasImports) { + push(`import { ${ast.imports.join(', ')} } from 'vue'\n`) } genHoists(ast.hoists, context) push(`export default `) @@ -179,8 +184,9 @@ export function generate( push(`with (this) {`) indent() // function mode const declarations should be inside with block - if (mode === 'function' && imports) { - push(`const { ${imports} } = _Vue`) + // also they should be renamed to avoid collision with user properties + if (mode === 'function' && hasImports) { + push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`) newline() } } else { @@ -199,7 +205,7 @@ export function generate( // generate the VNode tree expression push(`return `) - genChildren(ast.children, context, true /* asRoot */) + genChildren(ast.children, context, true) if (!prefixIdentifiers) { deindent() push(`}`) @@ -223,13 +229,12 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) { } // This will generate a single vnode call if: -// - The list has length === 1, AND: -// - This is a root node, OR: -// - The only child is a text or expression. +// - 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. function genChildren( children: ChildNode[], context: CodegenContext, - asRoot: boolean = false + allowSingle: boolean = false ) { if (!children.length) { return context.push(`null`) @@ -237,7 +242,7 @@ function genChildren( const child = children[0] if ( children.length === 1 && - (asRoot || + (allowSingle || child.type === NodeTypes.TEXT || child.type == NodeTypes.EXPRESSION) ) { @@ -336,10 +341,10 @@ function genText(node: TextNode | ExpressionNode, context: CodegenContext) { } function genExpression(node: ExpressionNode, context: CodegenContext) { - const { push } = context + const { push, helper } = context const { content, children, isStatic, isInterpolation } = node if (isInterpolation) { - push(`${TO_STRING}(`) + push(`${helper(TO_STRING)}(`) } if (children) { genCompoundExpression(node, context) @@ -383,8 +388,11 @@ function genCompoundExpression(node: ExpressionNode, context: CodegenContext) { function genComment(node: CommentNode, context: CodegenContext) { if (__DEV__) { - context.push( - `${CREATE_VNODE}(${COMMENT}, 0, ${JSON.stringify(node.content)})`, + const { push, helper } = context + push( + `${helper(CREATE_VNODE)}(${helper(COMMENT)}, 0, ${JSON.stringify( + node.content + )})`, node ) } @@ -396,7 +404,7 @@ function genIf(node: IfNode, context: CodegenContext) { } function genIfBranch( - { condition, children, isRoot }: IfBranchNode, + { condition, children }: IfBranchNode, branches: IfBranchNode[], nextIndex: number, context: CodegenContext @@ -411,7 +419,7 @@ function genIfBranch( indent() context.indentLevel++ push(`? `) - genChildren(children, context, isRoot) + genChildren(children, context, true) context.indentLevel-- newline() push(`: `) @@ -424,14 +432,14 @@ function genIfBranch( } else { // v-else __DEV__ && assert(nextIndex === branches.length) - genChildren(children, context, isRoot) + genChildren(children, context, true) } } function genFor(node: ForNode, context: CodegenContext) { - const { push } = context + const { push, helper, indent, deindent } = context const { source, keyAlias, valueAlias, objectIndexAlias, children } = node - push(`${RENDER_LIST}(`, node) + push(`${helper(RENDER_LIST)}(`, node) genExpression(source, context) push(`, (`) if (valueAlias) { @@ -455,9 +463,12 @@ function genFor(node: ForNode, context: CodegenContext) { push(`, `) genExpression(objectIndexAlias, context) } - push(`) => `) - genChildren(children, context) - push(`)`) + push(`) => {`) + indent() + push(`return `) + genChildren(children, context, true) + deindent() + push(`})`) } // JavaScript diff --git a/packages/compiler-core/src/parse.ts b/packages/compiler-core/src/parse.ts index e80480fba..b153bab0f 100644 --- a/packages/compiler-core/src/parse.ts +++ b/packages/compiler-core/src/parse.ts @@ -479,7 +479,14 @@ function parseAttribute( advanceBy(context, name.length) // Value - let value: { content: string; loc: SourceLocation } | undefined = undefined + let value: + | { + content: string + isQuoted: boolean + loc: SourceLocation + } + | undefined = undefined + if (/^[\t\r\n\f ]*=/.test(context.source)) { advanceSpaces(context) advanceBy(context, 1) @@ -530,6 +537,13 @@ function parseAttribute( } } + if (value && value.isQuoted) { + const valueLoc = value.loc + valueLoc.start.offset++ + valueLoc.start.column++ + valueLoc.end = advancePositionWithClone(valueLoc.start, value.content) + } + return { type: NodeTypes.DIRECTIVE, name: @@ -567,13 +581,20 @@ function parseAttribute( function parseAttributeValue( context: ParserContext -): { content: string; loc: SourceLocation } | undefined { +): + | { + content: string + isQuoted: boolean + loc: SourceLocation + } + | undefined { const start = getCursor(context) let content: string - if (/^["']/.test(context.source)) { + const quote = context.source[0] + const isQuoted = quote === `"` || quote === `'` + if (isQuoted) { // Quoted value. - const quote = context.source[0] advanceBy(context, 1) const endIndex = context.source.indexOf(quote) @@ -605,7 +626,7 @@ function parseAttributeValue( content = parseTextData(context, match[0].length, TextModes.ATTRIBUTE_VALUE) } - return { content, loc: getSelection(context, start) } + return { content, isQuoted, loc: getSelection(context, start) } } function parseInterpolation( diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index ad62b402b..a7714b5c3 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -59,6 +59,7 @@ export interface TransformContext extends Required { parent: ParentNode childIndex: number currentNode: ChildNode | null + helper(name: string): string replaceNode(node: ChildNode): void removeNode(node?: ChildNode): void onNodeRemoved: () => void @@ -89,6 +90,10 @@ function createTransformContext( parent: root, childIndex: 0, currentNode: null, + helper(name) { + context.imports.add(name) + return prefixIdentifiers ? name : `_${name}` + }, replaceNode(node) { /* istanbul ignore if */ if (__DEV__ && !context.currentNode) { @@ -195,15 +200,15 @@ export function traverseNode(node: ChildNode, context: TransformContext) { switch (node.type) { case NodeTypes.COMMENT: - context.imports.add(CREATE_VNODE) + context.helper(CREATE_VNODE) // inject import for the Comment symbol, which is needed for creating // comment nodes with `createVNode` - context.imports.add(COMMENT) + context.helper(COMMENT) break case NodeTypes.EXPRESSION: // no need to traverse, but we need to inject toString helper if (node.isInterpolation) { - context.imports.add(TO_STRING) + context.helper(TO_STRING) } break diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index a28a9e1ea..e346afbd1 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -42,12 +42,11 @@ export const transformElement: NodeTransform = (node, context) => { let componentIdentifier: string | undefined if (isComponent) { - context.imports.add(RESOLVE_COMPONENT) componentIdentifier = `_component_${toValidId(node.tag)}` context.statements.push( - `const ${componentIdentifier} = ${RESOLVE_COMPONENT}(${JSON.stringify( - node.tag - )})` + `const ${componentIdentifier} = ${context.helper( + RESOLVE_COMPONENT + )}(${JSON.stringify(node.tag)})` ) } @@ -70,13 +69,15 @@ export const transformElement: NodeTransform = (node, context) => { } const { loc } = node - context.imports.add(CREATE_VNODE) - const vnode = createCallExpression(CREATE_VNODE, args, loc) + const vnode = createCallExpression( + context.helper(CREATE_VNODE), + args, + loc + ) if (runtimeDirectives && runtimeDirectives.length) { - context.imports.add(APPLY_DIRECTIVES) node.codegenNode = createCallExpression( - APPLY_DIRECTIVES, + context.helper(APPLY_DIRECTIVES), [ vnode, createArrayExpression( @@ -147,11 +148,10 @@ function buildProps( mergeArgs.push(exp) } else { // v-on="obj" -> toHandlers(obj) - context.imports.add(TO_HANDLERS) mergeArgs.push({ type: NodeTypes.JS_CALL_EXPRESSION, loc, - callee: TO_HANDLERS, + callee: context.helper(TO_HANDLERS), arguments: [exp] }) } @@ -197,8 +197,11 @@ function buildProps( ) } if (mergeArgs.length > 1) { - context.imports.add(MERGE_PROPS) - propsExpression = createCallExpression(MERGE_PROPS, mergeArgs, elementLoc) + propsExpression = createCallExpression( + context.helper(MERGE_PROPS), + mergeArgs, + elementLoc + ) } else { // single v-bind with nothing else - no need for a mergeProps call propsExpression = mergeArgs[0] @@ -285,12 +288,12 @@ function createDirectiveArgs( dir: DirectiveNode, context: TransformContext ): ArrayExpression { - // inject import for `resolveDirective` - context.imports.add(RESOLVE_DIRECTIVE) // inject statement for resolving directive const dirIdentifier = `_directive_${toValidId(dir.name)}` context.statements.push( - `const ${dirIdentifier} = ${RESOLVE_DIRECTIVE}(${JSON.stringify(dir.name)})` + `const ${dirIdentifier} = ${context.helper( + RESOLVE_DIRECTIVE + )}(${JSON.stringify(dir.name)})` ) const dirArgs: ArrayExpression['elements'] = [dirIdentifier] const { loc } = dir diff --git a/packages/compiler-core/src/transforms/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts index b1cf727aa..04c6bf34d 100644 --- a/packages/compiler-core/src/transforms/vFor.ts +++ b/packages/compiler-core/src/transforms/vFor.ts @@ -24,7 +24,7 @@ export const transformFor = createStructuralDirectiveTransform( const parseResult = parseForExpression(dir.exp, context) if (parseResult) { - context.imports.add(RENDER_LIST) + context.helper(RENDER_LIST) const { source, value, key, index } = parseResult context.replaceNode({ diff --git a/packages/compiler-core/src/transforms/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts index 70120d5f1..1d8fe08aa 100644 --- a/packages/compiler-core/src/transforms/vIf.ts +++ b/packages/compiler-core/src/transforms/vIf.ts @@ -19,14 +19,10 @@ export const transformIf = createStructuralDirectiveTransform( processExpression(dir.exp, context) } if (dir.name === 'if') { - // check if this v-if is root - so that in codegen we can avoid generating - // arrays for each branch - const isRoot = context.parent === context.root context.replaceNode({ type: NodeTypes.IF, loc: node.loc, - branches: [createIfBranch(node, dir, isRoot)], - isRoot + branches: [createIfBranch(node, dir)] }) } else { // locate the adjacent v-if @@ -43,7 +39,7 @@ export const transformIf = createStructuralDirectiveTransform( if (sibling && sibling.type === NodeTypes.IF) { // move the node to the if node's branches context.removeNode() - const branch = createIfBranch(node, dir, sibling.isRoot) + const branch = createIfBranch(node, dir) if (__DEV__ && comments.length) { branch.children = [...comments, ...branch.children] } @@ -67,16 +63,11 @@ export const transformIf = createStructuralDirectiveTransform( } ) -function createIfBranch( - node: ElementNode, - dir: DirectiveNode, - isRoot: boolean -): IfBranchNode { +function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode { return { type: NodeTypes.IF_BRANCH, loc: node.loc, condition: dir.name === 'else' ? undefined : dir.exp, - children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node], - isRoot + children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node] } } diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts index f857f629e..2745b041c 100644 --- a/packages/compiler-core/src/utils.ts +++ b/packages/compiler-core/src/utils.ts @@ -31,7 +31,7 @@ export function getInnerRange( export function advancePositionWithClone( pos: Position, source: string, - numberOfCharacters: number + numberOfCharacters: number = source.length ): Position { return advancePositionWithMutation({ ...pos }, source, numberOfCharacters) } @@ -41,7 +41,7 @@ export function advancePositionWithClone( export function advancePositionWithMutation( pos: Position, source: string, - numberOfCharacters: number + numberOfCharacters: number = source.length ): Position { let linesCount = 0 let lastNewLinePos = -1 @@ -57,7 +57,7 @@ export function advancePositionWithMutation( pos.column = lastNewLinePos === -1 ? pos.column + numberOfCharacters - : Math.max(1, numberOfCharacters - lastNewLinePos - 1) + : Math.max(1, numberOfCharacters - lastNewLinePos) return pos }