--- /dev/null
+import { bench, describe } from 'vitest'
+
+import { type SSRBuffer, createBuffer } from '../src/render'
+import { unrollBuffer } from '../src/renderToString'
+
+function createSyncBuffer(levels: number, itemsPerLevel: number): SSRBuffer {
+ const buffer = createBuffer()
+
+ function addItems(buf: ReturnType<typeof createBuffer>, level: number) {
+ for (let i = 1; i <= levels * itemsPerLevel; i++) {
+ buf.push(`sync${level}.${i}`)
+ }
+ if (level < levels) {
+ const subBuffer = createBuffer()
+ addItems(subBuffer, level + 1)
+ buf.push(subBuffer.getBuffer())
+ }
+ }
+
+ addItems(buffer, 1)
+ return buffer.getBuffer()
+}
+
+function createMixedBuffer(levels: number, itemsPerLevel: number): SSRBuffer {
+ const buffer = createBuffer()
+
+ function addItems(buf: ReturnType<typeof createBuffer>, level: number) {
+ for (let i = 1; i <= levels * itemsPerLevel; i++) {
+ if (i % 3 === 0) {
+ // @ts-expect-error testing...
+ buf.push(Promise.resolve(`async${level}.${i}`))
+ } else {
+ buf.push(`sync${level}.${i}`)
+ }
+ }
+ if (level < levels) {
+ const subBuffer = createBuffer()
+ addItems(subBuffer, level + 1)
+ buf.push(subBuffer.getBuffer())
+ }
+ }
+
+ addItems(buffer, 1)
+ return buffer.getBuffer()
+}
+
+describe('unrollBuffer', () => {
+ let syncBuffer = createBuffer().getBuffer()
+ let mixedBuffer = createBuffer().getBuffer()
+
+ bench(
+ 'sync',
+ () => {
+ return unrollBuffer(syncBuffer) as any
+ },
+ {
+ setup() {
+ syncBuffer = createSyncBuffer(5, 3)
+ },
+ },
+ )
+
+ bench(
+ 'mixed',
+ () => {
+ return unrollBuffer(mixedBuffer) as any
+ },
+ {
+ setup() {
+ mixedBuffer = createMixedBuffer(5, 3)
+ },
+ },
+ )
+})
const { isVNode } = ssrUtils
-async function unrollBuffer(buffer: SSRBuffer): Promise<string> {
- if (buffer.hasAsync) {
- let ret = ''
- for (let i = 0; i < buffer.length; i++) {
- let item = buffer[i]
- if (isPromise(item)) {
- item = await item
- }
- if (isString(item)) {
- ret += item
- } else {
- ret += await unrollBuffer(item)
- }
+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
}
- return ret
- } else {
- // sync buffer can be more efficiently unrolled without unnecessary await
- // ticks
- return unrollBufferSync(buffer)
+
+ 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 {