51014e6416686cbf764d91bbc3a08b1b153fd550055e0bc3d548cbc405e164740171b3208dfb3606cb6e21d0100b7330b29677ed97a3200851605842f9c550 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /* @flow */
  2. /**
  3. * Original RenderStream implementation by Sasha Aickin (@aickin)
  4. * Licensed under the Apache License, Version 2.0
  5. * http://www.apache.org/licenses/LICENSE-2.0
  6. *
  7. * Modified by Evan You (@yyx990803)
  8. */
  9. const stream = require('stream')
  10. import { isTrue, isUndef } from 'shared/util'
  11. import { createWriteFunction } from './write'
  12. export default class RenderStream extends stream.Readable {
  13. buffer: string;
  14. render: (write: Function, done: Function) => void;
  15. expectedSize: number;
  16. write: Function;
  17. next: Function;
  18. end: Function;
  19. done: boolean;
  20. constructor (render: Function) {
  21. super()
  22. this.buffer = ''
  23. this.render = render
  24. this.expectedSize = 0
  25. this.write = createWriteFunction((text, next) => {
  26. const n = this.expectedSize
  27. this.buffer += text
  28. if (this.buffer.length >= n) {
  29. this.next = next
  30. this.pushBySize(n)
  31. return true // we will decide when to call next
  32. }
  33. return false
  34. }, err => {
  35. this.emit('error', err)
  36. })
  37. this.end = () => {
  38. this.emit('beforeEnd')
  39. // the rendering is finished; we should push out the last of the buffer.
  40. this.done = true
  41. this.push(this.buffer)
  42. }
  43. }
  44. pushBySize (n: number) {
  45. const bufferToPush = this.buffer.substring(0, n)
  46. this.buffer = this.buffer.substring(n)
  47. this.push(bufferToPush)
  48. }
  49. tryRender () {
  50. try {
  51. this.render(this.write, this.end)
  52. } catch (e) {
  53. this.emit('error', e)
  54. }
  55. }
  56. tryNext () {
  57. try {
  58. this.next()
  59. } catch (e) {
  60. this.emit('error', e)
  61. }
  62. }
  63. _read (n: number) {
  64. this.expectedSize = n
  65. // it's possible that the last chunk added bumped the buffer up to > 2 * n,
  66. // which means we will need to go through multiple read calls to drain it
  67. // down to < n.
  68. if (isTrue(this.done)) {
  69. this.push(null)
  70. return
  71. }
  72. if (this.buffer.length >= n) {
  73. this.pushBySize(n)
  74. return
  75. }
  76. if (isUndef(this.next)) {
  77. // start the rendering chain.
  78. this.tryRender()
  79. } else {
  80. // continue with the rendering.
  81. this.tryNext()
  82. }
  83. }
  84. }