|
- import { Transform, TransformCallback } from 'stream'
-
- // Thanks: https://stackoverflow.com/a/45126242
- class StreamReplacer extends Transform {
- private pendingChunk: Buffer
-
- constructor (private readonly replacer: (line: string) => string) {
- super()
- }
-
- _transform (chunk: Buffer, _encoding: BufferEncoding, done: TransformCallback) {
- try {
- this.pendingChunk = this.pendingChunk?.length
- ? Buffer.concat([ this.pendingChunk, chunk ])
- : chunk
-
- let index: number
-
- // As long as we keep finding newlines, keep making slices of the buffer and push them to the
- // readable side of the transform stream
- while ((index = this.pendingChunk.indexOf('\n')) !== -1) {
- // The `end` parameter is non-inclusive, so increase it to include the newline we found
- const line = this.pendingChunk.slice(0, ++index)
-
- // `start` is inclusive, but we are already one char ahead of the newline -> all good
- this.pendingChunk = this.pendingChunk.slice(index)
-
- // We have a single line here! Prepend the string we want
- this.push(this.doReplace(line))
- }
-
- return done()
- } catch (err) {
- return done(err)
- }
- }
-
- _flush (done: TransformCallback) {
- // If we have any remaining data in the cache, send it out
- if (!this.pendingChunk?.length) return done()
-
- try {
- return done(null, this.doReplace(this.pendingChunk))
- } catch (err) {
- return done(err)
- }
- }
-
- private doReplace (buffer: Buffer) {
- const line = this.replacer(buffer.toString('utf8'))
-
- return Buffer.from(line, 'utf8')
- }
- }
-
- export {
- StreamReplacer
- }
|