type RawSlots,
type StaticSlots,
type VaporSlot,
+ currentSlotOwner,
dynamicSlotsProxyHandlers,
- getParentInstance,
getSlot,
- setCurrentSlotConsumer,
+ setCurrentSlotOwner,
} from './componentSlots'
import { hmrReload, hmrRerender } from './hmr'
import {
resetInsertionState()
}
- const parentInstance = getParentInstance()
-
let prevSuspense: SuspenseBoundary | null = null
- if (__FEATURE_SUSPENSE__ && parentInstance && parentInstance.suspense) {
- prevSuspense = setParentSuspense(parentInstance.suspense)
+ if (__FEATURE_SUSPENSE__ && currentInstance && currentInstance.suspense) {
+ prevSuspense = setParentSuspense(currentInstance.suspense)
}
if (
(isSingleRoot ||
// transition has attrs fallthrough
- (parentInstance && isVaporTransition(parentInstance!.type))) &&
+ (currentInstance && isVaporTransition(currentInstance!.type))) &&
component.inheritAttrs !== false &&
- isVaporComponent(parentInstance) &&
- parentInstance.hasFallthrough
+ isVaporComponent(currentInstance) &&
+ currentInstance.hasFallthrough
) {
// check if we are the single root of the parent
// if yes, inject parent attrs as dynamic props source
- const attrs = parentInstance.attrs
+ const attrs = currentInstance.attrs
if (rawProps && rawProps !== EMPTY_OBJ) {
;((rawProps as RawProps).$ || ((rawProps as RawProps).$ = [])).push(
() => attrs,
}
// keep-alive
- if (parentInstance && parentInstance.vapor && isKeepAlive(parentInstance)) {
- const cached = (parentInstance as KeepAliveInstance).getCachedComponent(
+ if (
+ currentInstance &&
+ currentInstance.vapor &&
+ isKeepAlive(currentInstance)
+ ) {
+ const cached = (currentInstance as KeepAliveInstance).getCachedComponent(
component,
)
// @ts-expect-error
// vdom interop enabled and component is not an explicit vapor component
if (appContext.vapor && !component.__vapor) {
- const prevSlotConsumer = setCurrentSlotConsumer(null)
const frag = appContext.vapor.vdomMount(
component as any,
- parentInstance as any,
+ currentInstance as any,
rawProps,
rawSlots,
)
- setCurrentSlotConsumer(prevSlotConsumer)
if (!isHydrating) {
if (_insertionParent) insert(frag, _insertionParent, _insertionAnchor)
} else {
rawSlots as RawSlots,
appContext,
once,
- parentInstance,
)
- // set currentSlotConsumer to null to avoid affecting the child components
- const prevSlotConsumer = setCurrentSlotConsumer(null)
+ // reset currentSlotOwner to null to avoid affecting the child components
+ const prevSlotOwner = setCurrentSlotOwner(null)
// HMR
if (__DEV__) {
endMeasure(instance, 'init')
}
- if (__FEATURE_SUSPENSE__ && parentInstance && parentInstance.suspense) {
+ if (__FEATURE_SUSPENSE__ && currentInstance && currentInstance.suspense) {
setParentSuspense(prevSuspense)
}
- // restore currentSlotConsumer to previous value after setupFn is called
- setCurrentSlotConsumer(prevSlotConsumer)
+ // restore currentSlotOwner to previous value after setupFn is called
+ setCurrentSlotOwner(prevSlotOwner)
onScopeDispose(() => unmountComponent(instance), true)
if (_insertionParent || isHydrating) {
rawSlots?: RawSlots | null,
appContext?: GenericAppContext,
once?: boolean,
- parent: GenericComponentInstance | null = currentInstance,
) {
this.vapor = true
this.uid = nextUid()
this.type = comp
- this.parent = parent
- this.root = parent ? parent.root : this
+ this.parent = currentInstance
- if (parent) {
- this.appContext = parent.appContext
- this.provides = parent.provides
- this.ids = parent.ids
+ if (currentInstance) {
+ this.root = currentInstance.root
+ this.appContext = currentInstance.appContext
+ this.provides = currentInstance.provides
+ this.ids = currentInstance.ids
} else {
+ this.root = this
this.appContext = appContext || emptyContext
this.provides = Object.create(this.appContext.provides)
this.ids = ['', 0, 0]
: rawSlots
: EMPTY_OBJ
- this.scopeId = currentInstance && currentInstance.type.__scopeId
+ // Use currentSlotOwner for scopeId inheritance when inside a slot
+ // This ensures components created in slots inherit the slot owner's scopeId
+ const scopeOwner = currentSlotOwner || currentInstance
+ this.scopeId = scopeOwner && scopeOwner.type.__scopeId
// apply custom element special handling
if (comp.ce) {
;(el as any).$root = isSingleRoot
if (!isHydrating) {
- const scopeId = currentInstance!.type.__scopeId
+ // Use currentSlotOwner for scopeId when inside a slot
+ const scopeOwner = currentSlotOwner || currentInstance
+ const scopeId = scopeOwner!.type.__scopeId
if (scopeId) setScopeId(el, [scopeId])
}
currentInstance,
isAsyncWrapper,
isRef,
- setCurrentInstance,
} from '@vue/runtime-dom'
import type { LooseRawProps, VaporComponentInstance } from './component'
import { renderEffect } from './renderEffect'
}
}
-export let currentSlotConsumer: GenericComponentInstance | null = null
+/**
+ * Tracks the slot owner (the component that defines the slot content).
+ * This is used for:
+ * 1. Getting the correct rawSlots in forwarded slots (via createSlot)
+ * 2. Inheriting the slot owner's scopeId
+ */
+export let currentSlotOwner: VaporComponentInstance | null = null
-export function setCurrentSlotConsumer(
- consumer: GenericComponentInstance | null,
-): GenericComponentInstance | null {
+export function setCurrentSlotOwner(
+ owner: VaporComponentInstance | null,
+): VaporComponentInstance | null {
try {
- return currentSlotConsumer
+ return currentSlotOwner
} finally {
- currentSlotConsumer = consumer
+ currentSlotOwner = owner
}
}
/**
- * use currentSlotConsumer as parent, the currentSlotConsumer will be reset to null
- * before setupFn call to avoid affecting children and restore to previous value
- * after setupFn is called
+ * Get the effective slot instance for accessing rawSlots and scopeId.
+ * Prefers currentSlotOwner (if inside a slot), falls back to currentInstance.
*/
-export function getParentInstance(): GenericComponentInstance | null {
- return currentSlotConsumer || currentInstance
+export function getSlotInstance(): VaporComponentInstance {
+ return (currentSlotOwner || currentInstance) as VaporComponentInstance
}
/**
- * Wrap a slot function to memoize currentInstance
- * 1. ensure correct currentInstance in forwarded slots
- * 2. elements created in the slot inherit the slot owner's scopeId
+ * Wrap a slot function to track the slot owner.
+ *
+ * This ensures:
+ * 1. createSlot gets rawSlots from the correct component (slot owner)
+ * 2. Elements inherit the slot owner's scopeId
*/
export function withVaporCtx(fn: Function): BlockFn {
- const owner = currentInstance
+ const owner = currentInstance as VaporComponentInstance
return (...args: any[]) => {
- const prev = setCurrentInstance(owner)
- const prevConsumer = setCurrentSlotConsumer(prev[0])
+ const prevOwner = setCurrentSlotOwner(owner)
try {
return fn(...args)
} finally {
- setCurrentInstance(...prev)
- setCurrentSlotConsumer(prevConsumer)
+ setCurrentSlotOwner(prevOwner)
}
}
}
const _isLastInsertion = isLastInsertion
if (!isHydrating) resetInsertionState()
- const instance = currentInstance as VaporComponentInstance
+ // Use slot owner if inside a slot (forwarded slots), otherwise use currentInstance
+ const instance = getSlotInstance()
const rawSlots = instance.rawSlots
const slotProps = rawProps
? new Proxy(rawProps, rawPropsProxyHandlers)
MismatchTypes,
type TeleportProps,
type TeleportTargetElement,
+ currentInstance,
isMismatchAllowed,
isTeleportDeferred,
isTeleportDisabled,
setCurrentHydrationNode,
} from '../dom/hydration'
import { applyTransitionHooks } from './Transition'
-import { getParentInstance } from '../componentSlots'
export const VaporTeleportImpl = {
name: 'VaporTeleport',
super([])
this.rawProps = props
this.rawSlots = slots
- this.parentComponent = getParentInstance()
+ this.parentComponent = currentInstance
this.anchor = isHydrating
? undefined
: __DEV__
type GenericComponentInstance,
type TransitionHooks,
type VNode,
+ currentInstance,
queuePostFlushCb,
setCurrentInstance,
warnExtraneousAttributes,
locateFragmentEndAnchor,
locateHydrationNode,
} from './dom/hydration'
-import { getParentInstance } from './componentSlots'
import { isArray } from '@vue/shared'
import { renderEffect } from './renderEffect'
constructor(anchorLabel?: string) {
super([])
- this.parentComponent = getParentInstance()
+ this.parentComponent = currentInstance
if (isHydrating) {
this.anchorLabel = anchorLabel
locateHydrationNode()
} from '@vue/shared'
import { type RawProps, rawPropsProxyHandlers } from './componentProps'
import type { RawSlots, VaporSlot } from './componentSlots'
-import { currentSlotScopeIds } from './componentSlots'
+import { currentSlotScopeIds, getSlotInstance } from './componentSlots'
import { renderEffect } from './renderEffect'
import { _next, createTextNode } from './dom/node'
import { optimizePropertyLookup } from './dom/prop'
rawSlots as RawSlots,
parentComponent ? parentComponent.appContext : undefined,
undefined,
- parentComponent,
)
// overwrite how the vdom instance handles props
frag.nodes = vnode.el as any
}
- vnode.scopeId = (currentInstance && currentInstance.type.__scopeId) || null
+ // Use currentSlotOwner for scopeId when inside a slot
+ const scopeOwner = getSlotInstance()
+ vnode.scopeId = (scopeOwner && scopeOwner.type.__scopeId) || null
vnode.slotScopeIds = currentSlotScopeIds
frag.insert = (parentNode, anchor, transition) => {