]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler-core): wrap slot functions with render context
authorEvan You <yyx990803@gmail.com>
Mon, 16 Mar 2020 15:23:29 +0000 (11:23 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 16 Mar 2020 17:06:46 +0000 (13:06 -0400)
13 files changed:
packages/compiler-core/__tests__/__snapshots__/scopeId.spec.ts.snap
packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/runtimeHelpers.ts
packages/compiler-core/src/transforms/vSlot.ts
packages/compiler-ssr/__tests__/ssrComponent.spec.ts
packages/compiler-ssr/__tests__/ssrScopeId.spec.ts
packages/compiler-ssr/__tests__/ssrSuspense.spec.ts
packages/runtime-core/src/componentRenderUtils.ts
packages/runtime-core/src/helpers/scopeId.ts
packages/runtime-core/src/helpers/withRenderContext.ts [new file with mode: 0644]
packages/runtime-core/src/index.ts

index 6f69b2002e883e09f37988eac030c4aa72da2cfd..84f84e43848b86521d5e40da3dca14ed579c2f70 100644 (file)
@@ -19,7 +19,7 @@ export const render = _withId(function render(_ctx, _cache) {
 `;
 
 exports[`scopeId compiler support should wrap default slot 1`] = `
-"import { createVNode as _createVNode, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
+"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = _withScopeId(\\"test\\")
 
 export const render = _withId(function render(_ctx, _cache) {
@@ -28,14 +28,14 @@ export const render = _withId(function render(_ctx, _cache) {
   return (_openBlock(), _createBlock(_component_Child, null, {
     default: _withId(() => [
       _createVNode(\\"div\\")
-    ]),
+    ], _ctx),
     _: 1
   }))
 })"
 `;
 
 exports[`scopeId compiler support should wrap dynamic slots 1`] = `
-"import { createVNode as _createVNode, resolveComponent as _resolveComponent, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
+"import { createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, renderList as _renderList, createSlots as _createSlots, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = _withScopeId(\\"test\\")
 
 export const render = _withId(function render(_ctx, _cache) {
@@ -47,7 +47,7 @@ export const render = _withId(function render(_ctx, _cache) {
           name: \\"foo\\",
           fn: _withId(() => [
             _createVNode(\\"div\\")
-          ])
+          ], _ctx)
         }
       : undefined,
     _renderList(_ctx.list, (i) => {
@@ -55,7 +55,7 @@ export const render = _withId(function render(_ctx, _cache) {
         name: i,
         fn: _withId(() => [
           _createVNode(\\"div\\")
-        ])
+        ], _ctx)
       }
     })
   ]), 1024 /* DYNAMIC_SLOTS */))
@@ -63,7 +63,7 @@ export const render = _withId(function render(_ctx, _cache) {
 `;
 
 exports[`scopeId compiler support should wrap named slots 1`] = `
-"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
+"import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, createVNode as _createVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, openBlock as _openBlock, createBlock as _createBlock, withScopeId as _withScopeId } from \\"vue\\"
 const _withId = _withScopeId(\\"test\\")
 
 export const render = _withId(function render(_ctx, _cache) {
@@ -72,10 +72,10 @@ export const render = _withId(function render(_ctx, _cache) {
   return (_openBlock(), _createBlock(_component_Child, null, {
     foo: _withId(({ msg }) => [
       _createTextVNode(_toDisplayString(msg), 1 /* TEXT */)
-    ]),
+    ], _ctx),
     bar: _withId(() => [
       _createVNode(\\"div\\")
-    ]),
+    ], _ctx),
     _: 1
   }))
 })"
