diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,34 @@
+## MMark 0.1.0.0
+
+* The package now requires `mmark-0.1` and `mmark-ext-0.3` or later.
+
+* `--ext-footnotes` now checks the footnotes of a document: a reference that
+  leads nowhere, a footnote that nothing refers to, a footnote that is
+  defined twice, and more than one footnote section are all reported as
+  errors.
+
+* `--ext-toc` reports a document that asks for a table of contents but has
+  no headings to put in one. The table of contents is also inserted before
+  the other extensions run now, so `--ext-punctuation` applies to it too.
+
+* Added the following extensions, all new in `mmark-ext-0.3`:
+
+    * `--ext-emoji`, which replaces `:shortcode:` with the emoji it names
+      and reports a name it does not recognize.
+    * `--ext-lazy-images`, which gives every image `loading="lazy"` and
+      `decoding="async"`.
+    * `--ext-line-highlight`, which points at the lines of a code block
+      named by its info string, as in ```` ```haskell {2,4-6} ````. Both
+      syntax highlighters read the same specification, so this renders the
+      blocks they do not take.
+    * `--ext-mermaid`, which renders a `mermaid` code block as `<pre
+      class="mermaid">` for the mermaid script to pick up in the browser.
+    * `--ext-permalinks`, which appends to every heading a link to its own
+      id.
+
+* Removed `--ext-font-awesome` and `--ext-obfuscate-email`, because the
+  extensions behind them are gone from `mmark-ext-0.3`.
+
 ## MMark CLI 0.0.5.2
 
 * Maintenance release with more minimal dependencies.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -4,17 +4,20 @@
 [![Hackage](https://img.shields.io/hackage/v/mmark-cli.svg?style=flat)](https://hackage.haskell.org/package/mmark-cli)
 [![Stackage Nightly](http://stackage.org/package/mmark-cli/badge/nightly)](http://stackage.org/nightly/package/mmark-cli)
 [![Stackage LTS](http://stackage.org/package/mmark-cli/badge/lts)](http://stackage.org/lts/package/mmark-cli)
-![CI](https://github.com/mmark-md/mmark-cli/workflows/CI/badge.svg?branch=master)
+[![CI](https://github.com/mmark-md/mmark-cli/actions/workflows/ci.yaml/badge.svg)](https://github.com/mmark-md/mmark-cli/actions/workflows/ci.yaml)
 
 * [Templates](#templates)
 * [Extensions](#extensions)
   * [Comment paragraph](#comment-paragraph)
-  * [Font Awesome](#font-awesome)
+  * [Emoji](#emoji)
   * [Footnotes](#footnotes)
   * [Kbd tags](#kbd-tags)
+  * [Lazy images](#lazy-images)
+  * [Line highlighting](#line-highlighting)
   * [Link targets](#link-targets)
   * [MathJax](#mathjax)
-  * [Email obfuscation](#email-obfuscation)
+  * [Mermaid](#mermaid)
+  * [Permalinks](#permalinks)
   * [Punctuation prettifier](#punctuation-prettifier)
   * [GHC syntax highlighter](#ghc-syntax-highlighter)
   * [Skylighting](#skylighting)
@@ -26,15 +29,17 @@
 markdown processor.
 
 ```
-mmark—command line interface to MMark markdown processor
+mmark—command line interface to the MMark markdown processor
 
 Usage: mmark [-v|--version] [-i|--ifile IFILE] [-o|--ofile OFILE] [-j|--json]
-             [-t|--template FILE] [--ext-comment PREFIX] [--ext-font-awesome]
-             [--ext-footnotes] [--ext-kbd] [--ext-link-target] [--ext-mathjax]
-             [--ext-obfuscate-email CLASS] [--ext-punctuation]
-             [--ext-skylighting] [--ext-toc RANGE]
-  Command line interface to MMark markdown processor
+             [-t|--template FILE] [--ext-comment PREFIX] [--ext-emoji]
+             [--ext-footnotes] [--ext-kbd] [--ext-lazy-images]
+             [--ext-line-highlight] [--ext-link-target] [--ext-mathjax]
+             [--ext-mermaid] [--ext-permalinks] [--ext-punctuation]
+             [--ext-ghc-highlighter] [--ext-skylighting] [--ext-toc RANGE]
 
+  Command line interface to the MMark markdown processor
+
 Available options:
   -h,--help                Show this help text
   -v,--version             Print version of the program
