RedQuilt
A modern Markdown document processor in pure Ruby, with an arena-style AST. Passes the full CommonMark spec test suite, and generally faster than kramdown.
Installation
Add this line to Gemfile:
gem "red_quilt"
Quick Start
Parsing and rendering
require "red_quilt"
# Parse Markdown to a document
doc = RedQuilt.parse("# Hello\n\nThis is **bold**.")
html = doc.to_html
# => "<h1>Hello</h1>\n<p>This is <strong>bold</strong>.</p>\n"
# Or render directly (without building AST)
html = RedQuilt.render_html("# Hello\n\n**bold**")
HTML is safe by default
RedQuilt.render_html("Hi <em>tag</em>")
# => "<p>Hi <em>tag</em></p>\n"
RedQuilt.render_html("Hi <em>tag</em>", allow_html: true)
# => "<p>Hi <em>tag</em></p>\n"
Options
RedQuilt.parse and RedQuilt.render_html accept:
| Option | Default | Effect |
|---|---|---|
allow_html: |
false |
Pass raw HTML through instead of escaping it |
disallow_raw_html: |
false |
With allow_html, still neutralize GFM's dangerous tags (<script>, <iframe>, …) |
extended_autolinks: |
false |
GFM: linkify bare http(s):// / www. / email addresses |
footnotes: |
false |
GFM footnotes (see below) |
lint: |
false |
Collect lint diagnostics (empty links, missing image alt, heading-level skips) |
Footnotes (opt-in)
RedQuilt.render_html(<<~MD, footnotes: true)
Here is a reference.[^1]
[^1]: And the footnote text.
MD
# The reference becomes a superscript link, and a trailing
# <section class="footnotes"> lists the referenced definitions (in
# first-reference order) with backrefs.
Diagnostics
Parsing never raises on malformed input; warnings are collected on the document.
doc = RedQuilt.parse("[x](javascript:alert(1))", lint: true)
doc.diagnostics.map(&:rule) # => [:unsafe_url]
doc.diagnostics.first.severity # => :warning
Heading anchors (opt-in)
render_html / to_html accept heading_ids: to give every heading a
slugified id for anchor links.
RedQuilt.render_html("# Hello World\n\n## はじめに", heading_ids: true)
# => "<h1 id=\"hello-world\">Hello World</h1>\n<h2 id=\"はじめに\">はじめに</h2>\n"
Mermaid diagrams (opt-in)
render_html / to_html accept mermaid: to render `mermaid fenced
code blocks for mermaid.js.
In standalone output the mermaid.js runtime is also loaded from a CDN.
RedQuilt.render_html("```mermaid\ngraph LR\n A --> B\n```", mermaid: true)
# => "<pre class=\"mermaid\">graph LR\n A --> B\n</pre>\n"
# Full page that renders the diagram in a browser (CDN script included):
RedQuilt.parse("```mermaid\ngraph LR\n A --> B\n```")
.to_html(standalone: true, mermaid: true)
In standalone output each diagram is made interactive with svg-pan-zoom (loaded from a CDN).
Tilt integration
RedQuilt ships a Tilt adapter.
NOTE: It is not loaded by default; require it explicitly and add tilt to your own bundle:
require "red_quilt/tilt"
Tilt.new("page.md").render # => HTML
Tilt.new("page.md", footnotes: true).render
Native options (allow_html:, footnotes:, …) pass straight through; Tilt's escape_html: convention is also honored.
Documentation
- API reference —
Document/NodeRef/SourceSpan, supported syntax, and usage examples - Architecture overview (日本語)
- Arena usage guide (日本語)
- CommonMark conformance notes (日本語)
CommonMark Compatibility
RedQuilt achieves 100% compliance with the CommonMark v0.31.2 test cases. See the conformance notes for GFM extensions and intentional deviations.
Command-line Tool
RedQuilt ships with a redquilt CLI for converting Markdown files to HTML or inspecting the AST.
Basic usage
# Convert Markdown file to HTML
redquilt input.md > output.html
# Convert from stdin
echo "# Hello" | redquilt
# Output as AST (for debugging)
redquilt --format ast input.md
# Output as MDAST-compatible JSON (for external tools)
redquilt --format json input.md
# Standalone HTML document with title
redquilt --standalone --title "My Document" input.md
# Enable GFM extended autolinks / footnotes
redquilt --extended-autolinks --footnotes input.md
# Standalone page with the bare template (no embedded CSS)
redquilt --theme none input.md
# Write HTML to a file instead of stdout
redquilt -o output.html input.md
# Render and open the result in the default browser
redquilt --open input.md
# Render mermaid code blocks as diagrams (loads mermaid.js from a CDN)
redquilt --mermaid --open input.md
Options
--format FORMAT Output format: html (default), ast, json
--allow-html Pass raw HTML through to the output
--disallow-raw-html With --allow-html, filter GFM's dangerous tags
--extended-autolinks Linkify bare URLs and email addresses (GFM)
--footnotes Enable GFM footnotes
--lint Collect lint diagnostics
--[no-]standalone Wrap HTML in full document (default: on)
--auto-title Use the first heading's text as <title>
--title TITLE Explicit <title> text
--lang LANG html lang attribute (default: "en")
--css URL Add a stylesheet link
--theme THEME Embedded stylesheet: default (default) or none
-o, --output FILE Write HTML to FILE instead of stdout
--open Write HTML to a file and open it in the default
browser (forces --standalone; uses a file under
Dir.tmpdir when -o is not given)
--mermaid Render `mermaid` code blocks as diagrams (loads
mermaid.js from a CDN in standalone output)
--diagnostics Print diagnostics to stderr
--diagnostics-only Print diagnostics only (suppress output)
-h, --help Show help
-v, --version Show version
Exit code is 0 on success, 1 if errors are detected.
Safe-by-Default HTML Rendering
Security model
RedQuilt prioritizes security by default:
# Default: All HTML is escaped, dangerous URLs blocked
RedQuilt.render_html("<script>alert('xss')</script>")
# => "<p><script>alert('xss')</script></p>"
RedQuilt.render_html("[click](javascript:alert(1))")
# => "<p><a href=\"\">click</a></p>"
Allowed URL schemes
In link/image destinations, only these schemes are permitted:
- Absolute:
http://,https://,ftp://,tel:,ssh:// - Relative:
/path,#anchor,path/to/file - Special:
mailto:(autolinks only)
All other schemes (javascript:, data:, vbscript:, etc.) are blocked by replacing the URL with an empty string.
Autolinks (<scheme:...>) follow CommonMark and allow arbitrary schemes, so they use a denylist instead: only the script-executing schemes javascript:, vbscript:, and data: are blocked.
Opting into HTML pass-through
# Allow raw HTML (use with trusted input only)
RedQuilt.render_html(user_markdown, allow_html: true)
# This passes HTML blocks and inline tags through unchanged
Development
Running tests
bundle exec rake spec
Runs the full CommonMark 0.31.2 conformance suite (all 652 official examples,
parsed directly from spec/fixtures/cmark_spec-0.31.2.md) plus RedQuilt's own
feature tests — 1000 examples in total.
Benchmark
ruby spec/bench_inline.rb
ruby spec/bench_block.rb
Profiles parse performance on various Markdown patterns.
Performance (v0.6.1, Ruby 4.0.5)
Comparison against kramdown on arm64-darwin (Apple Silicon), measured with spec/bench_vs_kramdown.rb (benchmark-ips):
| Fixture | Size | RedQuilt (i/s) | kramdown (i/s) | RedQuilt vs kramdown |
|---|---|---|---|---|
| short_paragraph | 49 B | 26,531 | 5,416 | 4.90x faster |
| long_paragraph | 1.4 KB | 981 | 926 | within error |
| nested_emphasis | 1.4 KB | 999 | 689 | 1.45x faster |
| many_links | 2.0 KB | 1,131 | 794 | 1.43x faster |
| mixed_markup | 1.8 KB | 1,028 | 729 | 1.41x faster |
| deep_nesting | 800 B | 827 | 349 | 2.37x faster |
| cmark_spec | 205 KB | 39.1 | 30.2 | 1.30x faster |
Reproduce locally:
bundle exec ruby spec/bench_vs_kramdown.rb
License
MIT