marvdown
This is Marvdown ⚡️ A stupid simple Markdown parser
Summary
| Latest Version | Unknown |
|---|---|
| License | MIT |
| CI Status | Failing |
| Stars | 13 |
| Forks | 0 |
| Open Issues | 5 |
| Last Commit | 2026-09-06 |
| Downloads | 0 |
| Last Indexed | 2026-09-07 06:07 |
Tags
Installation
nimble install marvdown
choosenim install marvdown
git clone https://github.com/openpeeps/marvdown
OS Compatibility
| Platform | Linux | macOS | Windows | FreeBSD | OpenBSD | NetBSD | Android | iOS | WASM | Embedded |
|---|---|---|---|---|---|---|---|---|---|---|
| marvdown | ✓ | ✓ | ✓ | - | - | - | - | - | - | - |
Source
| Repository | https://github.com/openpeeps/marvdown |
|---|---|
| Homepage | https://openpeeps.github.io/marvdown/ |
| Registry Source | github |
README

This is Marvdown ⚡️ A stupid simple Markdown parser
nimble install marvdown
[!NOTE]
Marv is still in early development. Some features are not fully implemented yet. Contributions are welcome!
😍 Key Features
- [x] Extremely Fast & Lightweight! Check benchmarks
- [x] CommonMark compliant
- [x] Compiled cross-platform CLI app
- [x] Nim library for easy integration in your 👑 Nim projects
- [x] Markdown to HTML
- [x] Auto-generate heading IDs for anchor links
- [x] Table of contents data via
getSelectorItems - [x] Markdown to JSON (AST)
- [x] GitHub Flavored Markdown (partial): strikethrough, tables, task lists, autolinks, alerts
- [x] GitHub-style alerts (
NOTE,TIP,IMPORTANT,WARNING,CAUTION) - [x] Footnotes
- [x] Reference-style links
- [x] Bare URL & email autolinks
- [x] YAML front matter
- [x] Components:
@includefiles,@attrprops &$variableinterpolation - [x] Lazy-loading for iframes, videos & images
- [x] Custom per-line transform hook (
customTransform) - [ ] Markdown to PDF
About
Marv is a stupid simple markdown parser written in Nim. It can be used as a library in your Nim projects or as a CLI tool to convert markdown files to HTML. It supports headings, paragraphs, bold, italic, strikethrough, links, images, lists (incl. task lists), blockquotes (incl. GitHub alerts), code blocks, inline code, tables (with optional footer), footnotes, reference links, raw HTML, autolinks, YAML front matter and more.
Installing
Install Marvdown via Nimble
nimble install marvdown
Quick Start
From the command line
marvdown html sample.md --optAnchors
marvdown html sample.md --optAnchors --output out.html
marvdown json sample.md
--optAnchors— generate heading anchors (with a 🔗 link icon)--bench— print timing stats--components— enable@include/@attr/$variablecomponents
As a Nim library
import marvdown
# one-liner
echo marvdown.toHtml(readFile("sample.md"))
# with custom options
let opts = MarkdownOptions(
allowed: @[tagP, tagStrong, tagEm, tagA, tagCode, tagPre],
enableAnchors: true,
anchorIcon: "🔗"
)
var md = newMarkdown(readFile("sample.md"), opts)
echo md.toHtml()
Settings (MarkdownOptions)
All knobs live on the MarkdownOptions object passed to newMarkdown:
let opts = MarkdownOptions(
# Which raw HTML tags are allowed. Empty `@[]` means NO raw HTML.
allowed: @[tagA, tagDiv, tagSpan, tagImg, tagP, tagPre, tagCode],
# …or allow tags by category instead:
allowTagsByType: none(TagType), # tagNone | tagInline | tagBlock | tagAll
allowInlineStyle: false, # allow `style` attributes/tags
allowHtmlAttributes: false, # allow attributes like `width`, `title`
enableAnchors: true, # add id="…" + anchor link to headings
anchorIcon: "🔗", # icon used inside the anchor link
showFootnotes: true, # render footnotes at the end of the doc
htmlTableClasses: none(seq[string]), # e.g. some(@["table", "table-striped"])
enableEmailAutolinks: false, # `<user@example.com>` → mailto link
enableComponents: false, # enable @include / @attr / $variable
componentBaseDir: "", # base dir for @include paths
customTransform: nil, # proc(line: string): string per-line hook
lazyloadIframes: false, # <iframe src> → data-src
lazyloadVideos: false, # <video>/<audio>/<source> src → data-src
lazyloadImages: false, # <img> &  src → data-src
parseYaml: true # parse YAML front matter (--- blocks); set false to skip for speed
)
Features
Headings & anchors
Build a table of contents from the generated anchors:
for item in md.getSelectorItems(): # seq[(level, anchor, title)]
echo item.level, ". ", item.title, " #", item.anchor
Inline formatting
Supports bold, italic, code, ~~strikethrough~~ and combinations.
Links & autolinks
Inline links with optional titles, bare https:// auto-links and mailto: email autolinks via enableEmailAutolinks.
Images
Images with alt text and optional titles.
Lists & task lists
Unordered (-, *, +), ordered (1.) and task lists (- [x], - [ ]) with nesting.
Blockquotes & alerts
Blockquotes and GitHub-style alerts. Supported markers: NOTE, TIP, IMPORTANT, WARNING, CAUTION.
Code blocks
Fenced code blocks with language info and indented code blocks (4 spaces).
Tables (with optional footer)
| Name | Role |
| ---- | ---- |
| Ana | CEO |
| Bob | CTO |
|------|------|
| Total | 2 |
<table>
<thead><tr><th>Name</th><th>Role</th></tr></thead>
<tbody><tr><td>Ana</td><td>CEO</td></tr><tr><td>Bob</td><td>CTO</td></tr></tbody>
<tfoot><tr><td>Total</td><td>2</td></tr></tfoot>
</table>
Add CSS classes with htmlTableClasses: some(@["table", "table-striped"]).
Footnotes
Footnote references and definitions rendered at the end of the document.
Reference links
Supports explicit [text][ref], collapsed [text][] and shortcut [text] references.
Raw HTML
Raw HTML is gated by the allowed / allowTagsByType options.
Components (@include, @attr, $variable)
Include other files (markdown or HTML) and use props & variables in the HTML:
card.html:
<div @title="Marvdown" @badge="v0.1.4">
<h2>$title</h2>
<p>Release <code>$badge</code></p>
</div>
page.md:
@include("card.html")
<div>
<h2>Marvdown</h2>
<p>Release <code>v0.1.4</code></p>
</div>
@attr="value"is captured into a global scope and stripped from the output$variableis resolved from the scope; unknown variables stay literal\$escapes to a literal$- Enable with
enableComponents: trueand setcomponentBaseDirto the folder containing the includes
customTransform
Hook every body line before it is parsed — great for custom syntax:
let opts = MarkdownOptions(
customTransform: proc(line: string): string =
if line == "@card": "<div class=\"card\">Custom card</div>"
else: line
)
Lazy-loading media
MarkdownOptions(lazyloadIframes: true)
MarkdownOptions(lazyloadVideos: true)
MarkdownOptions(lazyloadImages: true)
<iframe src="…">, <video src>, <audio src>, <source src> and <img src> (both raw HTML and ) are rewritten from src to data-src, ready for an IntersectionObserver.
YAML front matter
let header = md.getHeader() # YAMLObject (OrderedTable)
echo yamlmod.getStr(header["title"]) # "Marvdown"
AST / JSON
echo marvdown.getAst(readFile("sample.md"))
[{"kind":"mdkHeading","level":1,"textAnchor":null,
"children":{"items":[{"kind":"mdkText","text":"Hello","children":null,"line":1}]},
"line":1}]
Examples
Check out the examples/ folder for runnable code:
# run the comprehensive feature example
nim c -r examples/example.nim
# or use the CLI on a feature-rich sample document
nim c src/marvdown.nim
./marvdown html examples/sample.md --optAnchors
./marvdown json examples/sample.md
Benchmarks
Marvdown is super fast! Run clue test -d:release (options allowTagsByType: tagAll, parseYaml: false):
Marvdown Benchmark – toHtml (wall time, release)
==================================================
| Document | Size | Iters | Total ms | Avg ms | Throughput | Out | CPU ms |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
| sample.md (CommonMark) | 27.1 KB | 500 | 269.20 | 0.538 | 49.22 MB/s | 27.9 KB | 269.04 |
| tests/data/big.md | 4.84 MB | 5 | 841.29 | 168.257 | 28.76 MB/s | 6.15 MB | 840.78 |
| synthetic 100 lines | 4.2 KB | 200 | 36.75 | 0.184 | 22.54 MB/s | 6.8 KB | 36.75 |
| synthetic 1k lines | 43.8 KB | 50 | 92.55 | 1.851 | 23.08 MB/s | 68.9 KB | 92.52 |
| synthetic 10k lines | 451.2 KB | 5 | 116.21 | 23.242 | 18.96 MB/s | 703.1 KB | 116.20 |
| tiny 1 line | 14 B | 1000 | 1.99 | 0.002 | 6.72 MB/s | 26 B | 1.99 |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
Iterations: 1760 | Total wall: 1357.98 ms
Marvdown Benchmark – toHtml + anchors
=======================================
| Document | Size | Iters | Total ms | Avg ms | Throughput | Out | CPU ms |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
| sample.md (CommonMark) + | 27.1 KB | 250 | 133.27 | 0.533 | 49.71 MB/s | 28.0 KB | 133.27 |
| synthetic 1k lines +anch | 43.8 KB | 25 | 48.78 | 1.951 | 21.90 MB/s | 75.6 KB | 48.79 |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
Iterations: 275 | Total wall: 182.05 ms
Marvdown Benchmark – toHtml vs toJson (sample.md)
===================================================
| Document | Size | Iters | Total ms | Avg ms | Throughput | Out | CPU ms |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
| sample toHtml | 27.1 KB | 100 | 52.58 | 0.526 | 50.40 MB/s | 27.9 KB | 52.58 |
| sample toJson | 27.1 KB | 100 | 72.27 | 0.723 | 36.66 MB/s | 42.6 KB | 72.27 |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
Iterations: 200 | Total wall: 124.85 ms
Scaling check – 100 vs 1k lines
=================================
| Document | Size | Iters | Total ms | Avg ms | Throughput | Out | CPU ms |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
| 100 lines | 4.2 KB | 100 | 18.40 | 0.184 | 22.50 MB/s | 6.8 KB | 18.40 |
| 1k lines | 43.8 KB | 20 | 36.95 | 1.848 | 23.13 MB/s | 68.9 KB | 36.95 |
|--------------------------|------------|---------|------------|------------|------------------|------------|------------|
Iterations: 120 | Total wall: 55.35 ms
Benchmark via tests/test_benchmark.nim plain-text table; Nim 2.2.0, macOS amd64 (clue). Re-run with clue test -d:release.
❤ Contributions & Support
- 🐛 Found a bug? Create a new Issue
- 👋 Wanna help? Fork it!
Credits
Original illustration made by 💙 Olha remixed with Sora.
🎩 License
Marv | MIT License.
Made by Humans from OpenPeeps.
Copyright © 2024 OpenPeeps & Contributors — All rights reserved.