index 0d0ddd8c7e6b0e35e7c9de05e5a9e32b43c73175..a7fdc60aa3143894b5e974c81f47721528370773 100644 (file)
@@ -239,14 +239,14 @@ exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expr
 
 return function render(_ctx, _cache) {
   with (_ctx) {
-    const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+    const { toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
 
     const _component_Comp = _resolveComponent(\\"Comp\\")
 
     return (_openBlock(), _createBlock(_component_Comp, null, {
-      default: ({ foo }) => [
+      default: _withCtx(({ foo }) => [
         _createTextVNode(_toDisplayString(_ctx.foo), 1 /* TEXT */)
-      ],
+      ], _ctx),
       _: 1
     }))
   }
index 6e3414a9ff738922079460919a0a85f6405fdc42..a1bdf0747e7e4f429e20b2d912cbb151fb406a9f 100644 (file)
@@ -1,36 +1,36 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`compiler: transform component slots dynamically named slots 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
 
   return (_openBlock(), _createBlock(_component_Comp, null, {
-    [_ctx.one]: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)],
-    [_ctx.two]: ({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)],
+    [_ctx.one]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx),
+    [_ctx.two]: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], _ctx),
     _: 1
   }, 1024 /* DYNAMIC_SLOTS */))
 }"
 `;
 
 exports[`compiler: transform component slots implicit default slot 1`] = `
-"const { createVNode: _createVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
 
   return (_openBlock(), _createBlock(_component_Comp, null, {
-    default: () => [
+    default: _withCtx(() => [
       _createVNode(\\"div\\")
-    ],
+    ], _ctx),
     _: 1
   }))
 }"
 `;
 
 exports[`compiler: transform component slots named slot with v-for w/ prefixIdentifiers: true 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, renderList: _renderList, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
@@ -39,7 +39,7 @@ return function render(_ctx, _cache) {
     _renderList(_ctx.list, (name) => {
       return {
         name: name,
-        fn: () => [_toDisplayString(name)]
+        fn: _withCtx(() => [_toDisplayString(name)], _ctx)
       }
     })
   ]), 1024 /* DYNAMIC_SLOTS */))
@@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
 `;
 
 exports[`compiler: transform component slots named slot with v-if + prefixIdentifiers: true 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
@@ -56,7 +56,7 @@ return function render(_ctx, _cache) {
     (_ctx.ok)
       ? {
           name: \\"one\\",
-          fn: (props) => [_toDisplayString(props)]
+          fn: _withCtx((props) => [_toDisplayString(props)], _ctx)
         }
       : undefined
   ]), 1024 /* DYNAMIC_SLOTS */))
@@ -68,7 +68,7 @@ exports[`compiler: transform component slots named slot with v-if + v-else-if +
 
 return function render(_ctx, _cache) {
   with (_ctx) {
-    const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+    const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
 
     const _component_Comp = _resolveComponent(\\"Comp\\")
 
@@ -76,16 +76,16 @@ return function render(_ctx, _cache) {
       ok
         ? {
             name: \\"one\\",
-            fn: () => [\\"foo\\"]
+            fn: _withCtx(() => [\\"foo\\"], _ctx)
           }
         : orNot
           ? {
               name: \\"two\\",
-              fn: (props) => [\\"bar\\"]
+              fn: _withCtx((props) => [\\"bar\\"], _ctx)
             }
           : {
               name: \\"one\\",
-              fn: () => [\\"baz\\"]
+              fn: _withCtx(() => [\\"baz\\"], _ctx)
             }
     ]), 1024 /* DYNAMIC_SLOTS */))
   }
@@ -97,7 +97,7 @@ exports[`compiler: transform component slots named slot with v-if 1`] = `
 
 return function render(_ctx, _cache) {
   with (_ctx) {
-    const { resolveComponent: _resolveComponent, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+    const { resolveComponent: _resolveComponent, withCtx: _withCtx, createSlots: _createSlots, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
 
     const _component_Comp = _resolveComponent(\\"Comp\\")
 
@@ -105,7 +105,7 @@ return function render(_ctx, _cache) {
       ok
         ? {
             name: \\"one\\",
-            fn: () => [\\"hello\\"]
+            fn: _withCtx(() => [\\"hello\\"], _ctx)
           }
         : undefined
     ]), 1024 /* DYNAMIC_SLOTS */))
@@ -114,14 +114,14 @@ return function render(_ctx, _cache) {
 `;
 
 exports[`compiler: transform component slots named slots 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
 
   return (_openBlock(), _createBlock(_component_Comp, null, {
-    one: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)],
-    two: ({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)],
+    one: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx),
+    two: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)], _ctx),
     _: 1
   }))
 }"
@@ -132,16 +132,16 @@ exports[`compiler: transform component slots named slots w/ implicit default slo
 
 return function render(_ctx, _cache) {
   with (_ctx) {
-    const { createVNode: _createVNode, resolveComponent: _resolveComponent, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+    const { createVNode: _createVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = _Vue
 
     const _component_Comp = _resolveComponent(\\"Comp\\")
 
     return (_openBlock(), _createBlock(_component_Comp, null, {
-      one: () => [\\"foo\\"],
-      default: () => [
+      one: _withCtx(() => [\\"foo\\"], _ctx),
+      default: _withCtx(() => [
         \\"bar\\",
         _createVNode(\\"span\\")
-      ],
+      ], _ctx),
       _: 1
     }))
   }
@@ -149,36 +149,36 @@ return function render(_ctx, _cache) {
 `;
 
 exports[`compiler: transform component slots nested slots scoping 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Inner = _resolveComponent(\\"Inner\\")
   const _component_Comp = _resolveComponent(\\"Comp\\")
 
   return (_openBlock(), _createBlock(_component_Comp, null, {
-    default: ({ foo }) => [
+    default: _withCtx(({ foo }) => [
       _createVNode(_component_Inner, null, {
-        default: ({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)],
+        default: _withCtx(({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)], _ctx),
         _: 1
       }, 1024 /* DYNAMIC_SLOTS */),
       \\" \\",
       _toDisplayString(foo),
       _toDisplayString(_ctx.bar),
       _toDisplayString(_ctx.baz)
-    ],
+    ], _ctx),
     _: 1
   }))
 }"
 `;
 
 exports[`compiler: transform component slots on-component default slot 1`] = `
-"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
+"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = Vue
 
 return function render(_ctx, _cache) {
   const _component_Comp = _resolveComponent(\\"Comp\\")
 
   return (_openBlock(), _createBlock(_component_Comp, null, {
-    default: ({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)],
+    default: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)], _ctx),
     _: 1
   }))
 }"
index 4d0b7298a292205661fb1056c7a65f3c0d757317..b83d2f3fbf8f44e26299fbb8e1692dc2700bc73b 100644 (file)
@@ -768,6 +768,8 @@ function genFunctionExpression(
 
   if (genScopeId) {
     push(`_withId(`)
+  } else if (isSlot) {
+    push(`_withCtx(`)
   }
   push(`(`, node)
   if (isArray(params)) {
@@ -796,8 +798,8 @@ function genFunctionExpression(
     deindent()
     push(`}`)
   }
-  if (genScopeId) {
-    push(`)`)
+  if (genScopeId || isSlot) {
+    push(`, _ctx)`)
   }
 }
 
index 11267c92989f8a4a682b3733f9e145ca06e7bc52..39409a6ba51fb22af22c86610193e0cfea196147 100644 (file)
@@ -26,6 +26,7 @@ export const SET_BLOCK_TRACKING = Symbol(__DEV__ ? `setBlockTracking` : ``)
 export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``)
 export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
 export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``)
+export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
 
 // Name mapping for runtime helpers that need to be imported from 'vue' in
 // generated code. Make sure these are correctly exported in the runtime!
@@ -56,7 +57,8 @@ export const helperNameMap: any = {
   [SET_BLOCK_TRACKING]: `setBlockTracking`,
   [PUSH_SCOPE_ID]: `pushScopeId`,
   [POP_SCOPE_ID]: `popScopeId`,
-  [WITH_SCOPE_ID]: `withScopeId`
+  [WITH_SCOPE_ID]: `withScopeId`,
+  [WITH_CTX]: `withCtx`
 }
 
 export function registerRuntimeHelpers(helpers: any) {
index 0207cd7c25c3d353d6d035d282c79f2904df3525..d02cda50dadc5478f506c8caed787384095397ed 100644 (file)
@@ -25,7 +25,7 @@ import {
 import { TransformContext, NodeTransform } from '../transform'
 import { createCompilerError, ErrorCodes } from '../errors'
 import { findDir, isTemplateNode, assert, isVSlot, hasScopeRef } from '../utils'
-import { CREATE_SLOTS, RENDER_LIST } from '../runtimeHelpers'
+import { CREATE_SLOTS, RENDER_LIST, WITH_CTX } from '../runtimeHelpers'
 import { parseForExpression, createForLoopParams } from './vFor'
 
 const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
@@ -119,6 +119,8 @@ export function buildSlots(
   slots: SlotsExpression
   hasDynamicSlots: boolean
 } {
+  context.helper(WITH_CTX)
+
   const { children, loc } = node
   const slotsProperties: Property[] = []
   const dynamicSlots: (ConditionalExpression | CallExpression)[] = []
index d4df85384694b8df378ec50b76ab358f68c4d75e..52acbfabf86ac6fb5a30d39543153e1042983171 100644 (file)
@@ -3,7 +3,7 @@ import { compile } from '../src'
 describe('ssr: components', () => {
   test('basic', () => {
     expect(compile(`<foo id="a" :prop="b" />`).code).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
@@ -20,7 +20,7 @@ describe('ssr: components', () => {
   test('dynamic component', () => {
     expect(compile(`<component is="foo" prop="b" />`).code)
       .toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
@@ -32,7 +32,7 @@ describe('ssr: components', () => {
 
     expect(compile(`<component :is="foo" prop="b" />`).code)
       .toMatchInlineSnapshot(`
-      "const { resolveDynamicComponent: _resolveDynamicComponent } = require(\\"vue\\")
+      "const { resolveDynamicComponent: _resolveDynamicComponent, withCtx: _withCtx } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
@@ -44,14 +44,14 @@ describe('ssr: components', () => {
   describe('slots', () => {
     test('implicit default slot', () => {
       expect(compile(`<foo>hello<div/></foo>`).code).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
           const _component_foo = _resolveComponent(\\"foo\\")
 
           _push(_ssrRenderComponent(_component_foo, null, {
-            default: (_, _push, _parent, _scopeId) => {
+            default: _withCtx((_, _push, _parent, _scopeId) => {
               if (_push) {
                 _push(\`hello<div\${_scopeId}></div>\`)
               } else {
@@ -60,7 +60,7 @@ describe('ssr: components', () => {
                   _createVNode(\\"div\\")
                 ]
               }
-            },
+            }, _ctx),
             _: 1
           }, _parent))
         }"
@@ -70,14 +70,14 @@ describe('ssr: components', () => {
     test('explicit default slot', () => {
       expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
         .toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
           const _component_foo = _resolveComponent(\\"foo\\")
 
           _push(_ssrRenderComponent(_component_foo, null, {
-            default: ({ msg }, _push, _parent, _scopeId) => {
+            default: _withCtx(({ msg }, _push, _parent, _scopeId) => {
               if (_push) {
                 _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
               } else {
@@ -85,7 +85,7 @@ describe('ssr: components', () => {
                   _createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */)
                 ]
               }
-            },
+            }, _ctx),
             _: 1
           }, _parent))
         }"
@@ -99,14 +99,14 @@ describe('ssr: components', () => {
         <template v-slot:named>bar</template>
       </foo>`).code
       ).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
           const _component_foo = _resolveComponent(\\"foo\\")
 
           _push(_ssrRenderComponent(_component_foo, null, {
-            default: (_, _push, _parent, _scopeId) => {
+            default: _withCtx((_, _push, _parent, _scopeId) => {
               if (_push) {
                 _push(\`foo\`)
               } else {
@@ -114,8 +114,8 @@ describe('ssr: components', () => {
                   _createTextVNode(\\"foo\\")
                 ]
               }
-            },
-            named: (_, _push, _parent, _scopeId) => {
+            }, _ctx),
+            named: _withCtx((_, _push, _parent, _scopeId) => {
               if (_push) {
                 _push(\`bar\`)
               } else {
@@ -123,7 +123,7 @@ describe('ssr: components', () => {
                   _createTextVNode(\\"bar\\")
                 ]
               }
-            },
+            }, _ctx),
             _: 1
           }, _parent))
         }"
@@ -136,7 +136,7 @@ describe('ssr: components', () => {
         <template v-slot:named v-if="ok">foo</template>
       </foo>`).code
       ).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
@@ -146,7 +146,7 @@ describe('ssr: components', () => {
             (_ctx.ok)
               ? {
                   name: \\"named\\",
-                  fn: (_, _push, _parent, _scopeId) => {
+                  fn: _withCtx((_, _push, _parent, _scopeId) => {
                     if (_push) {
                       _push(\`foo\`)
                     } else {
@@ -154,7 +154,7 @@ describe('ssr: components', () => {
                         _createTextVNode(\\"foo\\")
                       ]
                     }
-                  }
+                  }, _ctx)
                 }
               : undefined
           ]), _parent))
@@ -168,7 +168,7 @@ describe('ssr: components', () => {
         <template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
       </foo>`).code
       ).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
@@ -178,7 +178,7 @@ describe('ssr: components', () => {
             _renderList(_ctx.names, (key) => {
               return {
                 name: key,
-                fn: ({ msg }, _push, _parent, _scopeId) => {
+                fn: _withCtx(({ msg }, _push, _parent, _scopeId) => {
                   if (_push) {
                     _push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
                   } else {
@@ -186,7 +186,7 @@ describe('ssr: components', () => {
                       _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
                     ]
                   }
-                }
+                }, _ctx)
               }
             })
           ]), _parent))
@@ -209,14 +209,14 @@ describe('ssr: components', () => {
         </template>
       </foo>`).code
       ).toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
           const _component_foo = _resolveComponent(\\"foo\\")
 
           _push(_ssrRenderComponent(_component_foo, null, {
-            foo: ({ list }, _push, _parent, _scopeId) => {
+            foo: _withCtx(({ list }, _push, _parent, _scopeId) => {
               if (_push) {
                 if (_ctx.ok) {
                   _push(\`<div\${_scopeId}><!--[-->\`)
@@ -238,8 +238,8 @@ describe('ssr: components', () => {
                     : _createCommentVNode(\\"v-if\\", true)
                 ]
               }
-            },
-            bar: ({ ok }, _push, _parent, _scopeId) => {
+            }, _ctx),
+            bar: _withCtx(({ ok }, _push, _parent, _scopeId) => {
               if (_push) {
                 if (ok) {
                   _push(\`<div\${_scopeId}><!--[-->\`)
@@ -261,7 +261,7 @@ describe('ssr: components', () => {
                     : _createCommentVNode(\\"v-if\\", true)
                 ]
               }
-            },
+            }, _ctx),
             _: 1
           }, _parent))
         }"
@@ -287,7 +287,7 @@ describe('ssr: components', () => {
 
       expect(compile(`<keep-alive><foo/></keep-alive>`).code)
         .toMatchInlineSnapshot(`
-        "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+        "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
         const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
         return function ssrRender(_ctx, _push, _parent) {
index 8cb8c1ec6689bdb244eb048ac1ffb85416eaad43..fe9671b805758ae424b5263caef87f6016ab1998 100644 (file)
@@ -23,14 +23,14 @@ describe('ssr: scopeId', () => {
         scopeId
       }).code
     ).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent, createTextVNode: _createTextVNode } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
         const _component_foo = _resolveComponent(\\"foo\\")
 
         _push(_ssrRenderComponent(_component_foo, null, {
-          default: (_, _push, _parent, _scopeId) => {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
             if (_push) {
               _push(\`foo\`)
             } else {
@@ -38,7 +38,7 @@ describe('ssr: scopeId', () => {
                 _createTextVNode(\\"foo\\")
               ]
             }
-          },
+          }, _ctx),
           _: 1
         }, _parent))
       }"
@@ -51,14 +51,14 @@ describe('ssr: scopeId', () => {
         scopeId
       }).code
     ).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent, createVNode: _createVNode } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
         const _component_foo = _resolveComponent(\\"foo\\")
 
         _push(_ssrRenderComponent(_component_foo, null, {
-          default: (_, _push, _parent, _scopeId) => {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
             if (_push) {
               _push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
             } else {
@@ -66,7 +66,7 @@ describe('ssr: scopeId', () => {
                 _createVNode(\\"span\\", null, \\"hello\\")
               ]
             }
-          },
+          }, _ctx),
           _: 1
         }, _parent))
       }"
@@ -79,7 +79,7 @@ describe('ssr: scopeId', () => {
         scopeId
       }).code
     ).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent, createVNode: _createVNode } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
@@ -87,11 +87,11 @@ describe('ssr: scopeId', () => {
         const _component_bar = _resolveComponent(\\"bar\\")
 
         _push(_ssrRenderComponent(_component_foo, null, {
-          default: (_, _push, _parent, _scopeId) => {
+          default: _withCtx((_, _push, _parent, _scopeId) => {
             if (_push) {
               _push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
               _push(_ssrRenderComponent(_component_bar, null, {
-                default: (_, _push, _parent, _scopeId) => {
+                default: _withCtx((_, _push, _parent, _scopeId) => {
                   if (_push) {
                     _push(\`<span data-v-xxxxxxx\${_scopeId}></span>\`)
                   } else {
@@ -99,21 +99,21 @@ describe('ssr: scopeId', () => {
                       _createVNode(\\"span\\")
                     ]
                   }
-                },
+                }, _ctx),
                 _: 1
               }, _parent))
             } else {
               return [
                 _createVNode(\\"span\\", null, \\"hello\\"),
                 _createVNode(_component_bar, null, {
-                  default: () => [
+                  default: _withCtx(() => [
                     _createVNode(\\"span\\")
-                  ],
+                  ], _ctx),
                   _: 1
                 })
               ]
             }
-          },
+          }, _ctx),
           _: 1
         }, _parent))
       }"
index 7f38f513458b1a5d6e7b57f7978311be50b233fc..83a75cf91eb61ec430ec700771e31a0cdd9e8204 100644 (file)
@@ -3,7 +3,7 @@ import { compile } from '../src'
 describe('ssr compile: suspense', () => {
   test('implicit default', () => {
     expect(compile(`<suspense><foo/></suspense>`).code).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
@@ -30,7 +30,7 @@ describe('ssr compile: suspense', () => {
       </template>
     </suspense>`).code
     ).toMatchInlineSnapshot(`
-      "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+      "const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
       const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\")
 
       return function ssrRender(_ctx, _push, _parent) {
index 1130c9b7ade4f224e7c6365b034c3aba1ffa223d..523565929352e5a326e8c62b9d5c62324686e733 100644 (file)
@@ -18,7 +18,6 @@ import { warn } from './warning'
 // resolveComponent, resolveDirective) during render
 export let currentRenderingInstance: ComponentInternalInstance | null = null
 
-// exposed for server-renderer only
 export function setCurrentRenderingInstance(
   instance: ComponentInternalInstance | null
 ) {
index 32c7c99aa9f82d84c1b1b1e54fc6120751512823..a8ee24016df896ebc2cb92e6b58989fc0c7839b5 100644 (file)
@@ -2,6 +2,9 @@
 // These are only used in esm-bundler builds, but since exports cannot be
 // conditional, we can only drop inner implementations in non-bundler builds.
 
+import { ComponentInternalInstance } from '../component'
+import { withCtx } from './withRenderContext'
+
 export let currentScopeId: string | null = null
 const scopeIdStack: string[] = []
 
@@ -20,13 +23,14 @@ export function popScopeId() {
 
 export function withScopeId(id: string): <T extends Function>(fn: T) => T {
   if (__BUNDLER__) {
-    return ((fn: Function) => {
-      return function(this: any) {
+    return ((fn: Function, ctx?: ComponentInternalInstance) => {
+      function renderWithId(this: any) {
         pushScopeId(id)
         const res = fn.apply(this, arguments)
         popScopeId()
         return res
       }
+      return ctx ? withCtx(renderWithId, ctx) : renderWithId
     }) as any
   } else {
     return undefined as any
diff --git a/packages/runtime-core/src/helpers/withRenderContext.ts b/packages/runtime-core/src/helpers/withRenderContext.ts
new file mode 100644 (file)
index 0000000..47ed809
--- /dev/null
@@ -0,0 +1,16 @@
+import { Slot } from '../componentSlots'
+import { ComponentInternalInstance } from '../component'
+import {
+  setCurrentRenderingInstance,
+  currentRenderingInstance
+} from '../componentRenderUtils'
+
+export function withCtx(fn: Slot, ctx: ComponentInternalInstance) {
+  return function renderFnWithContext() {
+    const owner = currentRenderingInstance
+    setCurrentRenderingInstance(ctx)
+    const res = fn.apply(null, arguments)
+    setCurrentRenderingInstance(owner)
+    return res
+  }
+}
index 4fbf060e9f755af87b35f6e588c7aa63de5010bd..b05acf093c56c7fba030a59bc4ca1ad4d7eca099 100644 (file)
@@ -85,6 +85,7 @@ export {
 
 // For compiler generated code
 // should sync with '@vue/compiler-core/src/runtimeConstants.ts'
+export { withCtx } from './helpers/withRenderContext'
 export { withDirectives } from './directives'
 export {
   resolveComponent,