@@ -45,15 +50,18 @@
   -j,--json                Output parse errors and result in JSON format
   -t,--template FILE       Use the template located at this path
   --ext-comment PREFIX     Remove paragraphs that start with the given prefix
-  --ext-font-awesome       Enable support for inserting font awesome icons
+  --ext-emoji              Replace :shortcode: with the emoji it names
   --ext-footnotes          Enable support for footnotes
   --ext-kbd                Enable support for wrapping things in kbd tags
+  --ext-lazy-images        Let the browser decide when to fetch each image
+  --ext-line-highlight     Point at the lines of a code block named by its info
+                           string, e.g. "haskell {2,4-6}"
   --ext-link-target        Enable support for specifying link targets
   --ext-mathjax            Enable support for MathJax formulas
-  --ext-obfuscate-email CLASS
-                           Obfuscate email addresses assigning the specified
-                           class
+  --ext-mermaid            Render mermaid code blocks as diagrams in the browser
+  --ext-permalinks         Append a link to its own id to every heading
   --ext-punctuation        Enable punctuation prettifier
+  --ext-ghc-highlighter    Enable GHC syntax highlighter for Haskell code
   --ext-skylighting        Enable syntax highlighting of code snippets with
                            Skylighting
   --ext-toc RANGE          Enable generation of table of contents using the
@@ -61,22 +69,27 @@
                            "2-4"
 ```
 
+An extension may find something in the document that it cannot make sense
+of, such as a footnote reference that leads nowhere. When that happens it is
+reported the same way a parse error is, against the source of the document,
+and nothing is written to the output.
+
 ## Templates
 
 By using the `--template` argument, it's possible to create a standalone
 HTML page. The templating system we use is
 [Mustache](https://mustache.github.io/mustache.5.html), as implemented by
 the [stache](https://hackage.haskell.org/package/stache) library. The
-library conforms to the version 1.1.3 of the official [Mustache
+library conforms to version 1.1.3 of the official [Mustache
 specification](https://github.com/mustache/spec), but does not implement
-lambdas (which is an optional feature is the specification) for simplify and
-other technical reasons we won't touch here.
+lambdas (which is an optional feature of the specification) for simplicity
+and other technical reasons we won't touch here.
 
-If markdown source file has a YAML section, its contents will be provided as
-context for rendering of the template. In addition to that, a new top-level
-value bound to the variable named `output` will be available. That variable
-contains the HTML rendition of markdown document. It's best to interpolate
-it without HTML escaping, like so: `{{& output }}`.
+If the markdown source file has a YAML section, its contents will be
+provided as context for rendering of the template. In addition to that, a
+new top-level value bound to the variable named `output` will be available.
+That variable contains the HTML rendition of the markdown document. It's
+best to interpolate it without HTML escaping, like so: `{{& output }}`.
 
 ## Extensions
 
@@ -102,28 +115,35 @@
 <p>Third.</p>
 ```
 
-### Font awesome
+### Emoji
 
-* Option: `--ext-font-awesome`
+* Option: `--ext-emoji`
 
-This allows us to turn autolinks with `fa` scheme into font awesome icons:
+Replace every `:shortcode:` with the emoji it names:
 
 ```
-$ mmark --ext-font-awesome
-Here is the user icon: <fa:user>.
-
-A more interesting example: <fa:quote-left/3x/pull-left/border>.
+$ mmark --ext-emoji
+Ship it :rocket: and :tada:.
 ----------------------- Control-D
-<p>Here is the user icon: <span class="fa fa-user"></span>.</p>
-<p>A more interesting example:
-  <span class="fa fa-quote-left fa-3x fa-pull-left fa-border"></span>.
-</p>
+<p>Ship it 🚀 and 🎉.</p>
 ```
 
-In general, all path components in URIs that go after the name of icon will
-be prefixed with `"fa-"` and added as classes, so you can do a lot of fancy
-stuff, see http://fontawesome.io/examples/.
+A shortcode is a run of letters, digits, `_`, `+`, and `-` between colons. A
+name that is not one of the recognized ones is reported as an error rather
+than left alone, on the grounds that it is far more likely to be a typo than
+something you meant to keep:
 
