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

parsil

v2.2.0

Published

A parser combinators library written in Typescript

Downloads

11

Readme

Parsil

Build Status npm Version License

A lightweight parser‑combinator library for JavaScript/TypeScript. Compose small, pure parsers into powerful language parsers that run in Node, Bun, and modern browsers.


Key features

  • Combinators for building complex grammars from tiny pieces
  • Great TypeScript inference
  • UTF‑8 aware character parsers
  • Source positions: capture current offset (index) and attach spans with parser.withSpan() / parser.spanMap()
  • Works on string and binary inputs (TypedArray/ArrayBuffer/DataView)
  • Helpful error messages and ergonomics (run, fork, map, chain, errorMap)

Install

# npm
npm i parsil

# bun
bun add parsil

ESM‑only as of v2.0.0. If you use CommonJS, dynamically import:

const P = await import('parsil')

Quick start

import * as P from 'parsil'
// or: import P from 'parsil'; // default namespace export

// Parse one or more letters, then digits
const wordThenNumber = P.sequenceOf([P.letters, P.digits])

const ok = wordThenNumber.run('hello123')
// { isError: false, result: ['hello', '123'], index: 8 }

const fail = wordThenNumber.run('123')
// { isError: true, error: "ParseError ...", index: 0 }

Binary example: IPv4 header (excerpt)

import * as P from 'parsil'

const tag = (type: string) => (value: unknown) => ({ type, value })

const packetHeader = P.sequenceOf([
  P.uint(4).map(tag('Version')),
  P.uint(4).map(tag('IHL')),
  P.uint(6).map(tag('DSCP')),
  P.uint(2).map(tag('ECN')),
  P.uint(16).map(tag('Total Length')),
])

// run against a DataView/ArrayBuffer

Breaking changes in v2.0.0

  • ESM‑only distribution. The CommonJS entry has been removed. Use import (or dynamic import in CJS).
  • Engines: Node ≥ 20 (Bun ≥ 1.1).
  • Character parsers (anyChar, anyCharExcept, etc.) return string values (not code points) and have updated TS types.

API (overview)

Parsil exposes a Parser<T> type and a set of combinators. Everything below is available as a named export and also through the default namespace.

Methods on Parser<T>

  • .run(input){ isError, result?, error?, index }
  • .fork(input, onError, onSuccess) → call either callback
  • .map<U>(fn: (value: T) => U)Parser<U>
  • .chain<U>(fn: (value: T) => Parser<U>)Parser<U>
  • .errorMap(fn) → map error details
  • .skip<U>(other: Parser<U>)Parser<T>
  • .then<U>(other: Parser<U>)Parser<U>
  • .between<L, R>(left: Parser<L>, right: Parser<R>)Parser<U>
  • .lookahead() → peek without consuming
  • .withSpan()Parser<{ value: T; start: number; end: number }> (returns value + byte offsets consumed)
  • .spanMap(fn) → map (value, { start, end }) to your own node shape

Core primitives

  • str(text) – match a string
  • char(c) – match a single UTF‑8 char exactly
  • regex(re) – match via JS RegExp (anchored at current position)
  • digit/digits, letter/letters, whitespace/optionalWhitespace
  • anyChar, anyCharExcept(p)
  • index – current byte offset (non‑consuming)

Combinators

  • sequenceOf([p1, p2, ...]) – run in order, collect results
  • choice([p1, p2, ...]) – try in order until one succeeds
  • many(p) / manyOne(p) – zero or more / one or more
  • exactly(n)(p) – repeat parser n times
  • between(left, right)(value) – parse value between left and right
  • sepBy(sep)(value) / sepByOne(sep)(value) – separated lists
  • possibly(p) – optional (returns null when absent)
  • lookAhead(p), peek, startOfInput, endOfInput
  • recursive(thunk) – define mutually recursive parsers
  • succeed(x) / fail(msg) – constant success/failure

Binary helpers

  • uint(n) – read the next n bits as an unsigned integer
  • int(n) – read the next n bits as a signed integer
  • Utilities: getString, getUtf8Char, getNextCharWidth, getCharacterLength

Full examples live in the examples/ directory: simple expression parser, IPv4 header, etc.


Error handling

Use .fork if you want callbacks instead of returned objects:

P.str('hello').fork(
  'hello',
  (error, state) => console.error(error, state),
  (result, state) => console.log(result, state)
)

Source positions & spans

Parsil exposes a non‑consuming index parser and span helpers on every parser instance:

import * as P from 'parsil'

// Read current offset
const at = P.index.run('hello') // { result: 0, index: 0 }

// Attach start/end byte offsets to any parser
const greet = P.str('hello').withSpan()
// greet.run('hello!') → { result: { value: 'hello', start: 0, end: 5 }, index: 5 }

// Map value + span to your own node shape (e.g., for AST tooling)
const node = P.str('XY').spanMap((value, loc) => ({ kind: 'tok', value, loc }))
// node.run('XY!') → { result: { kind: 'tok', value: 'XY', loc: { start: 0, end: 2 } }, index: 2 }

Offsets are byte‑based; editors like VS Code/CodeMirror can convert to line/column.


Contributing

  • Run tests: bun test
  • Lint: bun run lint
  • Build: bun run build

PRs welcome! Please add tests for new combinators.


License

MIT © Maxime Blanc


Changelog

Further changes are listed in CHANGELOG.md.

v2.0.0 (BREAKING)

  • ESM-only distribution. CommonJS entry removed. Use import (or dynamic import() in CJS).
  • Engines: Node ≥ 20, Bun ≥ 1.1.
  • Character parsers (anyChar, anyCharExcept, etc.) now return string values; types updated accordingly.
  • Build & DX: moved to Bun for tests/build; CI updated; tests relocated out of src/.

v1.6.0

v1.5.0

v1.4.0

v1.3.0

  • Improved type inference in choice, sequenceOf, and exactly using TS variadic tuple types.

v1.2.0

v1.1.0