renderToStream.ts 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import {
  2. App,
  3. VNode,
  4. createVNode,
  5. ssrUtils,
  6. createApp,
  7. ssrContextKey
  8. } from 'vue'
  9. import { isString, isPromise } from '@vue/shared'
  10. import { renderComponentVNode, SSRBuffer, SSRContext } from './render'
  11. import { Readable } from 'stream'
  12. const { isVNode } = ssrUtils
  13. async function unrollBuffer(
  14. buffer: SSRBuffer,
  15. stream: Readable
  16. ): Promise<void> {
  17. if (buffer.hasAsync) {
  18. for (let i = 0; i < buffer.length; i++) {
  19. let item = buffer[i]
  20. if (isPromise(item)) {
  21. item = await item
  22. }
  23. if (isString(item)) {
  24. stream.push(item)
  25. } else {
  26. await unrollBuffer(item, stream)
  27. }
  28. }
  29. } else {
  30. // sync buffer can be more efficiently unrolled without unnecessary await
  31. // ticks
  32. unrollBufferSync(buffer, stream)
  33. }
  34. }
  35. function unrollBufferSync(buffer: SSRBuffer, stream: Readable) {
  36. for (let i = 0; i < buffer.length; i++) {
  37. let item = buffer[i]
  38. if (isString(item)) {
  39. stream.push(item)
  40. } else {
  41. // since this is a sync buffer, child buffers are never promises
  42. unrollBufferSync(item as SSRBuffer, stream)
  43. }
  44. }
  45. }
  46. export function renderToStream(
  47. input: App | VNode,
  48. context: SSRContext = {}
  49. ): Readable {
  50. if (isVNode(input)) {
  51. // raw vnode, wrap with app (for context)
  52. return renderToStream(createApp({ render: () => input }), context)
  53. }
  54. // rendering an app
  55. const vnode = createVNode(input._component, input._props)
  56. vnode.appContext = input._context
  57. // provide the ssr context to the tree
  58. input.provide(ssrContextKey, context)
  59. const stream = new Readable()
  60. Promise.resolve(renderComponentVNode(vnode))
  61. .then(buffer => unrollBuffer(buffer, stream))
  62. .then(() => {
  63. stream.push(null)
  64. })
  65. .catch(error => {
  66. stream.destroy(error)
  67. })
  68. return stream
  69. }