Преглед изворни кода

feat(runtime-vapor): add withVaporCtx helper to manage currentInstance context in slot blocks (#14007)

edison пре 7 месеци
родитељ
комит
d381c2f804

+ 3 - 3
packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap

@@ -26,7 +26,7 @@ export function render(_ctx) {
 `;
 
 exports[`compile > custom directive > component 1`] = `
-"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, createIf as _createIf, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, resolveDirective as _resolveDirective, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, withVaporDirectives as _withVaporDirectives, createIf as _createIf, withVaporCtx as _withVaporCtx, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
@@ -35,7 +35,7 @@ export function render(_ctx) {
   const _directive_hello = _resolveDirective("hello")
   const _directive_test = _resolveDirective("test")
   const n4 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createIf(() => (true), () => {
         const n3 = t0()
         _setInsertionState(n3, null, true)
@@ -44,7 +44,7 @@ export function render(_ctx) {
         return n3
       })
       return n0
-    }
+    })
   }, true)
   _withVaporDirectives(n4, [[_directive_test]])
   return n4

+ 76 - 76
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap

@@ -1,7 +1,7 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`compiler: transform slot > dynamic slots name 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template("foo")
 
 export function render(_ctx) {
@@ -10,10 +10,10 @@ export function render(_ctx) {
     $: [
       () => ({
         name: _ctx.name,
-        fn: () => {
+        fn: _withVaporCtx(() => {
           const n0 = t0()
           return n0
-        }
+        })
       })
     ]
   }, true)
@@ -22,7 +22,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > dynamic slots name w/ v-for 1`] = `
-"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
@@ -31,11 +31,11 @@ export function render(_ctx) {
     $: [
       () => (_createForSlots(_ctx.list, (item) => ({
         name: item,
-        fn: (_slotProps0) => {
+        fn: _withVaporCtx((_slotProps0) => {
           const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["bar"])))
           return n0
-        }
+        })
       })))
     ]
   }, true)
@@ -44,7 +44,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > dynamic slots name w/ v-for and provide absent key 1`] = `
-"import { resolveComponent as _resolveComponent, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createForSlots as _createForSlots, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template("foo")
 
 export function render(_ctx) {
@@ -53,10 +53,10 @@ export function render(_ctx) {
     $: [
       () => (_createForSlots(_ctx.list, (_, __, index) => ({
         name: index,
-        fn: () => {
+        fn: _withVaporCtx(() => {
           const n0 = t0()
           return n0
-        }
+        })
       })))
     ]
   }, true)
@@ -65,7 +65,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > dynamic slots name w/ v-if / v-else[-if] 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template("condition slot")
 const t1 = _template("another condition")
 const t2 = _template("else condition")
@@ -77,25 +77,25 @@ export function render(_ctx) {
       () => (_ctx.condition
         ? {
           name: "condition",
-          fn: () => {
+          fn: _withVaporCtx(() => {
             const n0 = t0()
             return n0
-          }
+          })
         }
         : _ctx.anotherCondition
           ? {
             name: "condition",
-            fn: (_slotProps0) => {
+            fn: _withVaporCtx((_slotProps0) => {
               const n2 = t1()
               return n2
-            }
+            })
           }
           : {
             name: "condition",
-            fn: () => {
+            fn: _withVaporCtx(() => {
               const n4 = t2()
               return n4
-            }
+            })
           })
     ]
   }, true)
@@ -104,114 +104,114 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > forwarded slots > <slot w/ nested component> 1`] = `
-"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _createForwardedSlot = _forwardedSlotCreator()
   const _component_Comp = _resolveComponent("Comp")
   const n2 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n1 = _createComponentWithFallback(_component_Comp, null, {
-        "default": () => {
+        "default": _withVaporCtx(() => {
           const n0 = _createForwardedSlot("default", null)
           return n0
-        }
+        })
       })
       return n1
-    }
+    })
   }, true)
   return n2
 }"
 `;
 
 exports[`compiler: transform slot > forwarded slots > <slot> tag only 1`] = `
-"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _createForwardedSlot = _forwardedSlotCreator()
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createForwardedSlot("default", null)
       return n0
-    }
+    })
   }, true)
   return n1
 }"
 `;
 
 exports[`compiler: transform slot > forwarded slots > <slot> tag w/ template 1`] = `
-"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _createForwardedSlot = _forwardedSlotCreator()
   const _component_Comp = _resolveComponent("Comp")
   const n2 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createForwardedSlot("default", null)
       return n0
-    }
+    })
   }, true)
   return n2
 }"
 `;
 
 exports[`compiler: transform slot > forwarded slots > <slot> tag w/ v-for 1`] = `
-"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createFor as _createFor, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createFor as _createFor, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _createForwardedSlot = _forwardedSlotCreator()
   const _component_Comp = _resolveComponent("Comp")
   const n3 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createFor(() => (_ctx.b), (_for_item0) => {
         const n2 = _createForwardedSlot("default", null)
         return n2
       })
       return n0
-    }
+    })
   }, true)
   return n3
 }"
 `;
 
 exports[`compiler: transform slot > forwarded slots > <slot> tag w/ v-if 1`] = `
-"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createIf as _createIf, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { forwardedSlotCreator as _forwardedSlotCreator, resolveComponent as _resolveComponent, createIf as _createIf, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _createForwardedSlot = _forwardedSlotCreator()
   const _component_Comp = _resolveComponent("Comp")
   const n3 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createIf(() => (_ctx.ok), () => {
         const n2 = _createForwardedSlot("default", null)
         return n2
       })
       return n0
-    }
+    })
   }, true)
   return n3
 }"
 `;
 
 exports[`compiler: transform slot > implicit default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template("<div></div>")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = t0()
       return n0
-    }
+    })
   }, true)
   return n1
 }"
 `;
 
 exports[`compiler: transform slot > named slots w/ implicit default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template("foo")
 const t1 = _template("bar")
 const t2 = _template("<span></span>")
@@ -219,63 +219,63 @@ const t2 = _template("<span></span>")
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n4 = _createComponentWithFallback(_component_Comp, null, {
-    "one": () => {
+    "one": _withVaporCtx(() => {
       const n0 = t0()
       return n0
-    },
-    "default": () => {
+    }),
+    "default": _withVaporCtx(() => {
       const n2 = t1()
       const n3 = t2()
       return [n2, n3]
-    }
+    })
   }, true)
   return n4
 }"
 `;
 
 exports[`compiler: transform slot > nested component slot 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, withVaporCtx as _withVaporCtx } from 'vue';
 
 export function render(_ctx) {
   const _component_B = _resolveComponent("B")
   const _component_A = _resolveComponent("A")
   const n1 = _createComponentWithFallback(_component_A, null, {
-    "default": () => {
+    "default": _withVaporCtx(() => {
       const n0 = _createComponentWithFallback(_component_B)
       return n0
-    }
+    })
   }, true)
   return n1
 }"
 `;
 
 exports[`compiler: transform slot > nested slots scoping 1`] = `
-"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Inner = _resolveComponent("Inner")
   const _component_Comp = _resolveComponent("Comp")
   const n5 = _createComponentWithFallback(_component_Comp, null, {
-    "default": (_slotProps0) => {
+    "default": _withVaporCtx((_slotProps0) => {
       const n1 = _createComponentWithFallback(_component_Inner, null, {
-        "default": (_slotProps1) => {
+        "default": _withVaporCtx((_slotProps1) => {
           const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz)))
           return n0
-        }
+        })
       })
       const n3 = t0()
       _renderEffect(() => _setText(n3, " " + _toDisplayString(_slotProps0["foo"] + _ctx.bar + _ctx.baz)))
       return [n1, n3]
-    }
+    })
   }, true)
   return n5
 }"
 `;
 
 exports[`compiler: transform slot > on component dynamically named slot 1`] = `
-"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
@@ -284,11 +284,11 @@ export function render(_ctx) {
     $: [
       () => ({
         name: _ctx.named,
-        fn: (_slotProps0) => {
+        fn: _withVaporCtx((_slotProps0) => {
           const n0 = t0()
           _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
           return n0
-        }
+        })
       })
     ]
   }, true)
@@ -297,48 +297,48 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > on component named slot 1`] = `
-"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
-    "named": (_slotProps0) => {
+    "named": _withVaporCtx((_slotProps0) => {
       const n0 = t0()
       _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
-    }
+    })
   }, true)
   return n1
 }"
 `;
 
 exports[`compiler: transform slot > on-component default slot 1`] = `
-"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
-    "default": (_slotProps0) => {
+    "default": _withVaporCtx((_slotProps0) => {
       const n0 = t0()
       _renderEffect(() => _setText(n0, _toDisplayString(_slotProps0["foo"] + _ctx.bar)))
       return n0
-    }
+    })
   }, true)
   return n1
 }"
 `;
 
 exports[`compiler: transform slot > quote slot name 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback } from 'vue';
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n1 = _createComponentWithFallback(_component_Comp, null, {
-    "nav-bar-title-before": () => {
+    "nav-bar-title-before": _withVaporCtx(() => {
       return null
-    }
+    })
   }, true)
   return n1
 }"
@@ -367,7 +367,7 @@ export function render(_ctx) {
 `;
 
 exports[`compiler: transform slot > with whitespace: 'preserve' > implicit default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" Header ")
 const t1 = _template(" ")
 const t2 = _template("<p></p>")
@@ -375,57 +375,57 @@ const t2 = _template("<p></p>")
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n4 = _createComponentWithFallback(_component_Comp, null, {
-    "header": () => {
+    "header": _withVaporCtx(() => {
       const n0 = t0()
       return n0
-    },
-    "default": () => {
+    }),
+    "default": _withVaporCtx(() => {
       const n2 = t1()
       const n3 = t2()
       return [n2, n3]
-    }
+    })
   }, true)
   return n4
 }"
 `;
 
 exports[`compiler: transform slot > with whitespace: 'preserve' > named default slot + implicit whitespace content 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" Header ")
 const t1 = _template(" Default ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n5 = _createComponentWithFallback(_component_Comp, null, {
-    "header": () => {
+    "header": _withVaporCtx(() => {
       const n0 = t0()
       return n0
-    },
-    "default": () => {
+    }),
+    "default": _withVaporCtx(() => {
       const n3 = t1()
       return n3
-    }
+    })
   }, true)
   return n5
 }"
 `;
 
 exports[`compiler: transform slot > with whitespace: 'preserve' > should not generate whitespace only default slot 1`] = `
-"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
+"import { resolveComponent as _resolveComponent, withVaporCtx as _withVaporCtx, createComponentWithFallback as _createComponentWithFallback, template as _template } from 'vue';
 const t0 = _template(" Header ")
 const t1 = _template(" Footer ")
 
 export function render(_ctx) {
   const _component_Comp = _resolveComponent("Comp")
   const n5 = _createComponentWithFallback(_component_Comp, null, {
-    "header": () => {
+    "header": _withVaporCtx(() => {
       const n0 = t0()
       return n0
-    },
-    "footer": () => {
+    }),
+    "footer": _withVaporCtx(() => {
       const n3 = t1()
       return n3
-    }
+    })
   }, true)
   return n5
 }"

+ 7 - 7
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts

@@ -67,7 +67,7 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`"default": (_slotProps0) =>`)
+    expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
     expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
 
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
@@ -101,7 +101,7 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`"named": (_slotProps0) =>`)
+    expect(code).contains(`"named": _withVaporCtx((_slotProps0) =>`)
     expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
 
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
@@ -130,7 +130,7 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`fn: (_slotProps0) =>`)
+    expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`)
     expect(code).contains(`_slotProps0["foo"] + _ctx.bar`)
 
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
@@ -204,8 +204,8 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`"default": (_slotProps0) =>`)
-    expect(code).contains(`"default": (_slotProps1) =>`)
+    expect(code).contains(`"default": _withVaporCtx((_slotProps0) =>`)
+    expect(code).contains(`"default": _withVaporCtx((_slotProps1) =>`)
     expect(code).contains(`_slotProps0["foo"] + _slotProps1["bar"] + _ctx.baz`)
     expect(code).contains(`_slotProps0["foo"] + _ctx.bar + _ctx.baz`)
 
@@ -282,7 +282,7 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`fn: (_slotProps0) =>`)
+    expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`)
     expect(code).contains(`_setText(n0, _toDisplayString(_slotProps0["bar"]))`)
 
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
@@ -346,7 +346,7 @@ describe('compiler: transform slot', () => {
     )
     expect(code).toMatchSnapshot()
 
-    expect(code).contains(`fn: (_slotProps0) =>`)
+    expect(code).contains(`fn: _withVaporCtx((_slotProps0) =>`)
 
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
       type: IRNodeTypes.CREATE_COMPONENT_NODE,

+ 7 - 1
packages/compiler-vapor/src/generators/component.ts

@@ -29,6 +29,7 @@ import {
 import { genExpression, genVarName } from './expression'
 import { genPropKey, genPropValue } from './prop'
 import {
+  NodeTypes,
   type SimpleExpressionNode,
   createSimpleExpression,
   isMemberExpression,
@@ -407,7 +408,7 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) {
   let propsName: string | undefined
   let exitScope: (() => void) | undefined
   let depth: number | undefined
-  const { props, key } = oper
+  const { props, key, node } = oper
   const idsOfProps = new Set<string>()
 
   if (props) {
@@ -458,5 +459,10 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) {
     ]
   }
 
+  if (node.type === NodeTypes.ELEMENT && !isBuiltInComponent(node.tag)) {
+    // wrap with withVaporCtx to ensure correct currentInstance inside slot
+    blockFn = [`${context.helper('withVaporCtx')}(`, ...blockFn, `)`]
+  }
+
   return blockFn
 }

+ 13 - 1
packages/runtime-vapor/src/componentSlots.ts

@@ -1,7 +1,7 @@
 import { EMPTY_OBJ, NO, hasOwn, isArray, isFunction } from '@vue/shared'
 import { type Block, type BlockFn, insert } from './block'
 import { rawPropsProxyHandlers } from './componentProps'
-import { currentInstance, isRef } from '@vue/runtime-dom'
+import { currentInstance, isRef, setCurrentInstance } from '@vue/runtime-dom'
 import type { LooseRawProps, VaporComponentInstance } from './component'
 import { renderEffect } from './renderEffect'
 import {
@@ -97,6 +97,18 @@ export function getSlot(
   }
 }
 
+export function withVaporCtx(fn: Function): Function {
+  const instance = currentInstance as VaporComponentInstance
+  return (...args: any[]) => {
+    const prev = setCurrentInstance(instance)
+    try {
+      return fn(...args)
+    } finally {
+      setCurrentInstance(...prev)
+    }
+  }
+}
+
 export function forwardedSlotCreator(): (
   name: string | (() => string),
   rawProps?: LooseRawProps | null,

+ 5 - 1
packages/runtime-vapor/src/index.ts

@@ -16,7 +16,11 @@ export {
   isVaporComponent,
 } from './component'
 export { renderEffect } from './renderEffect'
-export { createSlot, forwardedSlotCreator } from './componentSlots'
+export {
+  createSlot,
+  forwardedSlotCreator,
+  withVaporCtx,
+} from './componentSlots'
 export { template } from './dom/template'
 export { createTextNode, child, nthChild, next, txt } from './dom/node'
 export {