+```
+$ mmark --ext-emoji
+Ship it :nosuchthing:.
+----------------------- Control-D
+<stdin>:1:1:
+  |
+1 | Ship it :nosuchthing:.
+  | ^
+there is no emoji called "nosuchthing"
+```
+
 ### Footnotes
 
 * Option: `--ext-footnotes`
@@ -140,8 +160,8 @@
 Here goes some text [1](footnote:1).
 
 > footnotes
-
-  1. Here we have the footnote.
+>
+> 1. Here we have the footnote.
 ----------------------- Control-D
 <p>Here goes some text <a href="#fn1" id="fnref1"><sup>1</sup></a>.</p>
 <ol>
@@ -151,11 +171,21 @@
 </ol>
 ```
 
-The extension is not fully safe though in the sense that we can't check that
-a footnote reference refers to an existing footnote and that footnotes have
-corresponding references, or that they are present in the document in the
-right order.
+The footnotes of a document are also checked, so a reference that leads
+nowhere, a footnote that nothing refers to, a footnote that is defined
+twice, and more than one footnote section are all reported as errors:
 
+```
+$ mmark --ext-footnotes
+Here goes some text [1](footnote:1).
+----------------------- Control-D
+<stdin>:1:21:
+  |
+1 | Here goes some text [1](footnote:1).
+  |                     ^
+there is no footnote 1
+```
+
 ### Kbd tags
 
 * Option: `--ext-kbd`
@@ -179,20 +209,78 @@
 To enable that mode press [Ctrl+A](kbd:).
 ```
 
+### Lazy images
+
+* Option: `--ext-lazy-images`
+
+Give every image `loading="lazy"` and `decoding="async"`, so that an image
+far down the page does not hold up the ones the reader can already see:
+
+```
+$ mmark --ext-lazy-images
+![a cat](cat.png)
+----------------------- Control-D
+<p><img loading="lazy" decoding="async" alt="a cat" src="cat.png"></p>
+```
+
+### Line highlighting
+
+* Option: `--ext-line-highlight`
+
+Point at the lines of a code block that the prose is about by naming them
+after the language in the info string. The lines are given the class
+`"highlighted-line"`, and the language, if there is one, still becomes the
+`language-` class of the `<code>` element:
+
+````
+$ mmark --ext-line-highlight
+```elixir {2}
+alpha
+beta
+gamma
+```
+----------------------- Control-D
+<pre><code class="language-elixir">alpha
+<span class="highlighted-line">beta
+</span>gamma
+</code></pre>
+````
+
+The specification is a comma-separated list of line numbers and ranges, so
+`{2,4-6}` points at lines 2, 4, 5, and 6.
+
+Both syntax highlighters read the same specification and point at the lines
+themselves, around the tokens they have coloured. This extension is what
+renders the blocks they do not take, so combining it with one of them
+highlights the languages that highlighter knows and still points at the
+lines in every other block:
+
+````
+$ mmark --ext-skylighting --ext-line-highlight
+```haskell {2}
+main :: IO ()
+main = return ()
+```
+----------------------- Control-D
+<div class="source-code"><pre><code class="language-haskell"><span class="ot">main ::</span><span> </span><span class="dt">IO</span><span> ()</span>
+<span class="highlighted-line"><span>main </span><span class="ot">=</span><span> </span><span class="fu">return</span><span> ()</span>
+</span></code></pre></div>
+````
+
 ### Link targets
 
 * Option: `--ext-link-target`
 
-When title of a link starts with the word `"_blank"`, `"_self"`,
-`"_parent"`, or `"_top"`, it's stripped from title (as well as all
-whitespace after it) and added as the value of `target` attribute of the
+When the title of a link starts with the word `"_blank"`, `"_self"`,
+`"_parent"`, or `"_top"`, it's stripped from the title (as well as all
+whitespace after it) and added as the value of the `target` attribute of the
 resulting link. For example:
 
 ```
-$ mmark --ext-kbd
+$ mmark --ext-link-target
 This [link](/url '_blank My title') opens in new tab.
 ----------------------- Control-D
-<p>This <a href="/url" title="My title" target="_blank">link</a>
+<p>This <a target="_blank" rel="noopener noreferrer" href="/url" title="My title">link</a>
 opens in new tab.</p>
 ```
 
@@ -226,39 +314,53 @@
 </p>
 ````
 
-### Email obfuscation
+### Mermaid
 
-* Option: `--obfuscate-email CLASS`
+* Option: `--ext-mermaid`
 
