UNPKG

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