2024-06-13 21:47:24 +00:00
|
|
|
import type { Component, PropType, VNode } from 'vue'
|
2024-10-11 10:31:46 +00:00
|
|
|
import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch, withMemo } from 'vue'
|
2022-11-24 12:24:14 +00:00
|
|
|
import { debounce } from 'perfect-debounce'
|
|
|
|
import { hash } from 'ohash'
|
2023-05-01 22:55:24 +00:00
|
|
|
import { appendResponseHeader } from 'h3'
|
2024-10-11 10:31:46 +00:00
|
|
|
import { type ActiveHeadEntry, type Head, injectHead } from '@unhead/vue'
|
2023-05-15 22:43:53 +00:00
|
|
|
import { randomUUID } from 'uncrypto'
|
2023-07-18 15:07:35 +00:00
|
|
|
import { joinURL, withQuery } from 'ufo'
|
2023-07-05 09:48:01 +00:00
|
|
|
import type { FetchResponse } from 'ofetch'
|
2023-12-19 12:21:29 +00:00
|
|
|
import { join } from 'pathe'
|
2023-06-25 16:38:15 +00:00
|
|
|
|
2024-03-08 22:58:37 +00:00
|
|
|
import type { NuxtIslandResponse } from '../types'
|
2023-10-30 21:05:02 +00:00
|
|
|
import { useNuxtApp, useRuntimeConfig } from '../nuxt'
|
|
|
|
import { prerenderRoutes, useRequestEvent } from '../composables/ssr'
|
2024-01-16 13:22:50 +00:00
|
|
|
import { getFragmentHTML } from './utils'
|
2022-11-24 12:24:14 +00:00
|
|
|
|
2023-07-31 12:01:50 +00:00
|
|
|
// @ts-expect-error virtual file
|
2024-03-15 16:16:41 +00:00
|
|
|
import { appBaseURL, remoteComponentIslands, selectiveClient } from '#build/nuxt.config.mjs'
|
2023-07-31 12:01:50 +00:00
|
|
|
|
2022-11-24 12:24:14 +00:00
|
|
|
const pKey = '_islandPromises'
|
2024-01-16 13:22:50 +00:00
|
|
|
const SSR_UID_RE = /data-island-uid="([^"]*)"/
|
2024-01-17 10:48:37 +00:00
|
|
|
const DATA_ISLAND_UID_RE = /data-island-uid(="")?(?!="[^"])/g
|
2024-01-16 13:22:50 +00:00
|
|
|
const SLOTNAME_RE = /data-island-slot="([^"]*)"/g
|
|
|
|
const SLOT_FALLBACK_RE = / data-island-slot="([^"]*)"[^>]*>/g
|
2024-10-22 13:39:50 +00:00
|
|
|
const ISLAND_SCOPE_ID_RE = /^<[^> ]*/
|
2022-11-24 12:24:14 +00:00
|
|
|
|
2024-01-16 13:22:50 +00:00
|
|
|
let id = 1
|
2023-08-07 22:03:40 +00:00
|
|
|
const getId = import.meta.client ? () => (id++).toString() : randomUUID
|
2023-05-22 20:25:04 +00:00
|
|
|
|
2023-12-19 12:21:29 +00:00
|
|
|
const components = import.meta.client ? new Map<string, Component>() : undefined
|
|
|
|
|
2024-03-15 16:16:41 +00:00
|
|
|
async function loadComponents (source = appBaseURL, paths: NuxtIslandResponse['components']) {
|
2024-09-18 19:41:53 +00:00
|
|
|
if (!paths) { return }
|
|
|
|
|
2024-06-13 21:47:24 +00:00
|
|
|
const promises: Array<Promise<void>> = []
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2024-09-18 19:41:53 +00:00
|
|
|
for (const [component, item] of Object.entries(paths)) {
|
2023-12-19 12:21:29 +00:00
|
|
|
if (!(components!.has(component))) {
|
|
|
|
promises.push((async () => {
|
2024-09-18 19:41:53 +00:00
|
|
|
const chunkSource = join(source, item.chunk)
|
2023-12-19 12:21:29 +00:00
|
|
|
const c = await import(/* @vite-ignore */ chunkSource).then(m => m.default || m)
|
|
|
|
components!.set(component, c)
|
|
|
|
})())
|
|
|
|
}
|
|
|
|
}
|
2024-09-18 19:41:53 +00:00
|
|
|
|
2023-12-19 12:21:29 +00:00
|
|
|
await Promise.all(promises)
|
|
|
|
}
|
|
|
|
|
2022-11-24 12:24:14 +00:00
|
|
|
export default defineComponent({
|
|
|
|
name: 'NuxtIsland',
|
2024-09-11 09:26:10 +00:00
|
|
|
inheritAttrs: false,
|
2022-11-24 12:24:14 +00:00
|
|
|
props: {
|
|
|
|
name: {
|
|
|
|
type: String,
|
2024-04-05 18:08:32 +00:00
|
|
|
required: true,
|
2022-11-24 12:24:14 +00:00
|
|
|
},
|
2023-07-31 08:51:09 +00:00
|
|
|
lazy: Boolean,
|
2022-11-24 12:24:14 +00:00
|
|
|
props: {
|
|
|
|
type: Object,
|
2024-04-05 18:08:32 +00:00
|
|
|
default: () => undefined,
|
2022-11-24 12:24:14 +00:00
|
|
|
},
|
|
|
|
context: {
|
|
|
|
type: Object,
|
2024-04-05 18:08:32 +00:00
|
|
|
default: () => ({}),
|
2023-07-30 21:00:41 +00:00
|
|
|
},
|
2024-06-10 22:20:27 +00:00
|
|
|
scopeId: {
|
|
|
|
type: String as PropType<string | undefined | null>,
|
|
|
|
default: () => undefined,
|
|
|
|
},
|
2023-07-30 21:00:41 +00:00
|
|
|
source: {
|
|
|
|
type: String,
|
2024-04-05 18:08:32 +00:00
|
|
|
default: () => undefined,
|
2023-12-19 12:21:29 +00:00
|
|
|
},
|
|
|
|
dangerouslyLoadClientComponents: {
|
|
|
|
type: Boolean,
|
2024-04-05 18:08:32 +00:00
|
|
|
default: false,
|
|
|
|
},
|
2022-11-24 12:24:14 +00:00
|
|
|
},
|
2024-03-06 16:45:43 +00:00
|
|
|
emits: ['error'],
|
|
|
|
async setup (props, { slots, expose, emit }) {
|
2024-01-16 13:22:50 +00:00
|
|
|
let canTeleport = import.meta.server
|
|
|
|
const teleportKey = ref(0)
|
2023-12-19 12:21:29 +00:00
|
|
|
const key = ref(0)
|
|
|
|
const canLoadClientComponent = computed(() => selectiveClient && (props.dangerouslyLoadClientComponents || !props.source))
|
2023-07-30 21:00:41 +00:00
|
|
|
const error = ref<unknown>(null)
|
2023-07-18 15:07:35 +00:00
|
|
|
const config = useRuntimeConfig()
|
2022-11-24 12:24:14 +00:00
|
|
|
const nuxtApp = useNuxtApp()
|
2023-09-28 07:36:13 +00:00
|
|
|
const filteredProps = computed(() => props.props ? Object.fromEntries(Object.entries(props.props).filter(([key]) => !key.startsWith('data-v-'))) : {})
|
|
|
|
const hashId = computed(() => hash([props.name, filteredProps.value, props.context, props.source]))
|
2023-03-20 21:47:06 +00:00
|
|
|
const instance = getCurrentInstance()!
|
2023-01-20 12:10:58 +00:00
|
|
|
const event = useRequestEvent()
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2024-10-11 10:31:46 +00:00
|
|
|
let activeHead: ActiveHeadEntry<Head>
|
|
|
|
|
2023-07-05 09:48:01 +00:00
|
|
|
// TODO: remove use of `$fetch.raw` when nitro 503 issues on windows dev server are resolved
|
2024-01-29 11:48:35 +00:00
|
|
|
const eventFetch = import.meta.server ? event!.fetch : import.meta.dev ? $fetch.raw : globalThis.fetch
|
2023-05-15 22:43:53 +00:00
|
|
|
const mounted = ref(false)
|
2024-01-16 13:22:50 +00:00
|
|
|
onMounted(() => { mounted.value = true; teleportKey.value++ })
|
2024-10-11 10:31:46 +00:00
|
|
|
onBeforeUnmount(() => { if (activeHead) { activeHead.dispose() } })
|
2023-07-12 06:28:22 +00:00
|
|
|
function setPayload (key: string, result: NuxtIslandResponse) {
|
2024-03-30 07:41:46 +00:00
|
|
|
const toRevive: Partial<NuxtIslandResponse> = {}
|
|
|
|
if (result.props) { toRevive.props = result.props }
|
|
|
|
if (result.slots) { toRevive.slots = result.slots }
|
|
|
|
if (result.components) { toRevive.components = result.components }
|
2024-08-22 12:05:39 +00:00
|
|
|
if (result.head) { toRevive.head = result.head }
|
2023-07-12 06:28:22 +00:00
|
|
|
nuxtApp.payload.data[key] = {
|
|
|
|
__nuxt_island: {
|
|
|
|
key,
|
2023-08-07 22:03:40 +00:00
|
|
|
...(import.meta.server && import.meta.prerender)
|
2023-07-12 06:28:22 +00:00
|
|
|
? {}
|
2023-12-19 12:21:29 +00:00
|
|
|
: { params: { ...props.context, props: props.props ? JSON.stringify(props.props) : undefined } },
|
2024-04-05 18:08:32 +00:00
|
|
|
result: toRevive,
|
2023-07-12 06:28:22 +00:00
|
|
|
},
|
2024-04-05 18:08:32 +00:00
|
|
|
...result,
|
2023-07-12 06:28:22 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-16 13:22:50 +00:00
|
|
|
|
2024-03-30 07:41:46 +00:00
|
|
|
const payloads: Partial<Pick<NuxtIslandResponse, 'slots' | 'components'>> = {}
|
2024-01-19 12:21:42 +00:00
|
|
|
|
2024-03-26 14:03:00 +00:00
|
|
|
if (instance.vnode.el) {
|
2024-03-30 07:41:46 +00:00
|
|
|
const slots = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.slots
|
|
|
|
if (slots) { payloads.slots = slots }
|
|
|
|
if (selectiveClient) {
|
|
|
|
const components = toRaw(nuxtApp.payload.data[`${props.name}_${hashId.value}`])?.components
|
|
|
|
if (components) { payloads.components = components }
|
|
|
|
}
|
2023-12-19 12:21:29 +00:00
|
|
|
}
|
2023-07-12 06:28:22 +00:00
|
|
|
|
2023-07-31 08:51:09 +00:00
|
|
|
const ssrHTML = ref<string>('')
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2024-03-26 14:03:00 +00:00
|
|
|
if (import.meta.client && instance.vnode?.el) {
|
|
|
|
ssrHTML.value = getFragmentHTML(instance.vnode.el, true)?.join('') || ''
|
2024-03-06 11:55:06 +00:00
|
|
|
const key = `${props.name}_${hashId.value}`
|
2024-03-13 00:06:46 +00:00
|
|
|
nuxtApp.payload.data[key] ||= {}
|
2024-03-06 11:55:06 +00:00
|
|
|
nuxtApp.payload.data[key].html = ssrHTML.value
|
2023-07-12 06:28:22 +00:00
|
|
|
}
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2023-09-05 10:27:00 +00:00
|
|
|
const uid = ref<string>(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? getId())
|
2023-06-27 09:38:40 +00:00
|
|
|
const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1]))
|
2023-05-15 22:43:53 +00:00
|
|
|
const html = computed(() => {
|
|
|
|
const currentSlots = Object.keys(slots)
|
2023-12-19 12:21:29 +00:00
|
|
|
let html = ssrHTML.value
|
|
|
|
|
2024-06-10 22:20:27 +00:00
|
|
|
if (props.scopeId) {
|
2024-10-22 13:39:50 +00:00
|
|
|
html = html.replace(ISLAND_SCOPE_ID_RE, full => full + ' ' + props.scopeId)
|
2024-06-10 22:20:27 +00:00
|
|
|
}
|
|
|
|
|
2023-12-19 12:21:29 +00:00
|
|
|
if (import.meta.client && !canLoadClientComponent.value) {
|
2024-01-19 12:21:42 +00:00
|
|
|
for (const [key, value] of Object.entries(payloads.components || {})) {
|
2024-01-16 16:33:45 +00:00
|
|
|
html = html.replace(new RegExp(` data-island-uid="${uid.value}" data-island-component="${key}"[^>]*>`), (full) => {
|
2024-01-16 13:22:50 +00:00
|
|
|
return full + value.html
|
2023-12-19 12:21:29 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-30 07:41:46 +00:00
|
|
|
if (payloads.slots) {
|
|
|
|
return html.replaceAll(SLOT_FALLBACK_RE, (full, slotName) => {
|
|
|
|
if (!currentSlots.includes(slotName)) {
|
|
|
|
return full + (payloads.slots?.[slotName]?.fallback || '')
|
|
|
|
}
|
|
|
|
return full
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return html
|
2023-05-15 22:43:53 +00:00
|
|
|
})
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2024-08-22 12:05:39 +00:00
|
|
|
const head = injectHead()
|
2022-11-24 12:24:14 +00:00
|
|
|
|
2023-07-04 04:21:27 +00:00
|
|
|
async function _fetchComponent (force = false) {
|
2023-06-19 22:06:46 +00:00
|
|
|
const key = `${props.name}_${hashId.value}`
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2024-03-06 11:55:06 +00:00
|
|
|
if (!force && nuxtApp.payload.data[key]?.html) { return nuxtApp.payload.data[key] }
|
2023-06-14 09:09:27 +00:00
|
|
|
|
2023-10-20 15:58:02 +00:00
|
|
|
const url = remoteComponentIslands && props.source ? new URL(`/__nuxt_island/${key}.json`, props.source).href : `/__nuxt_island/${key}.json`
|
2023-07-30 21:00:41 +00:00
|
|
|
|
2023-08-07 22:03:40 +00:00
|
|
|
if (import.meta.server && import.meta.prerender) {
|
2023-01-20 12:10:58 +00:00
|
|
|
// Hint to Nitro to prerender the island component
|
2023-10-19 22:44:45 +00:00
|
|
|
nuxtApp.runWithContext(() => prerenderRoutes(url))
|
2023-01-20 12:10:58 +00:00
|
|
|
}
|
2022-11-24 12:24:14 +00:00
|
|
|
// TODO: Validate response
|
2023-07-18 15:07:35 +00:00
|
|
|
// $fetch handles the app.baseURL in dev
|
2023-09-08 19:48:42 +00:00
|
|
|
const r = await eventFetch(withQuery(((import.meta.dev && import.meta.client) || props.source) ? url : joinURL(config.app.baseURL ?? '', url), {
|
2023-06-25 16:38:15 +00:00
|
|
|
...props.context,
|
2024-04-05 18:08:32 +00:00
|
|
|
props: props.props ? JSON.stringify(props.props) : undefined,
|
2023-06-25 16:38:15 +00:00
|
|
|
}))
|
2023-08-07 22:03:40 +00:00
|
|
|
const result = import.meta.server || !import.meta.dev ? await r.json() : (r as FetchResponse<NuxtIslandResponse>)._data
|
2023-06-25 16:38:15 +00:00
|
|
|
// TODO: support passing on more headers
|
2023-08-07 22:03:40 +00:00
|
|
|
if (import.meta.server && import.meta.prerender) {
|
2023-06-25 16:38:15 +00:00
|
|
|
const hints = r.headers.get('x-nitro-prerender')
|
|
|
|
if (hints) {
|
2024-01-29 11:48:35 +00:00
|
|
|
appendResponseHeader(event!, 'x-nitro-prerender', hints)
|
2022-11-24 12:24:14 +00:00
|
|
|
}
|
2023-06-25 16:38:15 +00:00
|
|
|
}
|
2023-07-12 06:28:22 +00:00
|
|
|
setPayload(key, result)
|
2023-06-14 09:09:27 +00:00
|
|
|
return result
|
2022-11-24 12:24:14 +00:00
|
|
|
}
|
2023-12-19 12:21:29 +00:00
|
|
|
|
2023-07-04 04:21:27 +00:00
|
|
|
async function fetchComponent (force = false) {
|
2022-11-24 12:24:14 +00:00
|
|
|
nuxtApp[pKey] = nuxtApp[pKey] || {}
|
2023-05-15 22:43:53 +00:00
|
|
|
if (!nuxtApp[pKey][uid.value]) {
|
2023-07-04 04:21:27 +00:00
|
|
|
nuxtApp[pKey][uid.value] = _fetchComponent(force).finally(() => {
|
2023-05-15 22:43:53 +00:00
|
|
|
delete nuxtApp[pKey]![uid.value]
|
2022-11-24 12:24:14 +00:00
|
|
|
})
|
|
|
|
}
|
2023-07-30 21:00:41 +00:00
|
|
|
try {
|
|
|
|
const res: NuxtIslandResponse = await nuxtApp[pKey][uid.value]
|
2024-08-22 12:05:39 +00:00
|
|
|
|
2024-01-16 13:22:50 +00:00
|
|
|
ssrHTML.value = res.html.replaceAll(DATA_ISLAND_UID_RE, `data-island-uid="${uid.value}"`)
|
2023-07-30 21:00:41 +00:00
|
|
|
key.value++
|
|
|
|
error.value = null
|
2024-01-19 12:21:42 +00:00
|
|
|
payloads.slots = res.slots || {}
|
|
|
|
payloads.components = res.components || {}
|
2023-12-19 12:21:29 +00:00
|
|
|
|
|
|
|
if (selectiveClient && import.meta.client) {
|
2024-01-16 13:22:50 +00:00
|
|
|
if (canLoadClientComponent.value && res.components) {
|
|
|
|
await loadComponents(props.source, res.components)
|
2023-12-19 12:21:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-11 10:31:46 +00:00
|
|
|
if (res?.head) {
|
|
|
|
if (activeHead) {
|
|
|
|
activeHead.patch(res.head)
|
|
|
|
} else {
|
|
|
|
activeHead = head.push(res.head)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-07 22:03:40 +00:00
|
|
|
if (import.meta.client) {
|
2023-07-30 21:00:41 +00:00
|
|
|
// must await next tick for Teleport to work correctly with static node re-rendering
|
2024-01-16 13:22:50 +00:00
|
|
|
nextTick(() => {
|
|
|
|
canTeleport = true
|
|
|
|
teleportKey.value++
|
|
|
|
})
|
2023-07-30 21:00:41 +00:00
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
error.value = e
|
2024-03-06 16:45:43 +00:00
|
|
|
emit('error', e)
|
2023-05-15 22:43:53 +00:00
|
|
|
}
|
2022-11-24 12:24:14 +00:00
|
|
|
}
|
|
|
|
|
2023-12-14 11:07:54 +00:00
|
|
|
expose({
|
2024-04-05 18:08:32 +00:00
|
|
|
refresh: () => fetchComponent(true),
|
2023-12-14 11:07:54 +00:00
|
|
|
})
|
|
|
|
|
2023-07-04 04:21:27 +00:00
|
|
|
if (import.meta.hot) {
|
|
|
|
import.meta.hot.on(`nuxt-server-component:${props.name}`, () => {
|
|
|
|
fetchComponent(true)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-07 22:03:40 +00:00
|
|
|
if (import.meta.client) {
|
2024-01-01 16:20:10 +00:00
|
|
|
watch(props, debounce(() => fetchComponent(), 100), { deep: true })
|
2022-11-24 12:24:14 +00:00
|
|
|
}
|
|
|
|
|
2024-02-05 10:36:20 +00:00
|
|
|
if (import.meta.client && !instance.vnode.el && props.lazy) {
|
2023-07-31 08:51:09 +00:00
|
|
|
fetchComponent()
|
2024-02-05 10:36:20 +00:00
|
|
|
} else if (import.meta.server || !instance.vnode.el || !nuxtApp.payload.serverRendered) {
|
2023-01-14 01:13:48 +00:00
|
|
|
await fetchComponent()
|
2024-01-16 13:22:50 +00:00
|
|
|
} else if (selectiveClient && canLoadClientComponent.value) {
|
2024-01-19 12:21:42 +00:00
|
|
|
await loadComponents(props.source, payloads.components)
|
2023-01-14 01:13:48 +00:00
|
|
|
}
|
2023-04-20 21:41:20 +00:00
|
|
|
|
2024-01-16 13:22:50 +00:00
|
|
|
return (_ctx: any, _cache: any) => {
|
2023-12-19 12:21:29 +00:00
|
|
|
if (!html.value || error.value) {
|
|
|
|
return [slots.fallback?.({ error: error.value }) ?? createVNode('div')]
|
2023-07-30 21:00:41 +00:00
|
|
|
}
|
2024-01-16 13:22:50 +00:00
|
|
|
return [
|
|
|
|
withMemo([key.value], () => {
|
|
|
|
return createVNode(Fragment, { key: key.value }, [h(createStaticVNode(html.value || '<div></div>', 1))])
|
|
|
|
}, _cache, 0),
|
|
|
|
|
|
|
|
// should away be triggered ONE tick after re-rendering the static node
|
|
|
|
withMemo([teleportKey.value], () => {
|
2024-06-13 21:47:24 +00:00
|
|
|
const teleports: Array<VNode> = []
|
2024-01-16 13:22:50 +00:00
|
|
|
// this is used to force trigger Teleport when vue makes the diff between old and new node
|
|
|
|
const isKeyOdd = teleportKey.value === 0 || !!(teleportKey.value && !(teleportKey.value % 2))
|
|
|
|
|
2024-03-26 14:03:00 +00:00
|
|
|
if (uid.value && html.value && (import.meta.server || props.lazy ? canTeleport : (mounted.value || instance.vnode?.el))) {
|
2024-01-16 13:22:50 +00:00
|
|
|
for (const slot in slots) {
|
|
|
|
if (availableSlots.value.includes(slot)) {
|
|
|
|
teleports.push(createVNode(Teleport,
|
|
|
|
// use different selectors for even and odd teleportKey to force trigger the teleport
|
|
|
|
{ to: import.meta.client ? `${isKeyOdd ? 'div' : ''}[data-island-uid="${uid.value}"][data-island-slot="${slot}"]` : `uid=${uid.value};slot=${slot}` },
|
2024-09-18 19:41:53 +00:00
|
|
|
{ default: () => (payloads.slots?.[slot]?.props?.length ? payloads.slots[slot].props : [{}]).map((data: any) => slots[slot]?.(data)) }),
|
2024-01-16 13:22:50 +00:00
|
|
|
)
|
2023-12-19 12:21:29 +00:00
|
|
|
}
|
2024-01-16 13:22:50 +00:00
|
|
|
}
|
2024-03-10 17:28:14 +00:00
|
|
|
if (selectiveClient) {
|
|
|
|
if (import.meta.server) {
|
2024-03-30 07:41:46 +00:00
|
|
|
if (payloads.components) {
|
|
|
|
for (const [id, info] of Object.entries(payloads.components)) {
|
|
|
|
const { html, slots } = info
|
|
|
|
let replaced = html.replaceAll('data-island-uid', `data-island-uid="${uid.value}"`)
|
|
|
|
for (const slot in slots) {
|
|
|
|
replaced = replaced.replaceAll(`data-island-slot="${slot}">`, full => full + slots[slot])
|
|
|
|
}
|
|
|
|
teleports.push(createVNode(Teleport, { to: `uid=${uid.value};client=${id}` }, {
|
2024-04-05 18:08:32 +00:00
|
|
|
default: () => [createStaticVNode(replaced, 1)],
|
2024-03-30 07:41:46 +00:00
|
|
|
}))
|
2024-01-16 13:22:50 +00:00
|
|
|
}
|
2024-03-10 17:28:14 +00:00
|
|
|
}
|
2024-03-30 07:41:46 +00:00
|
|
|
} else if (canLoadClientComponent.value && payloads.components) {
|
|
|
|
for (const [id, info] of Object.entries(payloads.components)) {
|
2024-03-10 17:28:14 +00:00
|
|
|
const { props, slots } = info
|
|
|
|
const component = components!.get(id)!
|
|
|
|
// use different selectors for even and odd teleportKey to force trigger the teleport
|
|
|
|
const vnode = createVNode(Teleport, { to: `${isKeyOdd ? 'div' : ''}[data-island-uid='${uid.value}'][data-island-component="${id}"]` }, {
|
|
|
|
default: () => {
|
2024-04-05 18:08:32 +00:00
|
|
|
return [h(component, props, Object.fromEntries(Object.entries(slots || {}).map(([k, v]) => ([k, () => createStaticVNode(`<div style="display: contents" data-island-uid data-island-slot="${k}">${v}</div>`, 1),
|
2024-03-10 17:28:14 +00:00
|
|
|
]))))]
|
2024-04-05 18:08:32 +00:00
|
|
|
},
|
2024-03-10 17:28:14 +00:00
|
|
|
})
|
|
|
|
teleports.push(vnode)
|
|
|
|
}
|
2024-01-16 13:22:50 +00:00
|
|
|
}
|
|
|
|
}
|
2023-12-19 12:21:29 +00:00
|
|
|
}
|
2024-01-16 13:22:50 +00:00
|
|
|
|
|
|
|
return h(Fragment, teleports)
|
2024-04-05 18:08:32 +00:00
|
|
|
}, _cache, 1),
|
2024-01-16 13:22:50 +00:00
|
|
|
]
|
2023-04-20 21:41:20 +00:00
|
|
|
}
|
2024-04-05 18:08:32 +00:00
|
|
|
},
|
2023-05-15 22:43:53 +00:00
|
|
|
})
|