JuanCaicedo
JuanCaicedo

Reputation: 3428

Transform stream stops processing early

I'm trying to extend the node.js Transform stream twice, once to split stdin into new lines, and another to alternate lowercase and uppercase.

SplitLines is working as intended, but AlternateUppercase is not.

index.js

const { Transform } = require('stream')

class SplitLines extends Transform {
  _transform(chunk, encoding, callback) {
    const parsed = chunk.toString().trim()
    const results = parsed.split('\n')
    results.forEach((line) => {
      this.push(`${line}\n`)
    })
  }
}

class AlternateUppercase extends Transform {
  constructor(options) {
    super(options)
    this.isEven = false
  }

  _transform(chunk, encoding, callback) {
    const line = chunk.toString()
    const altered = this.isEven ? line.toUpperCase() : line
    this.push(`${altered}\n`)
    this.isEven = !this.isEven
  }
}

process.stdin
  .pipe(new SplitLines())
  .pipe(new AlternateUppercase())
  .pipe(process.stdout)

How I'm testing

echo -e 'one\ntwo\nthree' | node index.js

What I'm seeing in the terminal

one

What I would expect to see

one
TWO
three

Am I doing something wrong which is causing

Upvotes: 0

Views: 752

Answers (1)

Helix
Helix

Reputation: 197

In the node documentation, you are required to call the callback function in order to receive the next chunk. This is not so apparent in the SplitLines function because you passed the whole string as a single chunk. However, when you do the push operation repeatedly SplitLines, you are sending multiple chunks, so you need to call the callback function.

const { Transform } = require('stream')

class SplitLines extends Transform {
  _transform(chunk, encoding, callback) {
    const parsed = chunk.toString().trim()
    const results = parsed.split('\n')
    results.forEach((line) => {
      this.push(`${line}\n`)
    })
  }
}

class AlternateUppercase extends Transform {
  constructor(options) {
    super(options)
    this.isEven = false
  }

  _transform(chunk, encoding, callback) {
    const line = chunk.toString()
    const altered = this.isEven ? line.toUpperCase() : line
    this.push(`${altered}\n`)
    callback() //must call callback to receive next chunk
    this.isEven = !this.isEven
  }
}

process.stdin
  .pipe(new SplitLines())
  .pipe(new AlternateUppercase())
  .pipe(process.stdout)

The output would be:

one

TWO

three


The multiple new lines appeared because you \n at both of the transformer. To fix this, you need to add \n only once, ie at SplitLines or AlternateUppercase only.

Upvotes: 1

Related Questions