/* @flow */ /** * Original RenderStream implementation by Sasha Aickin (@aickin) * Licensed under the Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 * * Modified by Evan You (@yyx990803) */ const stream = require('stream') import { isTrue, isUndef } from 'shared/util' import { createWriteFunction } from './write' export default class RenderStream extends stream.Readable { buffer: string; render: (write: Function, done: Function) => void; expectedSize: number; write: Function; next: Function; end: Function; done: boolean; constructor (render: Function) { super() this.buffer = '' this.render = render this.expectedSize = 0 this.write = createWriteFunction((text, next) => { const n = this.expectedSize this.buffer += text if (this.buffer.length >= n) { this.next = next this.pushBySize(n) return true // we will decide when to call next } return false }, err => { this.emit('error', err) }) this.end = () => { this.emit('beforeEnd') // the rendering is finished; we should push out the last of the buffer. this.done = true this.push(this.buffer) } } pushBySize (n: number) { const bufferToPush = this.buffer.substring(0, n) this.buffer = this.buffer.substring(n) this.push(bufferToPush) } tryRender () { try { this.render(this.write, this.end) } catch (e) { this.emit('error', e) } } tryNext () { try { this.next() } catch (e) { this.emit('error', e) } } _read (n: number) { this.expectedSize = n // it's possible that the last chunk added bumped the buffer up to > 2 * n, // which means we will need to go through multiple read calls to drain it // down to < n. if (isTrue(this.done)) { this.push(null) return } if (this.buffer.length >= n) { this.pushBySize(n) return } if (isUndef(this.next)) { // start the rendering chain. this.tryRender() } else { // continue with the rendering. this.tryNext() } } }