-This extension makes email addresses in autolinks be rendered as something
-like this:
+Render a `mermaid` code block as `<pre class="mermaid">`, which is what the
+[mermaid](https://mermaid.js.org/) script in the page looks for when it
+turns the diagram into an SVG in the browser:
 
+````
+$ mmark --ext-mermaid
+A diagram:
+
+```mermaid
+graph TD;
+  A-->B;
 ```
-[mark@arch ~]$ mmark --ext-obfuscate-email protected-email
-Send all your spam to <someone@example.org>, if you can!
 ----------------------- Control-D
-<p>Send all your spam to
-  <a href="javascript:void(0)"
-     class="protected-email"
-     data-email="someone@example.org">
-  Enable JavaScript to see this email</a>, if you can!
-</p>
-```
+<p>A diagram:</p>
+<pre class="mermaid">graph TD;
+  A--&gt;B;
+</pre>
+````
 
-You'll also need to include jQuery and this bit of JS code for the magic to
-work:
+You will need to include the mermaid script for anything to happen; this
+extension only marks the block for it. Rendering diagrams ahead of time
+instead requires the mermaid command line tool, which this program does not
+call — use the `mermaidScanner` and `mermaidSvg` functions of `mmark-ext`
+for that.
 
-```java-script
-$(document).ready(function () {
-    $(".protected-email").each(function () {
-        var item = $(this);
-        var email = item.data('email');
-        item.attr('href', 'mailto:' + email);
-        item.html(email);
-    });
-});
+### Permalinks
+
+* Option: `--ext-permalinks`
+
+Append to every heading a link to the id MMark gives that heading. The link
+is labelled `"#"` and given the class `"permalink"`, so that a style sheet
+can show it only when the heading is hovered:
+
 ```
+$ mmark --ext-permalinks
+# Story of my life
+----------------------- Control-D
+<h1 id="story-of-my-life">Story of my life<a href="#story-of-my-life" class="permalink" aria-hidden="true" tabindex="-1">#</a></h1>
+```
 
+The link says nothing a screen reader can use, so it is hidden from one and
+taken out of the order the keyboard walks.
+
 ### Punctuation prettifier
 
 * Option: `--ext-punctuation`
@@ -308,8 +410,7 @@
 ```
 ----------------------- Control-D
 <p>Some Haskell:</p>
-<div class="source-code"><pre><code class="language-haskell">
-<span class="va">main</span><span> </span><span class="sy">::</span><span> </span><span class="cr">IO</span><span> </span><span class="sy">(</span><span class="sy">)</span><span>
+<div class="source-code"><pre><code class="language-haskell"><span class="va">main</span><span> </span><span class="sy">::</span><span> </span><span class="cr">IO</span><span> </span><span class="sy">(</span><span class="sy">)</span><span>
 </span><span class="va">main</span><span> </span><span class="sy">=</span><span> </span><span class="va">return</span><span> </span><span class="sy">(</span><span class="sy">)</span><span>
 </span></code></pre></div>
 ````
@@ -337,9 +438,8 @@
 ```
 ----------------------- Control-D
 <p>Some Haskell:</p>
-<div class="source-code"><pre><code class="language-haskell">
-<span class="ot">main ::</span><span> </span><span class="dt">IO</span><span> ()</span>
-<span>main </span><span class="fu">=</span><span> return ()</span>
+<div class="source-code"><pre><code class="language-haskell"><span class="ot">main ::</span><span> </span><span class="dt">IO</span><span> ()</span>
+<span>main </span><span class="ot">=</span><span> </span><span class="fu">return</span><span> ()</span>
 </code></pre></div>
 ````
 
@@ -347,9 +447,15 @@
 
 * Option: `--ext-toc RANGE`
 
-Replace the code block with info string `"toc"` by table of contents
+Replace the code block with info string `"toc"` by a table of contents
 assembled from headings with levels from `N` to `M`, where `N-M` is `RANGE`.
+A document that asks for a table of contents but has no headings to put in
+one is reported as an error at the code block that asks.
 
+The table of contents is inserted before the other extensions run, so
+`--ext-punctuation` prettifies the headings in the table just as it does the
+headings in the document.
+
 For example:
 
 ````
@@ -359,7 +465,7 @@
 ```toc
 ```
 
-## Charpter 1
+## Chapter 1
 
 Foo.
 
@@ -374,7 +480,7 @@
 <h1 id="story-of-my-life">Story of my life</h1>
 <ul>
 <li>
-<a href="#charpter-1">Charpter 1</a>
+<a href="#chapter-1">Chapter 1</a>
 </li>
 <li>
 <a href="#chapter-2">Chapter 2</a>
@@ -385,7 +491,7 @@
 </ul>
 </li>
 </ul>
-<h2 id="charpter-1">Charpter 1</h2>
+<h2 id="chapter-1">Chapter 1</h2>
 <p>Foo.</p>
 <h2 id="chapter-2">Chapter 2</h2>
 <p>Bar.</p>
@@ -404,4 +510,4 @@
 
 Copyright © 2018–present Mark Karpov
 
-Distributed under BSD 3 clause license.
+Distributed under the BSD 3-clause license.
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -25,7 +25,6 @@
 import Paths_mmark_cli (version)
 import System.Directory (makeAbsolute)
 import System.Exit (exitFailure)
-import Text.MMark (MMarkErr)
 import Text.MMark qualified as MMark
 import Text.MMark.Extension.Common qualified as Ext
 import Text.Megaparsec (ParseErrorBundle (..), Parsec, SourcePos (..))
@@ -44,54 +43,64 @@
       Just file -> do
         absFile <- makeAbsolute file
         (absFile,) <$> T.readFile absFile
-  case MMark.parse mdFileName mdInput of
-    Left bundle -> do
-      if optJson
-        then (BL.putStrLn . Aeson.encode . parseErrorsJson) bundle
-        else putStr (M.errorBundlePretty bundle)
-      exitFailure
-    Right doc -> do
-      let exts =
-            mconcat
-              [ g optExtComment Ext.commentParagraph,
-                f optExtFontAwesome Ext.fontAwesome,
-                f optExtFootnotes Ext.footnotes,
-                f optExtKbd Ext.kbd,
-                f optExtLinkTarget Ext.linkTarget,
-                f optExtMathJax (Ext.mathJax (Just '$')),
-                g optExtObfuscateEmail Ext.obfuscateEmail,
-                f optExtPunctuationPrettifier Ext.punctuationPrettifier,
-                f optExtGhcSyntaxHighlighter Ext.ghcSyntaxHighlighter,
-                f optExtSkylighting Ext.skylighting,
-                g optExtToc $ \(from, to) ->
-                  Ext.toc "toc" . MMark.runScanner doc . Ext.tocScanner $ \x ->
-                    from <= x && x <= to
-              ]
-          f p x = if p then x else mempty
-          g p x = maybe mempty x p
-          applyTemplate tfile output = do
-            t <- U.compileMustacheFile tfile
-            return . TL.toStrict . U.renderMustache t $
-              case MMark.projectYaml doc of
-                Just (Object m) ->
-                  Object $
-                    Aeson.KeyMap.insert "output" (String output) m
-                _ ->
-                  Aeson.object
-                    [ "output" .= output
-                    ]
-      htmlOutput <-
-        maybe return applyTemplate optTemplate
-          . TL.toStrict
-          . L.renderText
-          . MMark.render
-          . MMark.useExtension exts
-          $ doc
-      if optJson
-        then
-          maybe BL.putStrLn BL.writeFile optOutputFile $
-            Aeson.encode (htmlDocJson htmlOutput)
-        else maybe T.putStr T.writeFile optOutputFile htmlOutput
+  doc0 <- orDie optJson (MMark.parse mdFileName mdInput)
+  let -- The order matters for the extensions that render code blocks: an
+      -- extension is given a block before the ones that follow it and
+      -- passes on what it does not recognize. Mermaid claims its own
+      -- blocks, the highlighters colour the languages they know, and the
+      -- line highlighter renders what is left over.
+      renderExts =
+        mconcat . concat $
+          [ [Ext.commentParagraph prefix | Just prefix <- [optExtComment]],
+            [Ext.footnotes | optExtFootnotes],
+            [Ext.kbd | optExtKbd],
+            [Ext.lazyImages | optExtLazyImages],
+            [Ext.linkTarget | optExtLinkTarget],
+            [Ext.mathJax (Just '$') | optExtMathJax],
+            [Ext.permalinks | optExtPermalinks],
+            [Ext.mermaid | optExtMermaid],
+            [Ext.ghcSyntaxHighlighter | optExtGhcSyntaxHighlighter],
+            [Ext.skylighting | optExtSkylighting],
+            [Ext.lineHighlight | optExtLineHighlight]
+          ]
+      -- The table of contents is inserted before the transformations that
+      -- rewrite text so that they apply to the table as well.
+      trans =
+        foldr (>=>) return . concat $
+          [ [Ext.toc "toc" (tocOf from to) | Just (from, to) <- [optExtToc]],
+            [Ext.emoji | optExtEmoji],
+            [Ext.punctuationPrettifier | optExtPunctuationPrettifier]
+          ]
+      tocOf from to =
+        MMark.runScanner (Ext.tocScanner (\x -> from <= x && x <= to)) doc0
+  when optExtFootnotes
+    $ orDie optJson
+    $ MMark.runCheck
+      (Ext.validateFootnotes (MMark.runScanner Ext.footnoteScanner doc0))
+      doc0
+  doc <- orDie optJson (MMark.runTrans trans doc0)
+  let applyTemplate tfile output = do
+        t <- U.compileMustacheFile tfile
+        return . TL.toStrict . U.renderMustache t $
+          case MMark.projectYaml doc of
+            Just (Object m) ->
+              Object $
+                Aeson.KeyMap.insert "output" (String output) m
+            _ ->
+              Aeson.object
+                [ "output" .= output
+                ]
+  htmlOutput <-
+    maybe return applyTemplate optTemplate
+      . TL.toStrict
+      . L.renderText
+      . MMark.render renderExts
+      $ doc
+  if optJson
+    then
+      maybe BL.putStrLn BL.writeFile optOutputFile $
+        Aeson.encode (htmlDocJson htmlOutput)
+    else maybe T.putStr T.writeFile optOutputFile htmlOutput
 
 ----------------------------------------------------------------------------
 -- Command line options parsing
@@ -108,18 +117,24 @@
     optTemplate :: !(Maybe FilePath),
     -- | Enable extension: 'Ext.commentParagraph'
     optExtComment :: !(Maybe Text),
-    -- | Enable extension: 'Ext.fontAwesome'
-    optExtFontAwesome :: !Bool,
+    -- | Enable extension: 'Ext.emoji'
+    optExtEmoji :: !Bool,
     -- | Enable extension: 'Ext.footnotes'
     optExtFootnotes :: !Bool,
     -- | Enable extension: 'Ext.kbd'
     optExtKbd :: !Bool,
+    -- | Enable extension: 'Ext.lazyImages'
+    optExtLazyImages :: !Bool,
+    -- | Enable extension: 'Ext.lineHighlight'
+    optExtLineHighlight :: !Bool,
     -- | Enable extension: 'Ext.linkTarget'
     optExtLinkTarget :: !Bool,
     -- | Enable extension: 'Ext.mathJax'
     optExtMathJax :: !Bool,
-    -- | Enable extension: 'Ext.obfuscateEmail'
-    optExtObfuscateEmail :: !(Maybe Text),
+    -- | Enable extension: 'Ext.mermaid'
+    optExtMermaid :: !Bool,
+    -- | Enable extension: 'Ext.permalinks'
+    optExtPermalinks :: !Bool,
     -- | Enable extension: 'Ext.punctuationPrettifier'
     optExtPunctuationPrettifier :: !Bool,
     -- | Enable extension: 'Ext.ghcSyntaxHighlighter'
@@ -190,8 +205,8 @@
         help "Remove paragraphs that start with the given prefix"
       ]
     <*> (switch . mconcat)
