]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler): use symbols for runtime helpers
authorEvan You <yyx990803@gmail.com>
Sat, 5 Oct 2019 21:18:25 +0000 (17:18 -0400)
committerEvan You <yyx990803@gmail.com>
Sun, 6 Oct 2019 02:48:13 +0000 (22:48 -0400)
26 files changed:
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__/testUtils.ts
packages/compiler-core/__tests__/transform.spec.ts
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/__tests__/transforms/transformSlotOutlet.spec.ts
packages/compiler-core/__tests__/transforms/vBind.spec.ts
packages/compiler-core/__tests__/transforms/vFor.spec.ts
packages/compiler-core/__tests__/transforms/vIf.spec.ts
packages/compiler-core/__tests__/transforms/vSlot.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/runtimeConstants.ts [deleted file]
packages/compiler-core/src/runtimeHelpers.ts [new file with mode: 0644]
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/hoistStatic.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/compiler-core/src/transforms/transformSlotOutlet.ts
packages/compiler-core/src/transforms/vBind.ts
packages/compiler-core/src/transforms/vFor.ts
packages/compiler-core/src/transforms/vIf.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/compiler-core/src/utils.ts
packages/shared/src/index.ts

index ec0e3e8b586029b90fe0f0d3f9022ed74d7ee2c4..4e70c5918b647d693f014dba0ac9181c51e043a0 100644 (file)
@@ -17,15 +17,15 @@ exports[`compiler: codegen Element (callExpression + objectExpression + arrayExp
 "
 return function render() {
   with (this) {
-    return createVNode(\\"div\\", {
+    return _createVNode(\\"div\\", {
       id: \\"foo\\",
       [prop]: bar,
       [foo + bar]: bar
     }, [
-      createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
+      _createVNode(\\"p\\", { \\"some-key\\": \\"foo\\" })
     ], [
       foo,
-      createVNode(\\"p\\")
+      _createVNode(\\"p\\")
     ])
   }
 }"
@@ -40,6 +40,19 @@ return function render() {
 }"
 `;
 
+exports[`compiler: codegen assets 1`] = `
+"
+return function render() {
+  with (this) {
+    const _component_Foo = _resolveComponent(\\"Foo\\")
+    const _component_barbaz = _resolveComponent(\\"bar-baz\\")
+    const _directive_my_dir = _resolveDirective(\\"my_dir\\")
+    
+    return null
+  }
+}"
+`;
+
 exports[`compiler: codegen comment 1`] = `
 "
 return function render() {
@@ -72,7 +85,7 @@ exports[`compiler: codegen function mode preamble 1`] = `
 
 return function render() {
   with (this) {
-    const { helperOne: _helperOne, helperTwo: _helperTwo } = _Vue
+    const { createVNode: _createVNode, resolveDirective: _resolveDirective } = _Vue
     
     return null
   }
@@ -80,7 +93,7 @@ return function render() {
 `;
 
 exports[`compiler: codegen function mode preamble w/ prefixIdentifiers: true 1`] = `
-"const { helperOne, helperTwo } = Vue
+"const { createVNode, resolveDirective } = Vue
 
 return function render() {
   const _ctx = this
@@ -119,7 +132,7 @@ return function render() {
 `;
 
 exports[`compiler: codegen module mode preamble 1`] = `
-"import { helperOne, helperTwo } from \\"vue\\"
+"import { createVNode, resolveDirective } from \\"vue\\"
 
 export default function render() {
   const _ctx = this
@@ -135,18 +148,6 @@ return function render() {
 }"
 `;
 
-exports[`compiler: codegen statements 1`] = `
-"
-return function render() {
-  with (this) {
-    const a = 1
-    const b = 2
-    
-    return null
-  }
-}"
-`;
-
 exports[`compiler: codegen static text 1`] = `
 "
 return function render() {
index d9ade929bac76119fd196c988bde9d352cbb3b00..5975fc392f5af6ae4d04d59eb80c82b671cc4ce2 100644 (file)
@@ -56,13 +56,13 @@ export default function render() {
     id: \\"foo\\",
     class: _ctx.bar.baz
   }, [
-    _toString(_ctx.world.burn()),
+    toString(_ctx.world.burn()),
     (openBlock(), (_ctx.ok)
       ? createBlock(\\"div\\", { key: 0 }, \\"yes\\")
       : createBlock(Fragment, { key: 1 }, [\\"no\\"])),
     (openBlock(), createBlock(Fragment, null, renderList(_ctx.list, (value, index) => {
       return (openBlock(), createBlock(\\"div\\", null, [
-        createVNode(\\"span\\", null, _toString(value + index), 1 /* TEXT */)
+        createVNode(\\"span\\", null, toString(value + index), 1 /* TEXT */)
       ]))
     }), 128 /* UNKEYED_FRAGMENT */))
   ], 2 /* CLASS */))
index 49311c8f31382c0d504453b60fa8cf37a4aa4ac1..d881bea48fdddd011e8d2596b030709a31abc740 100644 (file)
@@ -13,15 +13,23 @@ import {
   createCallExpression,
   createConditionalExpression
 } from '../src'
-import { CREATE_VNODE, COMMENT, TO_STRING } from '../src/runtimeConstants'
+import {
+  CREATE_VNODE,
+  COMMENT,
+  TO_STRING,
+  RESOLVE_DIRECTIVE,
+  helperNameMap,
+  RESOLVE_COMPONENT
+} from '../src/runtimeHelpers'
 import { createElementWithCodegen } from './testUtils'
 
 function createRoot(options: Partial<RootNode> = {}): RootNode {
   return {
     type: NodeTypes.ROOT,
     children: [],
-    imports: [],
-    statements: [],
+    helpers: [],
+    components: [],
+    directives: [],
     hoists: [],
     codegenNode: undefined,
     loc: locStub,
@@ -32,45 +40,69 @@ function createRoot(options: Partial<RootNode> = {}): RootNode {
 describe('compiler: codegen', () => {
   test('module mode preamble', () => {
     const root = createRoot({
-      imports: [`helperOne`, `helperTwo`]
+      helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
     })
     const { code } = generate(root, { mode: 'module' })
-    expect(code).toMatch(`import { helperOne, helperTwo } from "vue"`)
+    expect(code).toMatch(
+      `import { ${helperNameMap[CREATE_VNODE]}, ${
+        helperNameMap[RESOLVE_DIRECTIVE]
+      } } from "vue"`
+    )
     expect(code).toMatchSnapshot()
   })
 
   test('function mode preamble', () => {
     const root = createRoot({
-      imports: [`helperOne`, `helperTwo`]
+      helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
     })
     const { code } = generate(root, { mode: 'function' })
     expect(code).toMatch(`const _Vue = Vue`)
     expect(code).toMatch(
-      `const { helperOne: _helperOne, helperTwo: _helperTwo } = _Vue`
+      `const { ${helperNameMap[CREATE_VNODE]}: _${
+        helperNameMap[CREATE_VNODE]
+      }, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${
+        helperNameMap[RESOLVE_DIRECTIVE]
+      } } = _Vue`
     )
     expect(code).toMatchSnapshot()
   })
 
   test('function mode preamble w/ prefixIdentifiers: true', () => {
     const root = createRoot({
-      imports: [`helperOne`, `helperTwo`]
+      helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
     })
     const { code } = generate(root, {
       mode: 'function',
       prefixIdentifiers: true
     })
     expect(code).not.toMatch(`const _Vue = Vue`)
-    expect(code).toMatch(`const { helperOne, helperTwo } = Vue`)
+    expect(code).toMatch(
+      `const { ${helperNameMap[CREATE_VNODE]}, ${
+        helperNameMap[RESOLVE_DIRECTIVE]
+      } } = Vue`
+    )
     expect(code).toMatchSnapshot()
   })
 
-  test('statements', () => {
+  test('assets', () => {
     const root = createRoot({
-      statements: [`const a = 1`, `const b = 2`]
+      components: [`Foo`, `bar-baz`],
+      directives: [`my_dir`]
     })
     const { code } = generate(root, { mode: 'function' })
-    expect(code).toMatch(`const a = 1\n`)
-    expect(code).toMatch(`const b = 2\n`)
+    expect(code).toMatch(
+      `const _component_Foo = _${helperNameMap[RESOLVE_COMPONENT]}("Foo")\n`
+    )
+    expect(code).toMatch(
+      `const _component_barbaz = _${
+        helperNameMap[RESOLVE_COMPONENT]
+      }("bar-baz")\n`
+    )
+    expect(code).toMatch(
+      `const _directive_my_dir = _${
+        helperNameMap[RESOLVE_DIRECTIVE]
+      }("my_dir")\n`
+    )
     expect(code).toMatchSnapshot()
   })
 
@@ -122,7 +154,7 @@ describe('compiler: codegen', () => {
         codegenNode: createInterpolation(`hello`, locStub)
       })
     )
-    expect(code).toMatch(`return _${TO_STRING}(hello)`)
+    expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
     expect(code).toMatchSnapshot()
   })
 
@@ -136,7 +168,11 @@ describe('compiler: codegen', () => {
         }
       })
     )
-    expect(code).toMatch(`return _${CREATE_VNODE}(_${COMMENT}, 0, "foo")`)
+    expect(code).toMatch(
+      `return _${helperNameMap[CREATE_VNODE]}(_${
+        helperNameMap[COMMENT]
+      }, 0, "foo")`
+    )
     expect(code).toMatchSnapshot()
   })
 
@@ -155,7 +191,7 @@ describe('compiler: codegen', () => {
         ])
       })
     )
-    expect(code).toMatch(`return _ctx.foo + _${TO_STRING}(bar)`)
+    expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
     expect(code).toMatchSnapshot()
   })
 
