Skip to content

Package: output

Eugene Lazutkin edited this page Sep 26, 2026 · 14 revisions

The output package provides helpers for outputting text to the console.

output/show.js

output/show.js provides simple helpers for console output.

Function log()

log() is a wrapper around console.log().

log(text, options) outputs text to the console:

  • text — any value convertible to a Box.
  • options — an object with the following properties:
    • endOfLineCommand — a string, which is appended at the end of the line but before the new line. Defaults to \x1B[m (RESET_ALL).
    • colorDepth — the number of colors to use. Defaults to 24.

If colorDepth is less than 4 (16 colors), all CSI sequences will be removed from the output.

Function out()

out(text, options) is similar to log() but can write to any Writable stream via an additional option:

  • stream — the Writable stream to output to. Defaults to process.stdout.

Class Out

Out wraps out() into a reusable class bound to a stream.

Members:

Name Return type Description
constructor(stream) Out Creates a new Out object with the specified stream.
stream Writable The Writable stream to output to.
colorDepth number The number of colors to use.
out(text, options) this Outputs text to the stream.

For TTY streams colorDepth is initialized using stream.getColorDepth(). For other streams it is assumed to be 1.

out() works like the stand-alone function but uses this.stream and falls back to this.colorDepth.

Function debug()

debug(string) prints non-printable characters in hexadecimal format via console.log(). Useful for debugging escape sequences.

Exports

All functions and classes are exported by name. There is no default export.

output/writer.js

output/writer.js provides Writer — a full-featured class for writing text to any Writable stream, wrapping both TTY and plain streams.

For non-TTY streams (e.g., files), CSI escape sequences are removed automatically. Use forceColorDepth to preserve colors and styles.

Class Writer

Members:

Name Return type Description
constructor(stream = process.stdout, forceColorDepth) Writer Creates a new Writer object with the specified stream.
stream Writable The Writable stream to output to.
forceColorDepth number or undefined The number of colors to use.
isTTY boolean Whether the stream is a TTY stream.
rows number or undefined The number of rows in the terminal.
columns number or undefined The number of columns in the terminal.
size object The size of the terminal as {columns, rows}.
getColorDepth(...args) number The number of colors to use.
hasColors(count, ...args) boolean Whether the stream has a required number of colors.
async clearLine(dir) this Clears the current line according to dir.
async clearScreenDown() this Clears the screen down from the current cursor position.
async cursorTo(x, y) this Moves the cursor to the specified position using absolute coordinates.
async moveCursor(dx, dy) this Moves the cursor to the specified position using relative coordinates.
async writeString(s) this Writes s to the stream respecting TTY and colors.
async write(text, options) this Writes text to the stream.

forceColorDepth is used to override the number of colors. If forceColorDepth is defined, SGR sequences (responsible for colors and styles) are used even for non-TTY streams.

For non-TTY streams rows, columns, the result of getColorDepth() and properties of size are undefined. forceColorDepth overrides the result of getColorDepth().

For TTY streams, practically all accessors and methods delegate to the corresponding TTY stream methods:

Name TTY method
isTTY isTTY
rows rows
columns columns
size getWindowSize()
getColorDepth() getColorDepth()
hasColors() hasColors()
clearLine() clearLine()
clearScreenDown() clearScreenDown()
cursorTo() cursorTo()
moveCursor() moveCursor()

See the Node.js TTY documentation for argument details and return values, including the resize event.

clearLine(dir) interprets dir as a direction:

  • 0 — clear the entire line.
  • 1 — clear from the cursor to the end of the line.
  • -1 — clear from the beginning of the line to the cursor.

writeString() allows all CSI commands for TTY streams. If forceColorDepth is set, SGR sequences are preserved but other CSI sequences are removed. Otherwise, all CSI sequences are removed.

write(text, options) outputs a text container (strings, Box, or Panel — see Concepts). It can stream lines normally or draw them in place, advancing rows.

  • text — any value convertible to strings.
  • options — an object with the following properties:
    • sameColumn — This argument is used only for TTY streams. If true, the text is written in the same column as the previous line. If false, the text is streamed as is. If 'save', it is treated as true but the internal mechanism is different. See below for more details.
    • noLastNewLine — if true, the last line is not ended with a new line character.
    • beforeLine — the text to output before each line. Defaults to ''.
    • afterLine — the text to output after each line. Defaults to ''.

When using sameColumn on TTY streams, precede write() with cursorTo() or moveCursor(). With true, each line is written then the cursor moves to column start + one row down. With 'save', CURSOR_SAVE_POS/CURSOR_RESTORE_POS + CURSOR_DOWN1 are used instead.

The 'save' option handles accidental line wrapping but conflicts with code that already uses cursor save/restore (terminals don't support a stack). Use true in that case.

Exports

The Writer class is exported by name and as the default export.

output/updater.js

output/updater.js provides Updater — a class for updating console text in place.

Class Updater

Updater is the main mechanism for creating CLI UI with updatable text sections. Works with both TTY and non-TTY streams.

Name Return type Description
constructor(updater, options = {}, writer = new Writer()) this Creates a new Updater object.
updater object or function The source of updated frames.
writer Writer The Writer instance to use.
prologue string The text to output at the beginning.
epilogue string The text to output at the end.
beforeFrame string The text to output before each frame.
afterFrame string The text to output after each frame.
beforeLine string The text to output before each line.
afterLine string The text to output after each line.
noLastNewLine boolean If true, the last line is not ended with a new line character.
isDone boolean true if the updater is done.
isRefreshing boolean true if the updater is periodically refreshing by timer.
startRefreshing(ms = 100) this Starts refreshing the updater by timer.
stopRefreshing() this Stops refreshing the updater by timer.
reset() this Resets the updater to the initial state for a new run.
getFrame(state, ...args) string Gets the next frame hinting the updater state. Calls the target's nextFrame() if implemented (preferred for spinners — advances + reads), falling back to getFrame().
async writeFrame(state, ...args) undefined Writes the current frame hinting the updater state. Frames are written in call order.
async done() undefined Finishes updating: stops refreshing, waits for the frame being written, and writes the epilogue.
async update(state = 'active', ...args) this Updates the terminal with the current frame.
async final(...args) this Writes the final update and calls done().

updater can be either a function or an object. If it is an object, it must implement the following:

  • state — The state setter. The current updater state is assigned to it.
  • nextFrame(...args) — Preferred for spinners and progress bars: advances internal state by one step and returns the new frame. Used by Updater when present.
  • getFrame(...args) — Read-only fallback used when nextFrame() isn't implemented. Called once per frame; should return the current frame without mutating state.

If both methods are present, Updater calls nextFrame(). SpinnerBase (and its subclasses) implements both — pass a Spinner or spin\...`` directly.

If updater is a function, it is called as updater(state, ...args).

Both forms should return strings or a value convertible to strings.

state can be one of the following string values:

  • the empty string ('') — no updates are started.
  • 'active' — updates are started.
  • 'paused' — updates are paused.
  • 'finished' — updates are finished.

The updater can use this state to customize its frames.

For non-TTY streams, typically only 'finished' is used — no periodic updates occur.

constructor()'s options can have the following properties:

  • prologue — the text to output before the first frame. Typically it is sequence of CSI commands to prepare the terminal for updates. Defaults to RESET_ALL. Examples: hide the cursor, clear the screen.
  • epilogue — the text to output after the last frame. Typically it is sequence of CSI commands. Defaults to RESET_ALL. Examples: show the cursor, clear the screen.
  • beforeFrame — the text to output before each frame. Defaults to ''. Examples: clear the screen from the cursor before drawing a new frame.
  • afterFrame — the text to output after each frame. Defaults to ''. Examples: clear the screen from the cursor after drawing a new frame.
  • beforeLine — the text to output before each line. Defaults to ''. Examples: reset or set styles.
  • afterLine — the text to output after each line. Defaults to ''. Examples: reset styles, draw a border. You don't need it to clear leftovers: Updater clears a line that got narrower on its own (see below).
  • noLastNewLine — if true, the last line of a frame is not ended with a new line character. Defaults to false.

isRefreshing, startRefreshing() and stopRefreshing() are rarely used in non-TTY streams. Internally they are based on the setInterval function. Note: the first frame will be requested in ms milliseconds. If you want to update immediately, call update() before starting refreshing.

A frame waits for the one before it to finish writing, so frames never interleave, and a timer tick that arrives while a frame is still being written is skipped. When a frame has fewer lines than the one before it, the leftover lines below it are cleared (CLEAR_EOS), and when a line is narrower than the line it replaces, the rest of that line is cleared (CLEAR_EOL). Both are written only when needed. Widths count beforeLine and afterLine, ignore escape codes, and treat wide characters as two cells.

If isDone is true, update() and stopRefreshing() are no-ops, and done() and final() return the promise of the first done() call, which resolves when the epilogue is written.

reset() prepares the updater for a new run, for example the next task in a sequence. It stops refreshing, clears isDone, and forgets the last frame's size, so the next frame starts on new lines below the last one, which stays on screen. The next frame also sends the prologue again, so a run that follows done() starts with the prologue as the first run did. reset() doesn't reset the updater target; reset a spinner yourself to restart its animation.

If writer.isTTY is false, update() and startRefreshing() are no-ops.

Typical usage of the class:

import {Updater} from 'console-toolkit/output/updater.js';

const frameSource = state => 'state: ' + state;

const updater = new Updater(frameSource);

if (updater.writer.isTTY) {
  updater.update(); // draw the initial frame now
  updater.startRefreshing();
} else {
  updater.final(); // just draw the last frame
}

To track async operations (e.g., file downloads), call update() on progress events (TTY) and final() on completion. Both pass variadic ...args to the frame source.

For a more elaborate example, see scripts/memory.js. Sample output to a file:

Memory usage by this process (Press Ctrl+C to exit)
Legend: RSS - resident set size, green - goes down, red - goes up
┌───────────────┰────────────┬───────┐
│ Memory usage  ┃      Bytes │  Abbr │
┝━━━━━━━━━━━━━━━╋━━━━━━━━━━━━┿━━━━━━━┥
│ RSS           ┃ 48,758,784 │ 48.8M │
│ Heap total    ┃  7,524,352 │  7.5M │
│ Heap used     ┃  5,105,440 │  5.1M │
│ External      ┃  1,627,818 │  1.6M │
│ Array buffers ┃     10,515 │ 10.5k │
└───────────────┸────────────┴───────┘
 Heap - 5.1M of 7.5M:
 ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇

Exports

The Updater class is exported by name and as the default export.

Clone this wiki locally