-      [ long "ext-font-awesome",
-        help "Enable support for inserting font awesome icons"
+      [ long "ext-emoji",
+        help "Replace :shortcode: with the emoji it names"
       ]
     <*> (switch . mconcat)
       [ long "ext-footnotes",
@@ -202,6 +217,17 @@
         help "Enable support for wrapping things in kbd tags"
       ]
     <*> (switch . mconcat)
+      [ long "ext-lazy-images",
+        help "Let the browser decide when to fetch each image"
+      ]
+    <*> (switch . mconcat)
+      [ long "ext-line-highlight",
+        help
+          ( "Point at the lines of a code block named by its info string, "
+              ++ "e.g. \"haskell {2,4-6}\""
+          )
+      ]
+    <*> (switch . mconcat)
       [ long "ext-link-target",
         help "Enable support for specifying link targets"
       ]
@@ -209,12 +235,15 @@
       [ long "ext-mathjax",
         help "Enable support for MathJax formulas"
       ]
-    <*> (optional . fmap T.pack . strOption . mconcat)
-      [ long "ext-obfuscate-email",
-        metavar "CLASS",
-        help "Obfuscate email addresses assigning the specified class"
+    <*> (switch . mconcat)
+      [ long "ext-mermaid",
+        help "Render mermaid code blocks as diagrams in the browser"
       ]
     <*> (switch . mconcat)
+      [ long "ext-permalinks",
+        help "Append a link to its own id to every heading"
+      ]
+    <*> (switch . mconcat)
       [ long "ext-punctuation",
         help "Enable punctuation prettifier"
       ]
@@ -238,16 +267,34 @@
 ----------------------------------------------------------------------------
 -- Helpers
 
--- | Represent the given collection of parse errors as a 'Value'.
-parseErrorsJson :: ParseErrorBundle Text MMarkErr -> Value
-parseErrorsJson ParseErrorBundle {..} =
+-- | Return the result, or print the errors that were produced instead of it
+-- and exit. Both the parser and the extensions report their errors as a
+-- 'ParseErrorBundle', so the two are presented in exactly the same way.
+orDie ::
+  (M.ShowErrorComponent e) =>
+  -- | Whether to print the errors in the JSON format
+  Bool ->
+  -- | The result to return, or the errors to print
+  Either (ParseErrorBundle Text e) a ->
+  IO a
+orDie json result =
+  case result of
+    Left bundle -> do
+      if json
+        then (BL.putStrLn . Aeson.encode . errorsJson) bundle
+        else putStr (M.errorBundlePretty bundle)
+      exitFailure
+    Right x -> return x
+
+-- | Represent the given collection of errors as a 'Value'.
+errorsJson :: (M.ShowErrorComponent e) => ParseErrorBundle Text e -> Value
+errorsJson ParseErrorBundle {..} =
   Aeson.toJSON
-    . fmap parseErrorObj
+    . fmap errorObj
     . fst
     $ M.attachSourcePos M.errorOffset bundleErrors bundlePosState
   where
-    parseErrorObj :: (M.ParseError Text MMarkErr, SourcePos) -> Value
-    parseErrorObj (err, SourcePos {..}) =
+    errorObj (err, SourcePos {..}) =
       Aeson.object
         [ "file" .= sourceName,
           "line" .= M.unPos sourceLine,
diff --git a/mmark-cli.cabal b/mmark-cli.cabal
--- a/mmark-cli.cabal
+++ b/mmark-cli.cabal
@@ -1,11 +1,11 @@
 cabal-version:   2.4
 name:            mmark-cli
-version:         0.0.5.2
+version:         0.1.0.0
 license:         BSD-3-Clause
 license-file:    LICENSE.md
 maintainer:      Mark Karpov <markkarpov92@gmail.com>
 author:          Mark Karpov <markkarpov92@gmail.com>
-tested-with:     ghc ==9.4.7 ghc ==9.6.3 ghc ==9.8.1
+tested-with:     ghc ==9.10.3 ghc ==9.12.4 ghc ==9.14.1
 homepage:        https://github.com/mmark-md/mmark-cli
 bug-reports:     https://github.com/mmark-md/mmark-cli/issues
 synopsis:        Command line interface to the MMark markdown processor
@@ -38,17 +38,17 @@
         directory >=1.2.2 && <1.4,
         gitrev >=1.3 && <1.4,
         lucid >=2.6 && <3,
-        megaparsec >=7 && <10,
-        mmark >=0.0.6 && <0.1,
-        mmark-ext >=0.2 && <0.3,
-        optparse-applicative >=0.14 && <0.19,
+        megaparsec >=8 && <10,
+        mmark >=0.1 && <0.2,
+        mmark-ext >=0.3 && <0.4,
+        optparse-applicative >=0.14 && <0.20,
         stache >=2 && <3,
         text >=0.2 && <2.2
 
     if flag(dev)
         ghc-options:
             -Wall -Werror -Wredundant-constraints -Wpartial-fields
-            -Wunused-packages
+            -Wunused-packages -haddock -Winvalid-haddock
 
     else
         ghc-options: -O2 -Wall
