Way 1:
const clearLastLine = () => {
process.stdout.moveCursor(0, -1) // up one line
process.stdout.clearLine(1) // from cursor to end
}
Way 2:
const readline = require('readline')
const clearLastLine = () => {
readline.moveCursor(process.stdout, 0, -1) // up one line
readline.clearLine(process.stdout, 1) // from cursor to end
}
Way 3:
const ESC = '\x1b' // ASCII escape character
const CSI = ESC + '[' // control sequence introducer
const clearLastLine = () => {
process.stdout.write(CSI + 'A') // moves cursor up one line
process.stdout.write(CSI + 'K') // clears from cursor to line end
}
Way 1 uses the methods provided by the built-in tty module.
Way 2 uses the built-in readline module.
Way 3 uses ANSI escape sequences.
I learned about ANSI escape sequences by viewing the source code of log-update, which I found out about from dthree's answer. After doing some more research, I learned about the built-in readline and tty modules.
By reading the source code of Node.js, I learned that the methods used in way 1 are essentially wrappers around the methods used in way 2 and that the methods used in way 2 are essentially wrappers around the methods used in way 3. So every way ends up doing basically the same thing under the hood. I've ordered the ways from higher-level to lower-level.
I suggest using way 1 over way 2 and way 2 over way 3 mainly because I think that way 1 is less code and more clear (and therefore more readable) than way 2 and that way 2 is less code and more clear (and therefore more readable) than way 3 and that performance improvements (if any) achieved by using a lower-level way are likely negligible. However, I think way 3 avoids loading the built-in readline module.
This solution (every way) assumes your cursor is at the start of an empty line right before your program calls clearLastLine(). As Gajus states in his answer, the solution is simpler if instead your cursor is at the end of the last line. So could you use process.stdout.write() (instead of console.log()) to print the last line without a trailing newline?
Or the solution is even simpler if you don't need to print the last line in the first place. Is all this tinkering with stdout a code smell? Could you rearchitect your code so that the last line doesn't get printed at all?
In my case I'm testing a helper function I wrote that calls console.error(), and I don't want the logged error message to show up in my test output. So instead of having my helper function call console.error(), I could make it throw an error (or just return an error code and/or message or a boolean). And then my main program that calls my helper function could call console.error() if my helper function throws an error (or returns an an error code and/or message or false). Or I could pass a boolean flag argument to my helper function that tells it whether or not to log an error. Or I could redirect stderr, or mock the global console or stub console.error(), etc.
If you wanted to clear the last three lines written to system.stdout, you could do:
const clearLastLines = (count) => {
process.stdout.moveCursor(0, -count)
process.stdout.clearScreenDown()
}
clearLastLines(3)