remarkable
A fast, CommonMark-compliant Markdown parser for JavaScript with a pluggable, rule-based rendering pipeline.
Repository Health
Technical Analysis
Remarkable is a CommonMark-compliant Markdown-to-HTML parser for JavaScript, built around a three-stage rule pipeline (core, block, and inline) that developers can extend or override without forking the library. It powers Markdown rendering in projects like Docusaurus and has been used by Facebook, and ships as CommonJS, ESM, and UMD builds alongside a CLI for piping Markdown files or stdin straight to HTML.
Beyond parsing GitHub-Flavored Markdown extensions like tables, footnotes, and strikethrough out of the box, Remarkable’s .use() plugin API and named rule chains (before, after, push, at) let you insert custom syntax or rewrite the renderer entirely — the same architecture its own linkify and syntax-highlighting integrations use, and one later echoed by libraries like markdown-it.
What You Get
- CommonMark-compliant parsing plus GFM extensions (tables, footnotes, strikethrough) enabled by default
- A three-stage rule pipeline (core/block/inline), each with
before/after/push/atmethods for inserting or replacing individual parsing rules - Prebuilt CJS, ESM, and UMD/browser bundles plus a CLI (
remarkable --file) for standalone use - An official linkify plugin (
remarkable/linkify) for autoconverting URL-like text into links - Configurable presets (
default,full,commonmark) and typographer replacements for smart quotes, dashes, and symbols
Common Use Cases
- Static site generators - projects like Docusaurus use Remarkable to render Markdown content pages to HTML at build time.
- Custom Markdown dialects - teams needing non-standard syntax (e.g. custom callouts) extend the ruler chain instead of forking a parser.
- CLI Markdown-to-HTML conversion -
cat file.md | remarkableturns Markdown files into HTML for quick previews or build pipelines. - Embedding rendered Markdown in web apps - the UMD browser bundle lets client-side apps render user-submitted Markdown, with raw HTML disabled by default to reduce injection risk.
Under The Hood
Architecture
The Remarkable class (lib/index.js) wires together five collaborators — ParserCore, ParserBlock, ParserInline, Renderer, and a Ruler per parser stage — and exposes .parse()/.render() as thin wrappers around a per-call StateCore that tracks tokens and env. Each parser stage owns its own Ruler (lib/ruler.js), a responsibility-chain manager with before/after/push/at/enable/disable methods and a compiled, cached rule list keyed by chain name; rule modules live under rules_core/, rules_block/, and rules_inline/ as small, independent functions pushed onto these chains at construction time (parser_core.js). The Renderer mirrors this design with a flat rules object mapping token type to render function (lib/rules.js), so both parsing and rendering are extended the same way. Plugins are just functions invoked via .use(plugin, opts) that mutate these rulers directly — a minimal, well-scoped extension surface, though the classes still rely on mutable prototype state (this.__cache__, this.__rules__) rather than immutable data structures.
Tech Stack
The source is plain ES module JavaScript with no runtime framework, built into CommonJS, ESM, and browser UMD bundles via Rollup (rollup.config.js), with a browser field in package.json swapping in browser-safe entry points at bundle time. Runtime dependencies are minimal: argparse powers the CLI (lib/cli.js, exposed as bin/remarkable.js) and autolinker backs the optional linkify plugin. Development tooling includes Mocha for tests, nyc for coverage, ESLint (with eslint-plugin-es5 to keep the pre-transpiled source restricted to ES5-safe patterns), and benchmark for performance comparisons against commonmark and marked reference implementations.
Code Quality
Tests live under test/ and are comprehensive for a project this size: test/misc.js covers edge cases and options, test/ruler.js directly exercises the Ruler class’s rule-chain behavior, test/commonmark.js runs the official CommonMark spec fixtures, and test/linkify.js and test/cli.js cover the plugin and CLI entry points respectively. There is no TypeScript or other static type layer, and error handling is limited to descriptive throw new Error(...) calls for invalid rule names and missing presets. The README’s CI badge points to travis-ci.org, and there is no GitHub Actions workflow in .github/ — so automated CI is effectively dormant even though the test suite itself is solid, consistent with the repo’s overall inactive commit history.
What Makes It Unique
Remarkable’s contribution is less any single novel feature and more the clean separation of a rule-chain parser from a swappable per-type renderer, exposed through a tiny, consistent API (before/after/push/at, .use()) that plugin authors can reason about without reading the parser internals. This responsibility-chain design predates and closely resembles the architecture later popularized by markdown-it (built by an overlapping set of maintainers), rather than introducing a fundamentally new parsing technique — its main practical edge is the combination of CommonMark compliance with that pluggable architecture and JIT-friendly, monomorphic code for speed.