Peisar parses CommonMark Markdown with GitHub Flavored Markdown (GFM),
Kramdown-style block attributes, YAML front matter, source spans, AST
visitors, custom parser hooks, and configurable HTML output. It ships as
a native Node.js addon and as a plain Rust library (rlib) for use
in any Rust project.
Features
- CommonMark — headings, paragraphs, code blocks, block quotes, lists, thematic breaks, HTML blocks, emphasis, links, images, inline code, and hard or soft breaks.
- GFM — tables with column alignment, strikethrough, task lists, and autolinks.
- Kramdown attributes —
{:#id .class key="value"}on block elements. - YAML front matter — metadata is available separately from the parsed Markdown.
- AST visitors — transform the parsed AST from JavaScript or Rust before reading it or rendering HTML.
- Custom parser hooks — parse extension syntax (directives, wikilinks, mentions) during parsing, before the built-in matchers.
- Source spans — every node carries a zero-based
[start, end)span (line, column, byte offset) into the original source. - Node.js bindings — native N-API addon with TypeScript declarations.
- Rust crate — the same parser is usable as a plain Rust library, without Node.
Quick start
Install the package:
Parse Markdown and render it as HTML:
const = require
const document =
console.log
console.log
console.log
By default, GFM and Kramdown extensions are enabled and html is a complete
HTML document.
Development build
Configuration
Pass only the options you need; omitted properties retain their defaults.
const = require
const document =
console.log // <h1>Title</h1>
Supported options:
gfmandkramdownenable their respective syntax extensions (default:true).fileNameattaches a source name to the returned AST.fragmentreturns only rendered body content whentrue(default:false).charset,viewport,title,bodyClass, andstyleconfigure the full HTML document.bodyClassis applied to<body>.
AST visitors
const = require
const document =
document.
console.log // <h1 class="title">Hello</h1>
Custom parser hooks
Visitors transform the AST after parsing; parser hooks let extensions parse custom syntax during parsing. Hooks run before the built-in parsers (so they can both introduce new syntax and override built-ins), in registration order — the first hook to claim a position wins.
useParser takes an object with two optional callbacks:
const document =
// Positions must be complete objects — the hook's own `pos` value is a
// placeholder; accurate spans are computed automatically.
const zero =
document.
console.log
// <div class="note" data-name="note"></div>
// <p>See <a href="https://wiki.example.com/Some_Page">Some Page</a>.</p>
Notes:
- Hooks apply everywhere Markdown is parsed — headings, emphasis/link children, block quotes, list items, and table cells included.
parseBlockmust consume at least one line;parseInlineshould reportconsumed: 0(or returnundefined) to decline.- Source spans of hook-returned nodes are computed from
consumed; a Kramdown{:...}attribute line after a hook-parsed block is applied automatically. - Because the document is parsed at construction,
useParserre-parses the original Markdown. Register hooks before readingast,html,frontmatter, orastJson.
YAML front matter
Leading YAML front matter is excluded from the rendered Markdown and exposed
through frontmatter:
const document =
console.log // { title: 'Hello' }
Rust API
The same parser is available as a plain Rust library — no Node.js runtime
required. The crate builds both an rlib (for Rust consumers) and a
cdylib (the Node addon), so adding it as a dependency gives you the full
parsing API.
[]
= "0.1.2"
Parse Markdown into an AST
Document::parse is the entry point. AstOptions controls the enabled
extensions, and every node carries a zero-based source Span:
use Block;
use ;
let doc = parse;
assert_eq!;
match &doc.children
Strict CommonMark (disable GFM and Kramdown) with a file name attached:
use ;
let opts = AstOptions ;
let doc = parse;
assert_eq!;
Parse and render HTML
Peisar mirrors the JavaScript class: parse once, then read html,
ast, frontmatter, and ast_json:
use ;
let mut document = new;
// Default options: GFM + Kramdown enabled, full HTML document.
println!;
// <!DOCTYPE html>
// <html>
// <head>…</head>
// <body>
// <h1>Hello <strong>world</strong></h1>
// </body>
// </html>
// `fragment: true` renders just the body content.
let mut fragment = new;
println!; // <h1>Hello <strong>world</strong></h1>
PeisarOptions is the same shape as the JavaScript options object —
gfm, kramdown, fileName, fragment, charset, viewport, title,
bodyClass, and style — with every field optional.
AST, front matter, and JSON
PeisarAst is the pure-Rust document holder used by Peisar. It parses
front matter eagerly and re-runs registered visitors before each read:
use Block;
use PeisarAst;
let mut ast = new;
// YAML front matter as a serde_json value.
assert_eq!;
// The parsed AST (front matter already stripped).
assert!;
// Serialized AST JSON.
let json = ast.ast_json;
assert!;
Front matter is also available standalone through the frontmatter module:
use parse_markdown_frontmatter;
let parsed = parse_markdown_frontmatter?;
let md: &str = parsed.pure_markdown_content; // "# Hello"
let yaml = parsed.yaml_data.unwrap; // {"title": "Hello"}
// Or take both by value in one call.
let = parsed.into_parts;
AST visitors
Implement the AstVisitor trait and run it with visit_document_mut to
transform the AST before rendering. Each callback receives a &mut node and
returns a control struct that can keep, replace, remove, or insert around
it:
use ;
use ;
use ;
;
let mut doc = parse;
visit_document_mut;
VisitControl / InlineVisitControl support insert_before,
insert_after, replace_with, remove, and recurse (plus
keep_and_recurse() / remove() / replace_with(nodes) convenience
constructors). Replacements are not re-visited.
Inline parsing
parse_inline parses inline content in isolation (for table cells,
headings, or plain text). parse_inline_with_refs additionally resolves
reference-style links against a LinkRefMap:
use Inline;
use ;
let inlines = parse_inline;
assert!;
// Resolve `[label][]` against collected link reference definitions.
let mut refs = new;
refs.insert;
let inlines = parse_inline_with_refs;
assert!;
Custom parser hooks (Rust)
The AstParser trait is the Rust counterpart of the JavaScript useParser
hooks — hooks run before the built-in matchers, so they can introduce new
syntax or override built-ins:
use Block;
use ;
;
ParseHooks::empty().with(&hook) builds the ordered hook registry (the
first hook to claim a position wins). Note that hook-armed document parsing
is currently wired through the JS-facing registration APIs —
PeisarAst::add_parser / Peisar::use_parser — which wrap JS callbacks in
this same trait; pure-Rust hook registration for Document::parse is not
yet exposed at the crate root.
Rust API surface
Everything above is re-exported from the crate root:
| Item | Kind | Purpose |
|---|---|---|
Peisar |
struct | Parse + render in one place (mirrors the JS class) |
PeisarAst |
struct | Own the AST, visitors, hooks, front matter |
PeisarOptions |
struct | Combined parse + render options (JS shape) |
Document |
struct | AST root; Document::parse entry point |
AstOptions |
struct | gfm, kramdown, file_name |
parse_inline / parse_inline_with_refs |
fn | Standalone inline parsing |
LinkRefMap |
type | label → (url, title) map for reference links |
AstVisitor |
trait | Post-parse AST transforms |
AstParser |
trait | During-parse extension hooks |
BlockParseContext / InlineParseContext |
struct | Data given to hooks |
ParseHooks |
struct | Ordered hook registry |
token |
module | Block, Inline, Table, ListItem, Attributes, … |
span |
module | Span, Position source mapping |
visitor |
module | VisitControl, InlineVisitControl, visit_document_mut |
frontmatter |
module | parse_markdown_frontmatter, ParseResult |
Run the API docs locally with:
GFM examples
Tables
Task lists
- -
Strikethrough
~~deleted text~~
Project structure
src/
├── ast/ AST definitions, parsers, visitors, and tests
├── config/ JavaScript option conversion
├── frontmatter.rs YAML front-matter extraction
├── html.rs HTML renderer
└── lib.rs Public Rust API + Node.js N-API bindings
The generated TypeScript API is available in index.d.ts;
the Rust API is documented in the source and via cargo doc.