@@ -264,15 +300,15 @@ describe('compiler: codegen', () => {
       })
     )
     expect(code).toMatch(`
-    return ${CREATE_VNODE}("div", {
+    return _${helperNameMap[CREATE_VNODE]}("div", {
       id: "foo",
       [prop]: bar,
       [foo + bar]: bar
     }, [
-      ${CREATE_VNODE}("p", { "some-key": "foo" })
+      _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
     ], [
       foo,
-      ${CREATE_VNODE}("p")
+      _${helperNameMap[CREATE_VNODE]}("p")
     ])`)
     expect(code).toMatchSnapshot()
   })
index cc061d87de700a6b2a2a50d7d36fc7e5d25dc042..1ad6820010f54382e7270385b9a6f13bd1c621f8 100644 (file)
@@ -6,7 +6,7 @@ import {
   Namespaces,
   ElementTypes
 } from '../src'
-import { CREATE_VNODE } from '../src/runtimeConstants'
+import { CREATE_VNODE } from '../src/runtimeHelpers'
 import { isString } from '@vue/shared'
 
 const leadingBracketRE = /^\[/
index 076fa37a835986fa449a04b2a3431fa27049f449..6f778e156153b2b634e5574284efc32a36246799 100644 (file)
@@ -15,7 +15,7 @@ import {
   CREATE_BLOCK,
   FRAGMENT,
   RENDER_SLOT
-} from '../src/runtimeConstants'
+} from '../src/runtimeHelpers'
 import { transformIf } from '../src/transforms/vIf'
 import { transformFor } from '../src/transforms/vFor'
 import { transformElement } from '../src/transforms/transformElement'
