Earmark to MDEx Migration
Mix.install([
{:earmark, "~> 1.4"},
{:mdex, "~> 0.13"},
{:mdex_gfm, "~> 0.1"}
])
Intro
For anyone looking into MDEx after Earmark was deprecated, here’s a short guide with examples to migrate from Earmark syntax and options to MDEx.
Markdown sample
markdown = """
# Hello
This is **bold**!
- [x] Ship it
```elixir
child = spawn(fn -> send(current, {self(), 1 + 2}) end)
```
<strong>trusted HTML</strong>
"""
"# Hello\n\nThis is **bold**!\n\n- [x] Ship it\n\n```elixir\nchild = spawn(fn -> send(current, {self(), 1 + 2}) end)\n```\n\n<strong>trusted HTML</strong>\n"
Safety
Earmark renders raw HTML by default but MDEx removes it by default for security reasons. To keep the comparison similar, MDEx will use render: [unsafe: true] on examples.
See the Safety guide for more info.
MDExNative
This guide covers MDEx only but anyone looking for a pure Markdown converter without all the extra features provided by MDEx you can look into mdex_native that expose the underlying NIFs used by MDEx.
HTML
Earmark
{:ok, html, _messages} = Earmark.as_html(markdown)
IO.puts(html)
<h1>
Hello</h1>
<p>
This is <strong>bold</strong>!</p>
<ul>
<li>
[x] Ship it </li>
</ul>
<pre><code class="elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)</code></pre>
<strong>trusted HTML</strong>
:ok
MDEx
{:ok, html} = MDEx.to_html(markdown, render: [unsafe: true])
IO.puts(html)
<h1>Hello</h1>
<p>This is <strong>bold</strong>!</p>
<ul>
<li>[x] Ship it</li>
</ul>
<pre><code class="language-elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)
</code></pre>
<p><strong>trusted HTML</strong></p>
:ok
GFM - GitHub Flavored Markdown
Earmark
Earmark.as_html!(markdown, gfm: true)
|> IO.puts()
<h1>
Hello</h1>
<p>
This is <strong>bold</strong>!</p>
<ul>
<li>
[x] Ship it </li>
</ul>
<pre><code class="elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)</code></pre>
<strong>trusted HTML</strong>
:ok
MDEx
Use the MDExGFM plugin or enable options individually.
MDEx.to_html!(markdown,
plugins: [MDExGFM],
render: [unsafe: true]
)
|> IO.puts()
<h1>Hello</h1>
<p>This is <strong>bold</strong>!</p>
<ul>
<li><input type="checkbox" checked="" disabled="" /> Ship it</li>
</ul>
<pre lang="elixir"><code>child = spawn(fn -> send(current, {self(), 1 + 2}) end)
</code></pre>
<p><strong>trusted HTML</strong></p>
:ok
Options
A small sample of some differences on options.
Earmark
Earmark.as_html!(markdown, breaks: true)
|> IO.puts()
<h1>
Hello</h1>
<p>
This is <strong>bold</strong>!</p>
<ul>
<li>
[x] Ship it </li>
</ul>
<pre><code class="elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)</code></pre>
<strong>trusted HTML</strong>
:ok
Earmark.as_html!(markdown, smartypants: true)
|> IO.puts()
<h1>
Hello</h1>
<p>
This is <strong>bold</strong>!</p>
<ul>
<li>
[x] Ship it </li>
</ul>
<pre><code class="elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)</code></pre>
<strong>trusted HTML</strong>
:ok
MDEx
MDEx.to_html!(markdown,
render: [
hardbreaks: true,
unsafe: true
]
)
|> IO.puts()
<h1>Hello</h1>
<p>This is <strong>bold</strong>!</p>
<ul>
<li>[x] Ship it</li>
</ul>
<pre><code class="language-elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)
</code></pre>
<p><strong>trusted HTML</strong></p>
:ok
MDEx.to_html!(markdown,
parse: [smart: true],
render: [unsafe: true]
)
|> IO.puts()
<h1>Hello</h1>
<p>This is <strong>bold</strong>!</p>
<ul>
<li>[x] Ship it</li>
</ul>
<pre><code class="language-elixir">child = spawn(fn -> send(current, {self(), 1 + 2}) end)
</code></pre>
<p><strong>trusted HTML</strong></p>
:ok
AST
Earmark
Earmark.Parser.as_ast(markdown)
{:ok,
[
{"h1", [], ["Hello"], %{}},
{"p", [], ["This is ", {"strong", [], ["bold"], %{}}, "!"], %{}},
{"ul", [], [{"li", [], ["[x] Ship it"], %{}}], %{}},
{"pre", [],
[
{"code", [{"class", "elixir"}], ["child = spawn(fn -> send(current, {self(), 1 + 2}) end)"],
%{}}
], %{}},
{"strong", [], ["trusted HTML"], %{verbatim: true}}
], []}
MDEx
Application.put_env(:mdex, :inspect_format, :struct)
MDEx.parse_document(markdown, render: [unsafe: true])
{:ok,
%MDEx.Document{
nodes: [
%MDEx.Heading{
nodes: [%MDEx.Text{literal: "Hello", sourcepos: %MDEx.Sourcepos{start: {1, 3}, end: {1, 7}}}],
level: 1,
setext: false,
closed: false,
sourcepos: %MDEx.Sourcepos{start: {1, 1}, end: {1, 7}}
},
%MDEx.Paragraph{
nodes: [
%MDEx.Text{literal: "This is ", sourcepos: %MDEx.Sourcepos{start: {3, 1}, end: {3, 8}}},
%MDEx.Strong{
nodes: [
%MDEx.Text{literal: "bold", sourcepos: %MDEx.Sourcepos{start: {3, 11}, end: {3, 14}}}
],
sourcepos: %MDEx.Sourcepos{start: {3, 9}, end: {3, 16}}
},
%MDEx.Text{literal: "!", sourcepos: %MDEx.Sourcepos{start: {3, 17}, end: {3, 17}}}
],
sourcepos: %MDEx.Sourcepos{start: {3, 1}, end: {3, 17}}
},
%MDEx.List{
nodes: [
%MDEx.ListItem{
nodes: [
%MDEx.Paragraph{
nodes: [
%MDEx.Text{
literal: "[x] Ship it",
sourcepos: %MDEx.Sourcepos{start: {5, 3}, end: {5, 13}}
}
],
sourcepos: %MDEx.Sourcepos{start: {5, 3}, end: {5, 13}}
}
],
list_type: :bullet,
marker_offset: 0,
padding: 2,
start: 1,
delimiter: :period,
bullet_char: "-",
tight: false,
is_task_list: false,
sourcepos: %MDEx.Sourcepos{...}
}
],
...
},
...
],
...
}}