All files / runtime-core/src/helpers renderSlot.ts

100% Statements 74/74
97.87% Branches 46/47
100% Functions 3/3
100% Lines 74/74

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 1183x                                               3x 102x 102x 102x     102x 102x 102x 102x 102x 78x 69x 9x 102x       24x 24x 24x 24x 24x 24x 24x 24x 24x   24x   78x   102x 1x 1x     1x 1x 1x           102x 48x 48x 78x 78x 102x 102x     75x 102x 102x 102x 102x 102x   102x 102x 102x 102x 27x 51x 102x 102x 7x 7x 102x 48x 48x 78x 78x   3x 88x 88x 88x 86x 86x 78x 78x 5x   86x 77x 88x 77x 11x 88x  
import type { Data } from '../component'
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'
 
/**
 * Compiler runtime helper for rendering `<slot/>`
 * @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!.ce ||
    (currentRenderingInstance!.parent &&
      isAsyncWrapper(currentRenderingInstance!.parent) &&
      currentRenderingInstance!.parent.ce)
  ) {
    // in custom element mode, render <slot/> 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
}