| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- import {
- type App,
- type VNode,
- createApp,
- createVNode,
- ssrContextKey,
- ssrUtils,
- } from 'vue'
- import { isPromise, isString } from '@vue/shared'
- import {
- type SSRBuffer,
- type SSRContext,
- cleanupContext,
- renderComponentVNode,
- } from './render'
- const { isVNode } = ssrUtils
- function nestedUnrollBuffer(
- buffer: SSRBuffer,
- parentRet: string,
- startIndex: number,
- ): Promise<string> | string {
- if (!buffer.hasAsync) {
- return parentRet + unrollBufferSync(buffer)
- }
- let ret = parentRet
- for (let i = startIndex; i < buffer.length; i += 1) {
- const item = buffer[i]
- if (isString(item)) {
- ret += item
- continue
- }
- if (isPromise(item)) {
- return item.then(nestedItem => {
- buffer[i] = nestedItem
- return nestedUnrollBuffer(buffer, ret, i)
- })
- }
- const result = nestedUnrollBuffer(item, ret, 0)
- if (isPromise(result)) {
- return result.then(nestedItem => {
- buffer[i] = nestedItem
- return nestedUnrollBuffer(buffer, '', i)
- })
- }
- ret = result
- }
- return ret
- }
- export function unrollBuffer(buffer: SSRBuffer): Promise<string> | string {
- return nestedUnrollBuffer(buffer, '', 0)
- }
- function unrollBufferSync(buffer: SSRBuffer): string {
- let ret = ''
- for (let i = 0; i < buffer.length; i++) {
- let item = buffer[i]
- if (isString(item)) {
- ret += item
- } else {
- // since this is a sync buffer, child buffers are never promises
- ret += unrollBufferSync(item as SSRBuffer)
- }
- }
- return ret
- }
- export async function renderToString(
- input: App | VNode,
- context: SSRContext = {},
- ): Promise<string> {
- if (isVNode(input)) {
- // raw vnode, wrap with app (for context)
- return renderToString(createApp({ render: () => input }), context)
- }
- // rendering an app
- const vnode = createVNode(input._component, input._props)
- vnode.appContext = input._context
- // provide the ssr context to the tree
- input.provide(ssrContextKey, context)
- try {
- const buffer = await renderComponentVNode(vnode)
- const result = await unrollBuffer(buffer as SSRBuffer)
- await resolveTeleports(context)
- return result
- } finally {
- cleanupContext(context)
- }
- }
- export async function resolveTeleports(context: SSRContext): Promise<void> {
- if (context.__teleportBuffers) {
- context.teleports = context.teleports || {}
- for (const key in context.__teleportBuffers) {
- // note: it's OK to await sequentially here because the Promises were
- // created eagerly in parallel.
- context.teleports[key] = await unrollBuffer(
- await Promise.all([context.__teleportBuffers[key]]),
- )
- }
- }
- }
|