import type { RawSlots, Slots } from '../componentSlots' import { type ContextualRenderFn, currentRenderingInstance, } from '../componentRenderContext' import { Comment, Fragment, type VNode, type VNodeArrayChildren, createBlock, createVNode, isVNode, openBlock, } from '../vnode' import { PatchFlags, SlotFlags, isSymbol } from '@vue/shared' import { warn } from '../warning' import { isAsyncWrapper } from '../apiAsyncComponent' import type { Data } from '../component' /** * Compiler runtime helper for rendering `` * @private */ export function renderSlot( slots: Slots, name: string, props: Data = {}, // this is not a user-facing function, so the fallback is always generated by // the compiler and guaranteed to be a function returning an array fallback?: () => VNodeArrayChildren, noSlotted?: boolean, ): VNode { if ( currentRenderingInstance && (currentRenderingInstance.ce || (currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.ce)) ) { // in custom element mode, render as actual slot outlets // wrap it with a fragment because in shadowRoot: false mode the slot // element gets replaced by injected content if (name !== 'default') props.name = name return ( openBlock(), createBlock( Fragment, null, [createVNode('slot', props, fallback && fallback())], PatchFlags.STABLE_FRAGMENT, ) ) } let slot = slots[name] if (__DEV__ && slot && slot.length > 1) { warn( `SSR-optimized slot function detected in a non-SSR-optimized render ` + `function. You need to mark this component with $dynamic-slots in the ` + `parent template.`, ) slot = () => [] } // a compiled slot disables block tracking by default to avoid manual // invocation interfering with template-based block tracking, but in // `renderSlot` we can be sure that it's template-based so we can force // enable it. if (slot && (slot as ContextualRenderFn)._c) { ;(slot as ContextualRenderFn)._d = false } openBlock() const validSlotContent = slot && ensureValidVNode(slot(props)) const slotKey = props.key || // slot content array of a dynamic conditional slot may have a branch // key attached in the `createSlots` helper, respect that (validSlotContent && (validSlotContent as any).key) const rendered = createBlock( Fragment, { key: (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) + // #7256 force differentiate fallback content from actual content (!validSlotContent && fallback ? '_fb' : ''), }, validSlotContent || (fallback ? fallback() : []), validSlotContent && (slots as RawSlots)._ === SlotFlags.STABLE ? PatchFlags.STABLE_FRAGMENT : PatchFlags.BAIL, ) if (!noSlotted && rendered.scopeId) { rendered.slotScopeIds = [rendered.scopeId + '-s'] } if (slot && (slot as ContextualRenderFn)._c) { ;(slot as ContextualRenderFn)._d = true } return rendered } export function ensureValidVNode( vnodes: VNodeArrayChildren, ): VNodeArrayChildren | null { return vnodes.some(child => { if (!isVNode(child)) return true if (child.type === Comment) return false if ( child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren) ) return false return true }) ? vnodes : null }