@@ -225,14 +225,14 @@ describe('compiler: transform', () => {
   test('should inject toString helper for interpolations', () => {
     const ast = parse(`{{ foo }}`)
     transform(ast, {})
-    expect(ast.imports).toContain(TO_STRING)
+    expect(ast.helpers).toContain(TO_STRING)
   })
 
   test('should inject createVNode and Comment for comments', () => {
     const ast = parse(`<!--foo-->`)
     transform(ast, {})
-    expect(ast.imports).toContain(CREATE_VNODE)
-    expect(ast.imports).toContain(COMMENT)
+    expect(ast.helpers).toContain(CREATE_VNODE)
+    expect(ast.helpers).toContain(COMMENT)
   })
 
   describe('root codegenNode', () => {
@@ -256,11 +256,11 @@ describe('compiler: transform', () => {
         expressions: [
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${OPEN_BLOCK}`
+            callee: OPEN_BLOCK
           },
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${CREATE_BLOCK}`,
+            callee: CREATE_BLOCK,
             arguments: args
           }
         ]
@@ -277,7 +277,7 @@ describe('compiler: transform', () => {
       expect(ast.codegenNode).toMatchObject({
         codegenNode: {
           type: NodeTypes.JS_CALL_EXPRESSION,
-          callee: `_${RENDER_SLOT}`
+          callee: RENDER_SLOT
         }
       })
     })
@@ -326,7 +326,7 @@ describe('compiler: transform', () => {
       const ast = transformWithCodegen(`<div/><div/>`)
       expect(ast.codegenNode).toMatchObject(
         createBlockMatcher([
-          `_${FRAGMENT}`,
+          FRAGMENT,
           `null`,
           [
             { type: NodeTypes.ELEMENT, tag: `div` },
index 37dfec292c3136ed61ff4cb45f2deb2fb406cb56..d82f87ab242394776e988457d5f12398b532e72f 100644 (file)
@@ -12,7 +12,7 @@ import {
   RESOLVE_DIRECTIVE,
   APPLY_DIRECTIVES,
   TO_HANDLERS
-} from '../../src/runtimeConstants'
+} from '../../src/runtimeHelpers'
 import {
   CallExpression,
   NodeTypes,
@@ -52,13 +52,13 @@ function parseWithElementTransform(
 describe('compiler: element transform', () => {
   test('import + resolve component', () => {
     const { root } = parseWithElementTransform(`<Foo/>`)
-    expect(root.imports).toContain(RESOLVE_COMPONENT)
-    expect(root.statements[0]).toMatch(`${RESOLVE_COMPONENT}("Foo")`)
+    expect(root.helpers).toContain(RESOLVE_COMPONENT)
+    expect(root.components).toContain(`Foo`)
   })
 
   test('static props', () => {
     const { node } = parseWithElementTransform(`<div id="foo" class="bar" />`)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments).toMatchObject([
       `"div"`,
       createObjectMatcher({
@@ -70,7 +70,7 @@ describe('compiler: element transform', () => {
 
   test('props + children', () => {
     const { node } = parseWithElementTransform(`<div id="foo"><span/></div>`)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments).toMatchObject([
       `"div"`,
       createObjectMatcher({
@@ -81,7 +81,7 @@ describe('compiler: element transform', () => {
           type: NodeTypes.ELEMENT,
           tag: 'span',
           codegenNode: {
-            callee: `_${CREATE_VNODE}`,
+            callee: CREATE_VNODE,
             arguments: [`"span"`]
           }
         }
@@ -91,7 +91,7 @@ describe('compiler: element transform', () => {
 
   test('0 placeholder for children with no props', () => {
     const { node } = parseWithElementTransform(`<div><span/></div>`)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments).toMatchObject([
       `"div"`,
       `null`,
@@ -100,7 +100,7 @@ describe('compiler: element transform', () => {
           type: NodeTypes.ELEMENT,
           tag: 'span',
           codegenNode: {
-            callee: `_${CREATE_VNODE}`,
+            callee: CREATE_VNODE,
             arguments: [`"span"`]
           }
         }
@@ -111,8 +111,8 @@ describe('compiler: element transform', () => {
   test('v-bind="obj"', () => {
     const { root, node } = parseWithElementTransform(`<div v-bind="obj" />`)
     // single v-bind doesn't need mergeProps
-    expect(root.imports).not.toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).not.toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
     // should directly use `obj` in props position
     expect(node.arguments[1]).toMatchObject({
       type: NodeTypes.SIMPLE_EXPRESSION,
@@ -124,11 +124,11 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div id="foo" v-bind="obj" />`
     )
-    expect(root.imports).toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments[1]).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${MERGE_PROPS}`,
+      callee: MERGE_PROPS,
       arguments: [
         createObjectMatcher({
           id: 'foo'
@@ -145,11 +145,11 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div v-bind="obj" id="foo" />`
     )
-    expect(root.imports).toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).toContain(MERGE_PROPS)
+    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.SIMPLE_EXPRESSION,
@@ -166,11 +166,11 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div id="foo" v-bind="obj" class="bar" />`
     )
-    expect(root.imports).toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments[1]).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${MERGE_PROPS}`,
+      callee: MERGE_PROPS,
       arguments: [
         createObjectMatcher({
           id: 'foo'
@@ -190,18 +190,18 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div id="foo" v-on="obj" class="bar" />`
     )
-    expect(root.imports).toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments[1]).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${MERGE_PROPS}`,
+      callee: MERGE_PROPS,
       arguments: [
         createObjectMatcher({
           id: 'foo'
         }),
         {
           type: NodeTypes.JS_CALL_EXPRESSION,
-          callee: `_${TO_HANDLERS}`,
+          callee: TO_HANDLERS,
           arguments: [
             {
               type: NodeTypes.SIMPLE_EXPRESSION,
@@ -220,18 +220,18 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div id="foo" v-on="handlers" v-bind="obj" />`
     )
-    expect(root.imports).toContain(MERGE_PROPS)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(root.helpers).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments[1]).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${MERGE_PROPS}`,
+      callee: MERGE_PROPS,
       arguments: [
         createObjectMatcher({
           id: 'foo'
         }),
         {
           type: NodeTypes.JS_CALL_EXPRESSION,
-          callee: `_${TO_HANDLERS}`,
+          callee: TO_HANDLERS,
           arguments: [
             {
               type: NodeTypes.SIMPLE_EXPRESSION,
@@ -249,7 +249,7 @@ describe('compiler: element transform', () => {
 
   test('should handle plain <template> as normal element', () => {
     const { node } = parseWithElementTransform(`<template id="foo" />`)
-    expect(node.callee).toBe(`_${CREATE_VNODE}`)
+    expect(node.callee).toBe(CREATE_VNODE)
     expect(node.arguments).toMatchObject([
       `"template"`,
       createObjectMatcher({
@@ -281,7 +281,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: [
@@ -312,14 +312,14 @@ describe('compiler: element transform', () => {
         }
       }
     )
-    expect(root.imports).toContain(RESOLVE_DIRECTIVE)
-    expect(root.statements[0]).toMatch(`${RESOLVE_DIRECTIVE}("foo")`)
+    expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
+    expect(root.directives).toContain(`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"`,
           `null`,
@@ -357,12 +357,12 @@ describe('compiler: element transform', () => {
     const { root, node } = parseWithElementTransform(
       `<div v-foo v-bar="x" v-baz:[arg].mod.mad="y" />`
     )
-    expect(root.imports).toContain(RESOLVE_DIRECTIVE)
-    expect(root.statements[0]).toMatch(`${RESOLVE_DIRECTIVE}("foo")`)
-    expect(root.statements[1]).toMatch(`${RESOLVE_DIRECTIVE}("bar")`)
-    expect(root.statements[2]).toMatch(`${RESOLVE_DIRECTIVE}("baz")`)
+    expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
+    expect(root.directives).toContain(`foo`)
+    expect(root.directives).toContain(`bar`)
+    expect(root.directives).toContain(`baz`)
 
-    expect(node.callee).toBe(`_${APPLY_DIRECTIVES}`)
+    expect(node.callee).toBe(APPLY_DIRECTIVES)
     expect(node.arguments).toMatchObject([
       {
         type: NodeTypes.JS_CALL_EXPRESSION
index 2a71190b12007787fd5387f17035c7f8c54fe2a3..2a3e5455fb80b15396681250bf7bf55baa1bfab0 100644 (file)
@@ -10,7 +10,7 @@ import { transformElement } from '../../src/transforms/transformElement'
 import { transformOn } from '../../src/transforms/vOn'
 import { transformBind } from '../../src/transforms/vBind'
 import { transformExpression } from '../../src/transforms/transformExpression'
-import { RENDER_SLOT } from '../../src/runtimeConstants'
+import { RENDER_SLOT } from '../../src/runtimeHelpers'
 import { transformSlotOutlet } from '../../src/transforms/transformSlotOutlet'
 
 function parseWithSlots(template: string, options: CompilerOptions = {}) {
@@ -35,7 +35,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot/>`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [`$slots`, `"default"`]
     })
   })
@@ -44,7 +44,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot name="foo" />`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [`$slots`, `"foo"`]
     })
   })
@@ -53,7 +53,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot :name="foo" />`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         {
@@ -98,7 +98,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot foo="bar" :baz="qux" />`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"default"`,
@@ -135,7 +135,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot name="foo" foo="bar" :baz="qux" />`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"foo"`,
@@ -173,7 +173,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot :name="foo" foo="bar" :baz="qux" />`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         { content: `foo`, isStatic: false },
@@ -211,7 +211,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot><div/></slot>`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"default"`,
@@ -230,7 +230,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot name="foo"><div/></slot>`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"foo"`,
@@ -249,7 +249,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot :foo="bar"><div/></slot>`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"default"`,
@@ -282,7 +282,7 @@ describe('compiler: transform <slot> outlets', () => {
     const ast = parseWithSlots(`<slot name="foo" :foo="bar"><div/></slot>`)
     expect((ast.children[0] as ElementNode).codegenNode).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${RENDER_SLOT}`,
+      callee: RENDER_SLOT,
       arguments: [
         `$slots`,
         `"foo"`,
index 460a763220802eb3f0658314fc1b5566648f5df2..2f1c9aba7ed68cc933ffdf506b63f024a1c8e585 100644 (file)
@@ -9,7 +9,7 @@ import {
 } from '../../src'
 import { transformBind } from '../../src/transforms/vBind'
 import { transformElement } from '../../src/transforms/transformElement'
-import { CAMELIZE } from '../../src/runtimeConstants'
+import { CAMELIZE, helperNameMap } from '../../src/runtimeHelpers'
 import { transformExpression } from '../../src/transforms/transformExpression'
 
 function parseWithVBind(
@@ -123,7 +123,7 @@ describe('compiler: transform v-bind', () => {
       .arguments[1] as ObjectExpression
     expect(props.properties[0]).toMatchObject({
       key: {
-        content: `_${CAMELIZE}(foo)`,
+        content: `_${helperNameMap[CAMELIZE]}(foo)`,
         isStatic: false
       },
       value: {
@@ -142,7 +142,7 @@ describe('compiler: transform v-bind', () => {
     expect(props.properties[0]).toMatchObject({
       key: {
         children: [
-          `${CAMELIZE}(`,
+          `${helperNameMap[CAMELIZE]}(`,
           { content: `_ctx.foo` },
           `(`,
           { content: `_ctx.bar` },
index 8a3547b250e4c75658b86ded6b1a2dbf9ea081e8..2697358c0566e0ca8e85d053c5083e200c8cfe3c 100644 (file)
@@ -23,7 +23,7 @@ import {
   FRAGMENT,
   RENDER_LIST,
   RENDER_SLOT
-} from '../../src/runtimeConstants'
+} from '../../src/runtimeHelpers'
 import { PatchFlags } from '@vue/runtime-dom'
 import { PatchFlagNames } from '@vue/shared'
 import { createObjectMatcher } from '../testUtils'
@@ -575,17 +575,17 @@ describe('compiler: v-for', () => {
         expressions: [
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${OPEN_BLOCK}`
+            callee: OPEN_BLOCK
           },
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${CREATE_BLOCK}`,
+            callee: CREATE_BLOCK,
             arguments: [
-              `_${FRAGMENT}`,
+              FRAGMENT,
               `null`,
               {
                 type: NodeTypes.JS_CALL_EXPRESSION,
-                callee: `_${RENDER_LIST}`,
+                callee: RENDER_LIST,
                 arguments: [
                   {}, // to be asserted by each test
                   {
@@ -597,11 +597,11 @@ describe('compiler: v-for', () => {
                           expressions: [
                             {
                               type: NodeTypes.JS_CALL_EXPRESSION,
-                              callee: `_${OPEN_BLOCK}`
+                              callee: OPEN_BLOCK
                             },
                             {
                               type: NodeTypes.JS_CALL_EXPRESSION,
-                              callee: `_${CREATE_BLOCK}`
+                              callee: CREATE_BLOCK
                             }
                           ]
                         }
@@ -703,7 +703,7 @@ describe('compiler: v-for', () => {
         source: { content: `items` },
         params: [{ content: `item` }],
         blockArgs: [
-          `_${FRAGMENT}`,
+          FRAGMENT,
           `null`,
           [
             { type: NodeTypes.TEXT, content: `hello` },
@@ -728,7 +728,7 @@ describe('compiler: v-for', () => {
         params: [{ content: `item` }],
         returns: {
           type: NodeTypes.JS_CALL_EXPRESSION,
-          callee: `_${RENDER_SLOT}`
+          callee: RENDER_SLOT
         }
       })
       expect(generate(root).code).toMatchSnapshot()
@@ -746,7 +746,7 @@ describe('compiler: v-for', () => {
         params: [{ content: `item` }],
         returns: {
           type: NodeTypes.JS_CALL_EXPRESSION,
-          callee: `_${RENDER_SLOT}`
+          callee: RENDER_SLOT
         }
       })
       expect(generate(root).code).toMatchSnapshot()
@@ -781,7 +781,7 @@ describe('compiler: v-for', () => {
         source: { content: `items` },
         params: [{ content: `item` }],
         blockArgs: [
-          `_${FRAGMENT}`,
+          FRAGMENT,
           createObjectMatcher({
             key: `[item]`
           }),
@@ -804,7 +804,7 @@ describe('compiler: v-for', () => {
         expressions: [
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${OPEN_BLOCK}`,
+            callee: OPEN_BLOCK,
             arguments: []
           },
           {
@@ -812,14 +812,14 @@ describe('compiler: v-for', () => {
             test: { content: `ok` },
             consequent: {
               type: NodeTypes.JS_CALL_EXPRESSION,
-              callee: `_${CREATE_BLOCK}`,
+              callee: CREATE_BLOCK,
               // should optimize v-if + v-for into a single Fragment block
               arguments: [
-                `_${FRAGMENT}`,
+                FRAGMENT,
                 createObjectMatcher({ key: `[0]` }),
                 {
                   type: NodeTypes.JS_CALL_EXPRESSION,
-                  callee: `_${RENDER_LIST}`,
+                  callee: RENDER_LIST,
                   arguments: [
                     { content: `list` },
                     {
@@ -830,11 +830,11 @@ describe('compiler: v-for', () => {
                         expressions: [
                           {
                             type: NodeTypes.JS_CALL_EXPRESSION,
-                            callee: `_${OPEN_BLOCK}`
+                            callee: OPEN_BLOCK
                           },
                           {
                             type: NodeTypes.JS_CALL_EXPRESSION,
-                            callee: `_${CREATE_BLOCK}`,
+                            callee: CREATE_BLOCK,
                             arguments: [`"div"`]
                           }
                         ]
index 778e01ebee444dc036b5dba71fc77fcc8bfd543c..4bee495b037e6affd475f40510884e5b7a490eb2 100644 (file)
@@ -24,7 +24,7 @@ import {
   MERGE_PROPS,
   APPLY_DIRECTIVES,
   RENDER_SLOT
-} from '../../src/runtimeConstants'
+} from '../../src/runtimeHelpers'
 import { createObjectMatcher } from '../testUtils'
 
 function parseWithIfTransform(
@@ -271,7 +271,7 @@ describe('compiler: v-if', () => {
         expressions: [
           {
             type: NodeTypes.JS_CALL_EXPRESSION,
-            callee: `_${OPEN_BLOCK}`,
+            callee: OPEN_BLOCK,
             arguments: []
           },
           {
@@ -281,13 +281,13 @@ describe('compiler: v-if', () => {
             },
             consequent: {
               type: NodeTypes.JS_CALL_EXPRESSION,
-              callee: `_${CREATE_BLOCK}`
+              callee: CREATE_BLOCK
             },
             alternate:
               depth < 1
                 ? {
                     type: NodeTypes.JS_CALL_EXPRESSION,
-                    callee: `_${CREATE_BLOCK}`
+                    callee: CREATE_BLOCK
                   }
                 : {
                     type: NodeTypes.JS_CONDITIONAL_EXPRESSION,
@@ -296,11 +296,11 @@ describe('compiler: v-if', () => {
                     },
                     consequent: {
                       type: NodeTypes.JS_CALL_EXPRESSION,
-                      callee: `_${CREATE_BLOCK}`
+                      callee: CREATE_BLOCK
                     },
                     alternate: {
                       type: NodeTypes.JS_CALL_EXPRESSION,
-                      callee: `_${CREATE_BLOCK}`
+                      callee: CREATE_BLOCK
                     }
                   }
           }
@@ -322,7 +322,7 @@ describe('compiler: v-if', () => {
       ])
       const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
         .alternate as CallExpression
-      expect(branch2.arguments).toMatchObject([`_${EMPTY}`])
+      expect(branch2.arguments).toMatchObject([EMPTY])
       expect(generate(root).code).toMatchSnapshot()
     })
 
@@ -335,7 +335,7 @@ describe('compiler: v-if', () => {
       const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
         .consequent as CallExpression
       expect(branch1.arguments).toMatchObject([
-        `_${FRAGMENT}`,
+        FRAGMENT,
         createObjectMatcher({ key: `[0]` }),
         [
           { type: NodeTypes.ELEMENT, tag: 'div' },
@@ -345,7 +345,7 @@ describe('compiler: v-if', () => {
       ])
       const branch2 = (codegenNode.expressions[1] as ConditionalExpression)
         .alternate as CallExpression
-      expect(branch2.arguments).toMatchObject([`_${EMPTY}`])
+      expect(branch2.arguments).toMatchObject([EMPTY])
       expect(generate(root).code).toMatchSnapshot()
     })
 
@@ -359,7 +359,7 @@ describe('compiler: v-if', () => {
         .consequent as CallExpression
       expect(branch1).toMatchObject({
         type: NodeTypes.JS_CALL_EXPRESSION,
-        callee: `_${RENDER_SLOT}`,
+        callee: RENDER_SLOT,
         arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })]
       })
       expect(generate(root).code).toMatchSnapshot()
@@ -375,7 +375,7 @@ describe('compiler: v-if', () => {
         .consequent as CallExpression
       expect(branch1).toMatchObject({
         type: NodeTypes.JS_CALL_EXPRESSION,
-        callee: `_${RENDER_SLOT}`,
+        callee: RENDER_SLOT,
         arguments: ['$slots', '"default"', createObjectMatcher({ key: `[0]` })]
       })
       expect(generate(root).code).toMatchSnapshot()
@@ -444,7 +444,7 @@ describe('compiler: v-if', () => {
         createObjectMatcher({ key: `[1]` })
       ])
       expect((branch2.alternate as CallExpression).arguments).toMatchObject([
-        `_${FRAGMENT}`,
+        FRAGMENT,
         createObjectMatcher({ key: `[2]` }),
         [
           {
@@ -464,7 +464,7 @@ describe('compiler: v-if', () => {
         .consequent as CallExpression
       expect(branch1.arguments[1]).toMatchObject({
         type: NodeTypes.JS_CALL_EXPRESSION,
-        callee: `_${MERGE_PROPS}`,
+        callee: MERGE_PROPS,
         arguments: [createObjectMatcher({ key: `[0]` }), { content: `obj` }]
       })
     })
@@ -477,7 +477,7 @@ describe('compiler: v-if', () => {
         .consequent as CallExpression
       expect(branch1.arguments[1]).toMatchObject({
         type: NodeTypes.JS_CALL_EXPRESSION,
-        callee: `_${MERGE_PROPS}`,
+        callee: MERGE_PROPS,
         arguments: [
           createObjectMatcher({
             key: '[0]',
@@ -496,7 +496,7 @@ describe('compiler: v-if', () => {
         .consequent as CallExpression
       expect(branch1.arguments[1]).toMatchObject({
         type: NodeTypes.JS_CALL_EXPRESSION,
-        callee: `_${MERGE_PROPS}`,
+        callee: MERGE_PROPS,
         arguments: [
           createObjectMatcher({ key: `[0]` }),
           { content: `obj` },
@@ -513,7 +513,7 @@ describe('compiler: v-if', () => {
       } = parseWithIfTransform(`<div v-if="ok" v-foo />`)
       const branch1 = (codegenNode.expressions[1] as ConditionalExpression)
         .consequent as CallExpression
-      expect(branch1.callee).toBe(`_${APPLY_DIRECTIVES}`)
+      expect(branch1.callee).toBe(APPLY_DIRECTIVES)
       const realBranch = branch1.arguments[0] as CallExpression
       expect(realBranch.arguments[1]).toMatchObject(
         createObjectMatcher({ key: `[0]` })
index 9e7213e9bb3c94e513612950601d8dfc9ff2e137..1641ef8114c748a5d2de275efe707a82af4f0b45 100644 (file)
@@ -17,7 +17,7 @@ import {
   trackSlotScopes,
   trackVForSlotScopes
 } from '../../src/transforms/vSlot'
-import { CREATE_SLOTS, RENDER_LIST } from '../../src/runtimeConstants'
+import { CREATE_SLOTS, RENDER_LIST } from '../../src/runtimeHelpers'
 import { createObjectMatcher } from '../testUtils'
 import { PatchFlags, PatchFlagNames } from '@vue/shared'
 import { transformFor } from '../../src/transforms/vFor'
@@ -360,7 +360,7 @@ describe('compiler: transform component slots', () => {
     )
     expect(slots).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${CREATE_SLOTS}`,
+      callee: CREATE_SLOTS,
       arguments: [
         createObjectMatcher({
           _compiled: `[true]`
@@ -451,7 +451,7 @@ describe('compiler: transform component slots', () => {
     )
     expect(slots).toMatchObject({
       type: NodeTypes.JS_CALL_EXPRESSION,
-      callee: `_${CREATE_SLOTS}`,
+      callee: CREATE_SLOTS,
       arguments: [
         createObjectMatcher({
           _compiled: `[true]`
index ad237d42ed8b527cb76aba576576608700f0e87e..300a8f217b48ebb416a02f4ffa611a31b72c2567 100644 (file)
@@ -1,5 +1,6 @@
 import { isString } from '@vue/shared'
 import { ForParseResult } from './transforms/vFor'
+import { CREATE_VNODE, RuntimeHelper } from './runtimeHelpers'
 
 // Vue template is a platform-agnostic superset of HTML (syntax only).
 // More namespaces like SVG and MathML are declared by platform specific
@@ -76,8 +77,9 @@ export type TemplateChildNode =
 export interface RootNode extends Node {
   type: NodeTypes.ROOT
   children: TemplateChildNode[]
-  imports: string[]
-  statements: string[]
+  helpers: RuntimeHelper[]
+  components: string[]
+  directives: string[]
   hoists: JSChildNode[]
   codegenNode: TemplateChildNode | JSChildNode | undefined
 }
@@ -93,6 +95,29 @@ export interface ElementNode extends Node {
   codegenNode: CallExpression | SimpleExpressionNode | undefined
 }
 
+export interface PlainElementNode extends ElementNode {
+  tagType: ElementTypes.ELEMENT
+  codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode
+}
+
+export interface ComponentNode extends ElementNode {
+  tagType: ElementTypes.COMPONENT
+  codegenNode: VNodeCodegenNode | VNodeWithDirectiveCodegenNode
+}
+
+export interface SlotOutletNode extends ElementNode {
+  tagType: ElementTypes.SLOT
+  codegenNode: SlotOutletCodegenNode
+}
+
+export interface VNodeCodegenNode extends CallExpression {
+  callee: typeof CREATE_VNODE
+}
+
+export interface VNodeWithDirectiveCodegenNode extends CallExpression {}
+
+export interface SlotOutletCodegenNode extends CallExpression {}
+
 export interface TextNode extends Node {
   type: NodeTypes.TEXT
   content: string
@@ -137,7 +162,12 @@ export interface InterpolationNode extends Node {
 // always dynamic
 export interface CompoundExpressionNode extends Node {
   type: NodeTypes.COMPOUND_EXPRESSION
-  children: (SimpleExpressionNode | InterpolationNode | TextNode | string)[]
+  children: (
+    | SimpleExpressionNode
+    | InterpolationNode
+    | TextNode
+    | string
+    | RuntimeHelper)[]
   // an expression parsed as the params of a function will track
   // the identifiers declared inside the function body.
   identifiers?: string[]
@@ -146,9 +176,11 @@ export interface CompoundExpressionNode extends Node {
 export interface IfNode extends Node {
   type: NodeTypes.IF
   branches: IfBranchNode[]
-  codegenNode: SequenceExpression
+  codegenNode: IfCodegenNode
 }
 
+export interface IfCodegenNode extends SequenceExpression {}
+
 export interface IfBranchNode extends Node {
   type: NodeTypes.IF_BRANCH
   condition: ExpressionNode | undefined // else
@@ -162,9 +194,11 @@ export interface ForNode extends Node {
   keyAlias: ExpressionNode | undefined
   objectIndexAlias: ExpressionNode | undefined
   children: TemplateChildNode[]
-  codegenNode: SequenceExpression
+  codegenNode: ForCodegenNode
 }
 
+export interface ForCodegenNode extends SequenceExpression {}
+
 // We also include a number of JavaScript AST nodes for code generation.
 // The AST is an intentionally minimal subset just to meet the exact needs of
 // Vue render function generation.
@@ -179,8 +213,13 @@ export type JSChildNode =
 
 export interface CallExpression extends Node {
   type: NodeTypes.JS_CALL_EXPRESSION
-  callee: string
-  arguments: (string | JSChildNode | TemplateChildNode | TemplateChildNode[])[]
+  callee: string | RuntimeHelper
+  arguments: (
+    | string
+    | RuntimeHelper
+    | JSChildNode
+    | TemplateChildNode
+    | TemplateChildNode[])[]
 }
 
 export interface ObjectExpression extends Node {
index 27e63eeb0a109aa196e71c341ea249f1c6739e61..a7b5f7181036cfd98c396535828dcc5bc1a203c7 100644 (file)
@@ -23,10 +23,19 @@ import {
   advancePositionWithMutation,
   assert,
   isSimpleIdentifier,
-  loadDep
+  loadDep,
+  toValidAssetId
 } from './utils'
-import { isString, isArray } from '@vue/shared'
-import { TO_STRING, CREATE_VNODE, COMMENT } from './runtimeConstants'
+import { isString, isArray, isSymbol } from '@vue/shared'
+import {
+  TO_STRING,
+  CREATE_VNODE,
+  COMMENT,
+  helperNameMap,
+  RESOLVE_COMPONENT,
+  RESOLVE_DIRECTIVE,
+  RuntimeHelper
+} from './runtimeHelpers'
 
 type CodegenNode = TemplateChildNode | JSChildNode
 
@@ -65,7 +74,7 @@ export interface CodegenContext extends Required<CodegenOptions> {
   offset: number
   indentLevel: number
   map?: SourceMapGenerator
-  helper(name: string): string
+  helper(key: RuntimeHelper): string
   push(code: string, node?: CodegenNode, openOnly?: boolean): void
   resetMapping(loc: SourceLocation): void
   indent(): void
@@ -77,7 +86,7 @@ function createCodegenContext(
   ast: RootNode,
   {
     mode = 'function',
-    prefixIdentifiers = false,
+    prefixIdentifiers = mode === 'module',
     sourceMap = false,
     filename = `template.vue.html`
   }: CodegenOptions
@@ -100,7 +109,8 @@ function createCodegenContext(
         ? undefined
         : new (loadDep('source-map')).SourceMapGenerator(),
 
-    helper(name) {
+    helper(key) {
+      const name = helperNameMap[key]
       return prefixIdentifiers ? name : `_${name}`
     },
     push(code, node, openOnly) {
@@ -172,8 +182,16 @@ export function generate(
   options: CodegenOptions = {}
 ): CodegenResult {
   const context = createCodegenContext(ast, options)
-  const { mode, push, prefixIdentifiers, indent, deindent, newline } = context
-  const hasImports = ast.imports.length
+  const {
+    mode,
+    push,
+    helper,
+    prefixIdentifiers,
+    indent,
+    deindent,
+    newline
+  } = context
+  const hasHelpers = ast.helpers.length > 0
   const useWithBlock = !prefixIdentifiers && mode !== 'module'
 
   // preambles
@@ -182,9 +200,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 declaration inside the
     // with block so it doesn't incur the `in` check cost for every helper access.
-    if (hasImports) {
+    if (hasHelpers) {
       if (prefixIdentifiers) {
-        push(`const { ${ast.imports.join(', ')} } = Vue\n`)
+        push(`const { ${ast.helpers.map(helper).join(', ')} } = Vue\n`)
       } else {
         // "with" mode.
         // save Vue in a separate variable to avoid collision
@@ -193,7 +211,7 @@ export function generate(
         // has check cost, but hoists are lifted out of the function - we need
         // to provide the helper here.
         if (ast.hoists.length) {
-          push(`const _${CREATE_VNODE} = Vue.createVNode\n`)
+          push(`const _${helperNameMap[CREATE_VNODE]} = Vue.createVNode\n`)
         }
       }
     }
@@ -202,8 +220,8 @@ export function generate(
     push(`return `)
   } else {
     // generate import statements for helpers
-    if (hasImports) {
-      push(`import { ${ast.imports.join(', ')} } from "vue"\n`)
+    if (hasHelpers) {
+      push(`import { ${ast.helpers.map(helper).join(', ')} } from "vue"\n`)
     }
     genHoists(ast.hoists, context)
     context.newline()
@@ -219,8 +237,12 @@ export function generate(
     indent()
     // function mode const declarations should be inside with block
     // also they should be renamed to avoid collision with user properties
-    if (hasImports) {
-      push(`const { ${ast.imports.map(n => `${n}: _${n}`).join(', ')} } = _Vue`)
+    if (hasHelpers) {
+      push(
+        `const { ${ast.helpers
+          .map(s => `${helperNameMap[s]}: _${helperNameMap[s]}`)
+          .join(', ')} } = _Vue`
+      )
       newline()
       newline()
     }
@@ -230,11 +252,13 @@ export function generate(
   }
 
   // generate asset resolution statements
-  if (ast.statements.length) {
-    ast.statements.forEach(s => {
-      push(s)
-      newline()
-    })
+  if (ast.components.length) {
+    genAssets(ast.components, 'component', context)
+  }
+  if (ast.directives.length) {
+    genAssets(ast.directives, 'directive', context)
+  }
+  if (ast.components.length || ast.directives.length) {
     newline()
   }
 
@@ -260,6 +284,23 @@ export function generate(
   }
 }
 
+function genAssets(
+  assets: string[],
+  type: 'component' | 'directive',
+  context: CodegenContext
+) {
+  const resolver = context.helper(
+    type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE
+  )
+  for (let i = 0; i < assets.length; i++) {
+    const id = assets[i]
+    context.push(
+      `const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)})`
+    )
+    context.newline()
+  }
+}
+
 function genHoists(hoists: JSChildNode[], context: CodegenContext) {
   if (!hoists.length) {
     return
@@ -297,7 +338,7 @@ function genNodeListAsArray(
 }
 
 function genNodeList(
-  nodes: (string | CodegenNode | TemplateChildNode[])[],
+  nodes: (string | RuntimeHelper | CodegenNode | TemplateChildNode[])[],
   context: CodegenContext,
   multilines: boolean = false
 ) {
@@ -322,11 +363,18 @@ function genNodeList(
   }
 }
 
-function genNode(node: CodegenNode | string, context: CodegenContext) {
+function genNode(
+  node: CodegenNode | RuntimeHelper | string,
+  context: CodegenContext
+) {
   if (isString(node)) {
     context.push(node)
     return
   }
+  if (isSymbol(node)) {
+    context.push(context.helper(node))
+    return
+  }
   switch (node.type) {
     case NodeTypes.ELEMENT:
     case NodeTypes.IF:
@@ -450,7 +498,10 @@ function genComment(node: CommentNode, context: CodegenContext) {
 
 // JavaScript
 function genCallExpression(node: CallExpression, context: CodegenContext) {
-  context.push(node.callee + `(`, node, true)
+  const callee = isString(node.callee)
+    ? node.callee
+    : context.helper(node.callee)
+  context.push(callee + `(`, node, true)
   genNodeList(node.arguments, context)
   context.push(`)`)
 }
index 820631ca497f633c0fd5f2f60657ddd2316ef95d..34998f73e8818520c739d6ce46005e5a11f085b9 100644 (file)
@@ -64,7 +64,10 @@ export function baseCompile(
     }
   })
 
-  return generate(ast, options)
+  return generate(ast, {
+    ...options,
+    prefixIdentifiers
+  })
 }
 
 // Also expose lower level APIs & types
diff --git a/packages/compiler-core/src/runtimeConstants.ts b/packages/compiler-core/src/runtimeConstants.ts
deleted file mode 100644 (file)
index f67263b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// Name mapping constants for runtime helpers that need to be imported in
-// generated code. Make sure these are correctly exported in the runtime!
-export const FRAGMENT = `Fragment`
-export const PORTAL = `Portal`
-export const COMMENT = `Comment`
-export const TEXT = `Text`
-export const SUSPENSE = `Suspense`
-export const EMPTY = `Empty`
-export const OPEN_BLOCK = `openBlock`
-export const CREATE_BLOCK = `createBlock`
-export const CREATE_VNODE = `createVNode`
-export const RESOLVE_COMPONENT = `resolveComponent`
-export const RESOLVE_DIRECTIVE = `resolveDirective`
-export const APPLY_DIRECTIVES = `applyDirectives`
-export const RENDER_LIST = `renderList`
-export const RENDER_SLOT = `renderSlot`
-export const CREATE_SLOTS = `createSlots`
-export const TO_STRING = `toString`
-export const MERGE_PROPS = `mergeProps`
-export const TO_HANDLERS = `toHandlers`
-export const CAMELIZE = `camelize`
diff --git a/packages/compiler-core/src/runtimeHelpers.ts b/packages/compiler-core/src/runtimeHelpers.ts
new file mode 100644 (file)
index 0000000..053a45f
--- /dev/null
@@ -0,0 +1,64 @@
+export const FRAGMENT = Symbol()
+export const PORTAL = Symbol()
+export const COMMENT = Symbol()
+export const TEXT = Symbol()
+export const SUSPENSE = Symbol()
+export const EMPTY = Symbol()
+export const OPEN_BLOCK = Symbol()
+export const CREATE_BLOCK = Symbol()
+export const CREATE_VNODE = Symbol()
+export const RESOLVE_COMPONENT = Symbol()
+export const RESOLVE_DIRECTIVE = Symbol()
+export const APPLY_DIRECTIVES = Symbol()
+export const RENDER_LIST = Symbol()
+export const RENDER_SLOT = Symbol()
+export const CREATE_SLOTS = Symbol()
+export const TO_STRING = Symbol()
+export const MERGE_PROPS = Symbol()
+export const TO_HANDLERS = Symbol()
+export const CAMELIZE = Symbol()
+
+export type RuntimeHelper =
+  | typeof FRAGMENT
+  | typeof PORTAL
+  | typeof COMMENT
+  | typeof TEXT
+  | typeof SUSPENSE
+  | typeof EMPTY
+  | typeof OPEN_BLOCK
+  | typeof CREATE_BLOCK
+  | typeof CREATE_VNODE
+  | typeof RESOLVE_COMPONENT
+  | typeof RESOLVE_DIRECTIVE
+  | typeof APPLY_DIRECTIVES
+  | typeof RENDER_LIST
+  | typeof RENDER_SLOT
+  | typeof CREATE_SLOTS
+  | typeof TO_STRING
+  | typeof MERGE_PROPS
+  | typeof TO_HANDLERS
+  | typeof CAMELIZE
+
+// Name mapping for runtime helpers that need to be imported from 'vue' in
+// generated code. Make sure these are correctly exported in the runtime!
+export const helperNameMap = {
+  [FRAGMENT]: `Fragment`,
+  [PORTAL]: `Portal`,
+  [COMMENT]: `Comment`,
+  [TEXT]: `Text`,
+  [SUSPENSE]: `Suspense`,
+  [EMPTY]: `Empty`,
+  [OPEN_BLOCK]: `openBlock`,
+  [CREATE_BLOCK]: `createBlock`,
+  [CREATE_VNODE]: `createVNode`,
+  [RESOLVE_COMPONENT]: `resolveComponent`,
+  [RESOLVE_DIRECTIVE]: `resolveDirective`,
+  [APPLY_DIRECTIVES]: `applyDirectives`,
+  [RENDER_LIST]: `renderList`,
+  [RENDER_SLOT]: `renderSlot`,
+  [CREATE_SLOTS]: `createSlots`,
+  [TO_STRING]: `toString`,
+  [MERGE_PROPS]: `mergeProps`,
+  [TO_HANDLERS]: `toHandlers`,
+  [CAMELIZE]: `camelize`
+}
index b995014f47ad88f16b3deeb8ca796eb410311bb7..363b7918e234d2bfeb618820eceefb747109bbc2 100644 (file)
@@ -14,7 +14,14 @@ import {
 } from './ast'
 import { isString, isArray } from '@vue/shared'
 import { CompilerError, defaultOnError } from './errors'
-import { TO_STRING, COMMENT, CREATE_VNODE, FRAGMENT } from './runtimeConstants'
+import {
+  TO_STRING,
+  COMMENT,
+  CREATE_VNODE,
+  FRAGMENT,
+  RuntimeHelper,
+  helperNameMap
+} from './runtimeHelpers'
 import { isVSlot, createBlockExpression, isSlotOutlet } from './utils'
 import { hoistStatic } from './transforms/hoistStatic'
 
@@ -57,8 +64,9 @@ export interface TransformOptions {
 
 export interface TransformContext extends Required<TransformOptions> {
   root: RootNode
-  imports: Set<string>
-  statements: Set<string>
+  helpers: Set<RuntimeHelper>
+  components: Set<string>
+  directives: Set<string>
   hoists: JSChildNode[]
   identifiers: { [name: string]: number | undefined }
   scopes: {
@@ -70,7 +78,8 @@ export interface TransformContext extends Required<TransformOptions> {
   parent: ParentNode | null
   childIndex: number
   currentNode: RootNode | TemplateChildNode | null
-  helper(name: string): string
+  helper<T extends RuntimeHelper>(name: T): T
+  helperString(name: RuntimeHelper): string
   replaceNode(node: TemplateChildNode): void
   removeNode(node?: TemplateChildNode): void
   onNodeRemoved: () => void
@@ -91,8 +100,9 @@ function createTransformContext(
 ): TransformContext {
   const context: TransformContext = {
     root,
-    imports: new Set(),
-    statements: new Set(),
+    helpers: new Set(),
+    components: new Set(),
+    directives: new Set(),
     hoists: [],
     identifiers: {},
     scopes: {
@@ -110,8 +120,14 @@ function createTransformContext(
     currentNode: root,
     childIndex: 0,
     helper(name) {
-      context.imports.add(name)
-      return prefixIdentifiers ? name : `_${name}`
+      context.helpers.add(name)
+      return name
+    },
+    helperString(name) {
+      return (
+        (context.prefixIdentifiers ? `` : `_`) +
+        helperNameMap[context.helper(name)]
+      )
     },
     replaceNode(node) {
       /* istanbul ignore if */
@@ -242,8 +258,9 @@ function finalizeRoot(root: RootNode, context: TransformContext) {
   }
 
   // finalize meta information
-  root.imports = [...context.imports]
-  root.statements = [...context.statements]
+  root.helpers = [...context.helpers]
+  root.components = [...context.components]
+  root.directives = [...context.directives]
   root.hoists = context.hoists
 }
 
index 23b8fbd944a3eb984c4377dfc89444d1c5107b50..164f2de3945f5ee54e441c5b350465ddcff420cd 100644 (file)
@@ -7,7 +7,7 @@ import {
   ElementTypes
 } from '../ast'
 import { TransformContext } from '../transform'
-import { APPLY_DIRECTIVES } from '../runtimeConstants'
+import { APPLY_DIRECTIVES } from '../runtimeHelpers'
 import { PropsExpression } from './transformElement'
 import { PatchFlags } from '@vue/shared'
 
@@ -41,7 +41,7 @@ function walk(
           flag === PatchFlags.TEXT
         ) {
           let codegenNode = child.codegenNode as CallExpression
-          if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
+          if (codegenNode.callee === APPLY_DIRECTIVES) {
             codegenNode = codegenNode.arguments[0] as CallExpression
           }
           const props = codegenNode.arguments[1] as
@@ -68,7 +68,7 @@ function walk(
 
 function getPatchFlag(node: ElementNode): number | undefined {
   let codegenNode = node.codegenNode as CallExpression
-  if (codegenNode.callee.includes(APPLY_DIRECTIVES)) {
+  if (codegenNode.callee === APPLY_DIRECTIVES) {
     codegenNode = codegenNode.arguments[0] as CallExpression
   }
   const flag = codegenNode.arguments[3]
index 513a788b612e954bfe482fdcc12c6f51df129045..3a483adba178f3ae2d20d8dadc3ae8517f7ce863 100644 (file)
@@ -25,12 +25,10 @@ import {
   RESOLVE_COMPONENT,
   MERGE_PROPS,
   TO_HANDLERS
-} from '../runtimeConstants'
-import { getInnerRange, isVSlot } from '../utils'
+} from '../runtimeHelpers'
+import { getInnerRange, isVSlot, toValidAssetId } from '../utils'
 import { buildSlots } from './vSlot'
 
-const toValidId = (str: string): string => str.replace(/[^\w]/g, '')
-
 // generate a JavaScript AST for this element's codegen
 export const transformElement: NodeTransform = (node, context) => {
   if (node.type === NodeTypes.ELEMENT) {
@@ -50,19 +48,14 @@ export const transformElement: NodeTransform = (node, context) => {
         let patchFlag: number = 0
         let runtimeDirectives: DirectiveNode[] | undefined
         let dynamicPropNames: string[] | undefined
-        let componentIdentifier: string | undefined
 
         if (isComponent) {
-          componentIdentifier = `_component_${toValidId(node.tag)}`
-          context.statements.add(
-            `const ${componentIdentifier} = ${context.helper(
-              RESOLVE_COMPONENT
-            )}(${JSON.stringify(node.tag)})`
-          )
+          context.helper(RESOLVE_COMPONENT)
+          context.components.add(node.tag)
         }
 
         const args: CallExpression['arguments'] = [
-          isComponent ? componentIdentifier! : `"${node.tag}"`
+          isComponent ? toValidAssetId(node.tag, `component`) : `"${node.tag}"`
         ]
         // props
         if (hasProps) {
@@ -402,13 +395,11 @@ function createDirectiveArgs(
   context: TransformContext
 ): ArrayExpression {
   // inject statement for resolving directive
-  const dirIdentifier = `_directive_${toValidId(dir.name)}`
-  context.statements.add(
-    `const ${dirIdentifier} = ${context.helper(
-      RESOLVE_DIRECTIVE
-    )}(${JSON.stringify(dir.name)})`
-  )
-  const dirArgs: ArrayExpression['elements'] = [dirIdentifier]
+  context.helper(RESOLVE_DIRECTIVE)
+  context.directives.add(dir.name)
+  const dirArgs: ArrayExpression['elements'] = [
+    toValidAssetId(dir.name, `directive`)
+  ]
   const { loc } = dir
   if (dir.exp) dirArgs.push(dir.exp)
   if (dir.arg) dirArgs.push(dir.arg)
index f897aa8cfb2a1cea6f9d9367c8e9fb417b0879b1..70421c6fda4d37d8bc091ff92d75c2fffe29948e 100644 (file)
@@ -8,7 +8,7 @@ import {
 import { isSlotOutlet } from '../utils'
 import { buildProps } from './transformElement'
 import { createCompilerError, ErrorCodes } from '../errors'
-import { RENDER_SLOT } from '../runtimeConstants'
+import { RENDER_SLOT } from '../runtimeHelpers'
 
 export const transformSlotOutlet: NodeTransform = (node, context) => {
   if (isSlotOutlet(node)) {
index 5c6e65a0b4308c1f31f50b9e839545e4fa287821..1d28e3546af2ecabe98f2e2387ea7dd4bb1efaf6 100644 (file)
@@ -2,7 +2,7 @@ import { DirectiveTransform } from '../transform'
 import { createObjectProperty, createSimpleExpression, NodeTypes } from '../ast'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { camelize } from '@vue/shared'
-import { CAMELIZE } from '../runtimeConstants'
+import { CAMELIZE } from '../runtimeHelpers'
 
 // v-bind without arg is handled directly in ./element.ts due to it affecting
 // codegen for the entire props object. This transform here is only for v-bind
@@ -20,10 +20,10 @@ export const transformBind: DirectiveTransform = (dir, context) => {
       if (arg.isStatic) {
         arg.content = camelize(arg.content)
       } else {
-        arg.content = `${context.helper(CAMELIZE)}(${arg.content})`
+        arg.content = `${context.helperString(CAMELIZE)}(${arg.content})`
       }
     } else {
-      arg.children.unshift(`${context.helper(CAMELIZE)}(`)
+      arg.children.unshift(`${context.helperString(CAMELIZE)}(`)
       arg.children.push(`)`)
     }
   }
index 70b170bfdf9462173d25bb458c8334eba5334db8..0cf239ea437787991d13ec444afadd3056dc10c3 100644 (file)
@@ -30,7 +30,7 @@ import {
   OPEN_BLOCK,
   CREATE_BLOCK,
   FRAGMENT
-} from '../runtimeConstants'
+} from '../runtimeHelpers'
 import { processExpression } from './transformExpression'
 import { PatchFlags, PatchFlagNames } from '@vue/shared'
 import { PropsExpression } from './transformElement'
index 7d09767c5367e682305223f0a9cf04578ed77f5a..b974943f3d1df29e6d82fc76cbef1de6821e8e64 100644 (file)
@@ -29,7 +29,7 @@ import {
   APPLY_DIRECTIVES,
   CREATE_VNODE,
   RENDER_SLOT
-} from '../runtimeConstants'
+} from '../runtimeHelpers'
 import { injectProp } from '../utils'
 import { PropsExpression } from './transformElement'
 
@@ -184,18 +184,18 @@ function createChildrenCodegenNode(
     const childCodegen = (child as ElementNode).codegenNode as CallExpression
     let vnodeCall = childCodegen
     // Element with custom directives. Locate the actual createVNode() call.
-    if (vnodeCall.callee.includes(APPLY_DIRECTIVES)) {
+    if (vnodeCall.callee === APPLY_DIRECTIVES) {
       vnodeCall = vnodeCall.arguments[0] as CallExpression
     }
     // Change createVNode to createBlock.
-    if (vnodeCall.callee.includes(CREATE_VNODE)) {
+    if (vnodeCall.callee === CREATE_VNODE) {
       vnodeCall.callee = helper(CREATE_BLOCK)
     }
     // It's possible to have renderSlot() here as well - which already produces
     // a block, so no need to change the callee. However it accepts props at
     // a different arg index so make sure to check for so that the key injection
     // logic below works for it too.
-    const propsIndex = vnodeCall.callee.includes(RENDER_SLOT) ? 2 : 1
+    const propsIndex = vnodeCall.callee === RENDER_SLOT ? 2 : 1
     // inject branch key
     const existingProps = vnodeCall.arguments[propsIndex] as
       | PropsExpression
index 7bd5cda039a13fd7fd9b73a568fb6931de0bc0a8..64b407b13017874184ca620bfd9e28ee08bf9d56 100644 (file)
@@ -24,7 +24,7 @@ import {
 import { TransformContext, NodeTransform } from '../transform'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { findDir, isTemplateNode, assert, isVSlot } from '../utils'
-import { CREATE_SLOTS, RENDER_LIST } from '../runtimeConstants'
+import { CREATE_SLOTS, RENDER_LIST } from '../runtimeHelpers'
 import { parseForExpression, createForLoopParams } from './vFor'
 
 const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
index e77a4856a6f65b90c3064e1a1f3cb8f4a9679fe7..084cd5dacd2801e69a1ac30a5dc95392d937ecba 100644 (file)
@@ -19,7 +19,7 @@ import {
 import { parse } from 'acorn'
 import { walk } from 'estree-walker'
 import { TransformContext } from './transform'
-import { OPEN_BLOCK, CREATE_BLOCK, MERGE_PROPS } from './runtimeConstants'
+import { OPEN_BLOCK, CREATE_BLOCK, MERGE_PROPS } from './runtimeHelpers'
 import { isString, isFunction } from '@vue/shared'
 import { PropsExpression } from './transforms/transformElement'
 
@@ -217,3 +217,10 @@ export function injectProp(
     ])
   }
 }
+
+export function toValidAssetId(
+  name: string,
+  type: 'component' | 'directive'
+): string {
+  return `_${type}_${name.replace(/[^\w]/g, '')}`
+}
index 11a72c1dcc40a58072a8a1021e5720ad10d8d869..6e1b90c176201508eb0950a2db2573058855b2aa 100644 (file)
@@ -30,6 +30,7 @@ export const isArray = Array.isArray
 export const isFunction = (val: any): val is Function =>
   typeof val === 'function'
 export const isString = (val: any): val is string => typeof val === 'string'
+export const isSymbol = (val: any): val is symbol => typeof val === 'symbol'
 export const isObject = (val: any): val is Record<any, any> =>
   val !== null && typeof val === 'object'