npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

libripgrep

v0.1.2

Published

Native bindings for ripgrep

Readme

libripgrep

Native ripgrep bindings for Node.js.

ripgrep isn't distributed as a library, but its core functionality is: this package binds the same Rust crates ripgrep is built from (ignore, globset, grep-regex, grep-matcher) into a JavaScript/TypeScript API, with all filesystem I/O and regex scanning running off the main thread on native worker threads. In benchmarks it matches the ripgrep CLI's speed.

Installation

npm install libripgrep

Requires Node.js v24.x.x or later. Only GNU/Linux and MacOS are supported.

Prebuilt binaries are included for linux-x64, linux-arm64, darwin-x64, and darwin-arm64. The main entry point auto-detects your platform. If you need a require graph with no dynamic loading at all (e.g. for bundlers), you can import a specific platform directly:

import { grepTree } from 'libripgrep/linux-arm64';

Basic usage

import { compileGlob, compileGrep, walkTree, grepTree } from 'libripgrep';

// Search a directory tree for regex matches (like `rg 'TODO|FIXME'`):
for await (const { entry, matches } of grepTree('./src', { patterns: ['TODO|FIXME'] })) {
	for (const { line, lineNumber } of matches) {
		console.log(`${entry.parentPath}/${entry.name}:${lineNumber}: ${line}`);
	}
}

// Walk a tree, respecting .gitignore et al (like `rg --files`):
for await (const entry of walkTree('.', { includeGlobs: ['**'] })) {
	console.log(entry.name, entry.isDirectory());
}

// Compile a glob to a reusable predicate:
const isSource = compileGlob('src/**/*.{js,ts}');
isSource('src/api.ts'); // => true

// Compile regexes to a reusable buffer scanner:
const scan = compileGrep({ patterns: ['^import\\b'] });
scan(await fs.promises.readFile('src/index.ts')); // => MatchedLine[]

API

compileGlob(globPattern, globOptions?)

Compiles a glob pattern and returns a matcher function (relativePath: string) => boolean. Throws on invalid patterns.

Globs use ripgrep's syntax (*, **, ?, [a-z], [!a-z] or [^a-z], {a,b} with nesting). / is always a literal separator: * and ? never match it. Matching runs against relative paths (no leading /).

GlobOptions:

