Imagine building a progress indicator in Node. We could start with low level "print" calls
1 | process.stdout.write('long message first') |
This prints messages one after another on the same line
1 | long message firstshort message |
Hmm, not the best output, the words are printed on the same line. If we want to print the second message on the second line, we have to print "new line" character \n
1 | process.stdout.write('long message first\n') |
which outputs
1 | long message first |
Note, this is what console.log
does by default - it adds \n
after each printed line.
But what if we send carriage return character \r
after the first message?
1 | process.stdout.write('long message first\r') |
Our output is garbled!
1 | short messagefirst |
The second message is shorter and thus it overwrites only a part of the first line of text. We cannot have this; we need to clear the rest of the line every time we print it. We can print a string of spaces to clear the line - but we need to know how many spaces to print. Luckily there is a property process.stdout.columns
that tells us exactly how many characters are in the terminal. We can clear the current line by print an empty line + \r
before we print new text.
1 | console.log('stdout width', process.stdout.columns) |
1 | stdout width 99 |
Then a second later the output becomes
1 | stdout width 99 |
Beautiful!
Docker
But what happens if our code runs in a terminal that is really feature-limited, like the output piped from the Docker build command? Here is a Docker file I will use
1 | FROM node:8 |
1 | Status: Downloaded newer image for node:8 |
There is no process.stdout.columns
number, even if the carriage return works! So how do we show the second line? Well, we can take a shortcut and just do the "newline" instead!
1 | const emptyLine = process.stdout.columns ? ''.padEnd(process.stdout.columns, ' ') : '\n' |
and it works nicely in the terminal and in Docker
1 | ---> Running in 8d63833b96ac |
Nice, except when you have progress bars ... which output thousands of messages when showing percentage increments for example. We have this problem when showing Cypress installation (see issue #1243) progress. The output log would just flood with thousands of identical lines like these
In this case we should treat using progress bars as an enhancement. By default the program should show only the text messages at the start and end of the action.
1 | Installing Cypress (version: 1.4.2) |
Only if we find that the terminal has process.stdout.columns
set, then we can use a more advanced printing and you can show progress indicators.
Note: the code is in repo bahmutov/test-line-return