Skip to content

Node.js · line reader

Jump to a line.
After an index, cheaply.

Stream, seek, or splice by line number without loading the file as a string. An optional .lseek sidecar makes later seeks an 8-byte lookup plus a short walk. It is not readline, not split, and not ripgrep.

Open the guidenpm i line-seeker
Diagram of seeking one line in a large log while only a 256 KiB buffer is held in memory.
getLine 500000after .lseekchunk 256 KiB
0runtime depsNode.js fs
256KiB chunksfile reads
8byte lookupthen ≤ stride lines
1full passto build .lseek

Honest split

Where it is strong, and where it is not.

stronger here

Repeated line N on a large file

buildIndex once. Later getLine reads 8 bytes from the sidecar, preads that offset, then walks at most stride lines. refreshIndex only scans bytes appended since.

RAM ≈ chunk size

weaker here

One pass, a tiny file, or search

readline or n-readlines for a single sequential walk. readFile + split for a few KB. ripgrep for search. Disk edits copy the whole file, not just the line.

use those instead

Optional index

Pay a scan once. Seek later.

buildIndex reads the file once and writes .lseek (~8 bytes per 64 lines). After that, line 500000 is an 8-byte lookup, a pread, then at most stride lines. Skip the index for a one-shot or a small file. find is still a linear scan.

How the sidecar is laid out →
// Index is optional. First build is a full read.
import { createSeeker, readLine, setLine } from "line-seeker";

await setLine("./notes.txt", 3, "updated");
const line = await readLine("./notes.txt", 3);

const mem = createSeeker({ text: "a\\nb\\nc\\n" });
await mem.replaceLine(2, "B");

const log = createSeeker("./huge.log");
await log.buildIndex({ stride: 64 });
await log.getLine(500_000);
01

Stream until the line

getLine(50) stops at line 50. Without an index, a late line still costs a prefix scan. The sequential cursor makes 50 then 51 cheap.

02

Optional sparse index

A .lseek sidecar stores every Nth offset. First build is a full read. Skip it unless you will seek again.

03

Same calls, different sources

Path, Buffer, or { text }. In-memory holds the buffer you passed in. Stdin is forward-only: no index, no edits.

04

Edits copy on disk

replaceLine splices in RAM for { text }. On a file it copies through a temp path. A one-line change on 5 GB rewrites 5 GB.

Node.js 18 · MIT

Limits are in the guide.

Zero dependencies. Node.js 18+.