| Option | Default | Meaning | | -------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | caseInsensitive | false | Match paths case-insensitively. | | backslashEscape | true | \ escapes the following character, making it literal (so you need two consecutive \ characters to match a single literal \). A trailing \ is an error. | | emptyAlternates | true | Empty alternate branches (foo{,.txt}) match zero characters. If disabled, such branches are ignored. | | allowUnclosedClass | false | Treat an unclosed [ as a literal instead of erroring. | | explicitDotfiles | false | Prevent **, *, ?, and negated classes from matching dotfiles. Literal dots and non-negated classes ([.a]) still can. |

compileGrep(grepOptions)

Compiles one or more regex patterns and returns a scanner function (data: Readonly<Buffer>) => MatchedLine[] that finds all lines matching any pattern. Compilation cost is paid once; the scanner can be reused on any number of buffers. Throws on invalid patterns (including empty patterns).

interface MatchedLine {
	line: string; // the entire matching line, without its terminator
	lineNumber: number; // 1-based
	matches: [number, number][]; // per-match [start, end) offsets into `line`, in UTF-16 code units (ready for String.prototype.slice)
	linesBefore: string[] | undefined; // provided when beforeContext is greater than zero
	linesAfter: string[] | undefined; // provided when afterContext is greater than zero
}

GrepOptions:

  • patterns — one or more regex patterns (Rust regex syntax, like ripgrep). A line matches if any pattern matches.
  • beforeContext — include up to this many physical lines immediately before each matching line. Default 0.
  • afterContext — include up to this many physical lines immediately after each matching line. Default 0.
  • regexOptions:

| Option | Default | Meaning | | ----------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | caseInsensitive | false | Case-insensitive matching. | | multiline | false | Patterns may match across lines; matches are reported clipped to each line they touch. Without it, patterns containing \n are compile errors (like ripgrep). | | multilineDotall | false | . matches line terminators (only meaningful with multiline). | | crlf | true | Treat \r\n as a line terminator: $ matches before it and the \r is excluded from line. | | unicode | true | Unicode-aware character classes (\w, \d, ...). |

Encodings are detected from a BOM (Byte Order Mark) only: UTF-16 LE/BE is transcoded, a UTF-8 BOM is stripped, and everything else is assumed to be UTF-8 (invalid bytes become U+FFFD). Buffers containing a NUL byte are considered binary and yield no matches, matching ripgrep's default binary handling.

walkTree(rootPath, walkOptions?)

An AsyncGenerator<TreeEntry> yielding the root and every file and directory within it. TreeEntry implements the fs.Dirent interface (name, parentPath, isFile(), isDirectory(), isSymbolicLink(), ...). Traversal runs on native threads and streams back with backpressure; exiting the loop early (break/return/throw) stops the walk.

WalkOptions:

| Option | Default | Meaning | | -------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | threads | 4 | Walker thread count. | | symlinks | false | Follow symlinks (with cycle detection). The root is always followed regardless. | | maxDepth | Infinity | 0 yields only the root. | | ignoreFiles | [] | Extra gitignore-format files; later files take precedence over earlier ones. | | ignoreStyle | 'all' | 'all' respects .gitignore (only inside real git repositories), .ignore, .rgignore, .git/info/exclude, and git's global excludes, ascending parent directories like ripgrep. 'no-git' respects only .ignore/.rgignore. 'none' respects nothing automatically. Even with 'none', explicit ignoreFiles are always respected. | | includeGlobs | [] | Only yield entries matching at least one glob (non-matching directories are still traversed). | | excludeGlobs | [] | Never yield or descend into entries matching any of these globs. | | globOptions | — | GlobOptions applied to both glob lists. |

Globs match paths relative to rootPath. excludeGlobs prunes traversal: an excluded directory is never descended into. includeGlobs only filters what is yielded: ['**/*.txt'] finds every nested .txt file, and the intermediate directories are traversed but not yielded (since they don't match). Hidden files are never treated specially — to exclude dotfiles, use includeGlobs: ['**'] with globOptions: { explicitDotfiles: true }.

grepTree(rootPath, grepTreeOptions)

An AsyncGenerator<{ entry: TreeEntry, matches: MatchedLine[] }> yielding each file with at least one matching line. Does the same traversal as walkTree() while reading and scanning candidate files on the walker threads — the main thread stays free.

GrepTreeOptions extends GrepOptions with:

| Option | Default | Meaning | | ------------- | ------- | ----------------------------------------------------------------------- | | maxFileSize | 16 MiB | Skip files larger than this many bytes (Infinity disables the limit). | | walkOptions | — | Traversal options, as in walkTree(). |

Behavior notes

  • All filesystem I/O (directory reading, file reading) happens off the main thread; mmap is never used.
  • Regex engine: ripgrep's default engine (grep-regex) only — no PCRE2. ripgrep's default regex and DFA size limits apply. Multiple patterns compile into one alternation, so overlapping matches from different patterns are reported leftmost-first, non-overlapping (as in ripgrep).
  • Patterns that could match a NUL byte are rejected, like ripgrep.
  • An error reading the root (e.g. it doesn't exist) rejects the generator. Errors deeper in the tree — unreadable directories or files, broken symlinks — are silently skipped, like ripgrep's warn-and-continue.
  • An unreadable root directory yields just the root entry, since the error occurs when reading its contents.
  • An empty file has zero lines; an empty match after a trailing newline is not reported on a phantom line.

Development

npm install            # also compiles TypeScript (prepare)
npm run build:native   # cargo build + copy into prebuilds/ (requires Rust)
npm test               # jest
npm run prettier
npm rust-test          # cargo test
npm rust-fmt           # cargo fmt
node bench/grep-tree.js <dir> # benchmark vs rg and naive Node

License

MIT