unhead
unhead copied to clipboard
`headTags` are empty when using `pipeToWebWritable`
import { createHead, useHead } from '@unhead/vue'
import { createSSRApp, ref } from 'vue'
import { pipeToWebWritable } from '@vue/server-renderer'
import { renderSSRHead } from '@unhead/ssr'
import { describe, it, expect } from 'vitest'
const template = String.raw`<!doctype html><html lang="en"><!--head--><div id="app"><!--app--></div></html>`
describe('vue ssr', () => {
it('pipeToWebWritable', async () => {
const head = createHead()
const app = createSSRApp({
async setup() {
const title = ref('initial title')
useHead({
title,
})
await new Promise(resolve => setTimeout(resolve, 200))
title.value = 'new title'
return () => '<div>hi</div>'
},
})
app.use(head)
const [prepend, append] = template.split('<!--app-->')
const { headTags } = await renderSSRHead(head)
const encoder = new TextEncoder()
const { writable } = new TransformStream({
start(controller) {
// prepend.replace('<!--head-->', headTags)
controller.enqueue(encoder.encode(prepend))
},
flush(controller) {
controller.enqueue(encoder.encode(append))
}
})
pipeToWebWritable(app, {}, writable)
expect(headTags).eq('<title>new title</title>')
})
})
❯ test/stream.test.js:29:22
27| await pipeToWebWritable(app, {}, writable)
28|
29| expect(headTags).eq('<title>new title</title>')
| ^
30| })
31| })
- Expected "<title>new title</title>"
+ Received ""
Hey @daliborgogic
Thanks for the issue.
I'm not entirely sure how to solve this, as head tags are written as the Vue app is rendered. Do you have any ideas?
@harlan-zw For now no idea. Will check what we can and what we can with hooks. You can close this issue. If we found a solution we will make a PR.
have you tried this code @daliborgogic @harlan-zw ?
const appHtml = await renderToString(app);
const { headTags } = await renderSSRHead(head);
I had the same case, then I was able to solve it by changing the order, first renderToString then renderSSRRHead
Isn't that just regular SSR? My understanding of the Vue rendering stream is that it's sending chunks as they are rendered so the head data would need to be appended as it's rendered, which it isn't for performance reasons.
@harlan-zw yes, it's regular SSR. You are right. Workaround until there is a better way
- const { headTags } = await renderSSRHead(head)
+ const decoder = new TextDecoder()
+ let c
+ let once = false
const { readable, writable } = new TransformStream({
- start() {/**/},
+ async transform(chunk, controller) {
+ if (!once) {
+ const decodedChunk = decoder.decode(chunk)
+ const { headTags } = await renderSSRHead(head)
+ const replacedHead = prepend.replace(`<!--head-->`, headTags)
+ const encodedChunk = encoder.encode(replacedHead + decodedChunk)
+ c = encodedChunk
+ once = true
+ } else {
+ c = chunk
+ }
+ controller.enqueue(c)
+ },
flush(controller) {
controller.enqueue(encoder.encode(append))
}
})
Going to track here https://github.com/unjs/unhead/issues/396 which is part of the v2 roadmap.