Перейти к содержимому

Эта страница ещё не переведена и показана на английском.

node:readline

Read input line by line, ask questions and redraw terminal lines with Elide's readline, readline/promises and tty modules, with examples.

Читать офлайнelide help --lang ru api-node-readline

На этой странице

node:readline and node:readline/promises match Node’s readline: line iteration over any readable stream, prompts and questions, and the cursor helpers. node:tty is covered below.

Read lines

An interface is async-iterable, one line per step, with the line ending removed. This counts lines and words on stdin:

ts
// lines.ts
import { createInterface } from "node:readline";

const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
let count = 0;
let words = 0;
for await (const line of rl) {
  count++;
  words += line.split(/\s+/).filter(Boolean).length;
}
console.log(`${count} lines, ${words} words`);
utf-8 · ts · Lines: 11
Terminal
bash
printf 'the quick brown fox\njumps over\n\nthe lazy dog\n' | elide lines.ts
text
4 lines, 9 words
utf-8 · text · Lines: 1

Any readable stream works as input; for a file, pass fs.createReadStream(path). crlfDelay: Infinity treats \r\n as one line break.

Ask a question

question() from node:readline/promises writes the query and resolves with the next line:

ts
// confirm.ts
import { createInterface } from "node:readline/promises";

const rl = createInterface({ input: process.stdin, output: process.stdout });
const answer = await rl.question("Delete 3 files? [y/N] ");
rl.close();
console.log(answer.trim().toLowerCase() === "y" ? "Deleted." : "Kept.");
utf-8 · ts · Lines: 7
Terminal
bash
echo y | elide confirm.ts
text
Delete 3 files? [y/N] Deleted.
utf-8 · text · Lines: 1

Pass { signal } to give up on an answer; the promise rejects with an AbortError:

ts
const answer = await rl.question("Name? ", { signal: AbortSignal.timeout(10_000) });
utf-8 · ts · Lines: 1

The callback form, require("node:readline").createInterface(…).question(query, callback), works the same way.

Note

With piped input, readline reads ahead: lines that arrive before the next question() are emitted as line events, not answers. To script several answers, read them with for await or a line listener.

Prompt loop

prompt() writes the prompt, and each line event delivers one entry. close() ends the loop and emits close:

ts
// calc.ts
import { createInterface } from "node:readline";

const ops: Record<string, (x: number, y: number) => number> = {
  "+": (x, y) => x + y,
  "-": (x, y) => x - y,
  "*": (x, y) => x * y,
  "/": (x, y) => x / y,
};

const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: "calc> " });
rl.prompt();
rl.on("line", (line) => {
  const expr = line.trim();
  if (expr === "exit") return rl.close();
  const [a, op, b] = expr.split(" ");
  const apply = ops[op];
  console.log(apply ? apply(Number(a), Number(b)) : "usage: NUMBER (+|-|*|/) NUMBER");
  rl.prompt();
});
rl.on("close", () => console.log("bye"));
utf-8 · ts · Lines: 21
Terminal
bash
printf '2 + 3\n6 * 7\nexit\n' | elide calc.ts
text
calc> 5
calc> 42
calc> bye
utf-8 · text · Lines: 3

Redraw a line

cursorTo, moveCursor, clearLine and clearScreenDown write the matching escape sequences to a stream. Draw only when the output is a terminal, so logs and pipes get clean text:

ts
// progress.ts
import { clearLine, cursorTo } from "node:readline";
import { setTimeout as sleep } from "node:timers/promises";

const total = 5;
for (let done = 0; done <= total; done++) {
  if (process.stdout.isTTY) {
    cursorTo(process.stdout, 0);
    clearLine(process.stdout, 1);
    process.stdout.write(`[${"#".repeat(done).padEnd(total, ".")}] ${done}/${total}`);
  }
  await sleep(100);
}
if (process.stdout.isTTY) process.stdout.write("\n");
console.log("Done.");
utf-8 · ts · Lines: 15

In a terminal the bar fills in place; elide progress.ts | cat prints only Done..

Read single keys

emitKeypressEvents turns raw input into keypress events with the key’s name and modifiers. Put the terminal in raw mode to receive each key as it is pressed, and restore it before exiting:

ts
// keys.ts
import { emitKeypressEvents } from "node:readline";

if (!process.stdin.isTTY) {
  console.error("keys.ts needs a terminal");
  process.exit(1);
}
emitKeypressEvents(process.stdin);
process.stdin.setRawMode(true);
console.log("Press keys; q or Ctrl+C quits.");

process.stdin.on("keypress", (text, key) => {
  if (key.name === "q" || (key.ctrl && key.name === "c")) {
    process.stdin.setRawMode(false);
    process.exit(0);
  }
  console.log(`${key.ctrl ? "Ctrl+" : ""}${key.name ?? text}`);
});
utf-8 · ts · Lines: 18

Terminals with node:tty

node:tty matches Node’s tty: isatty(fd), ReadStream and WriteStream. process.stdin.isTTY, process.stdout.isTTY, process.stdout.columns and process.stdout.rows report the terminal:

ts
// tty.ts
import { isatty } from "node:tty";

console.log("stdin is a terminal:", isatty(0));
console.log("stdout is a terminal:", isatty(1));
if (process.stdout.isTTY) console.log("size:", process.stdout.columns, "x", process.stdout.rows);
utf-8 · ts · Lines: 6

In a 100-by-30 terminal, elide tty.ts prints:

text
stdin is a terminal: true
stdout is a terminal: true
size: 100 x 30
utf-8 · text · Lines: 3

Differences from Node

  • readline/promises does not export the Readline class; use the cursor helpers from node:readline.
  • import { stdin, stdout } from "node:process" fails; use the process global, as in the examples above.

See also

Reference

Status: Partial · Not measured · since 1.1.0

Exports

ExportKindStatusNotes
ReadlineInterfaceclassFullclose(), pause(), resume(), question(), prompt(), write(), line, cursor
createInterface()functionFullCreates a new readline.Interface instance
clearLine()functionFullClears the current line of the given TTY stream in a specified direction
clearScreenDown()functionFullClears the screen from the current position of the cursor down
cursorTo()functionFullMoves the cursor to the specified position in the given TTY stream
moveCursor()functionFullMoves the cursor relative to its current position in the given TTY stream

tty

ExportKindStatusNotes
isatty()functionFullReturns true if the given fd is associated with a TTY
ReadStreampropertyFullThe ReadStream class for reading from a TTY
WriteStreampropertyFullThe WriteStream class for writing to a TTY

Not implemented

readline/promises.Readline.

Была ли эта страница полезной?