A Markdown parser for Swift Package Manager, using Github Flavored Markdown. As such it comes with a bunch of Markdown extensions such as fenced code blocks, tables, strikethrough, hard line breaks and auto links.
Additionally Parsley supports embedded metadata in Markdown documents, and it splits the document title out from the document body.
let input = """
---
author: Kevin
tags: Swift, Parsley
---
# Hello World
This is the body
"""
let document = try Parsley.parse(input)
print(document.title) // Hello World
print(document.body) // <p>This is the body</p>
print(document.metadata) // ["author": "Kevin", "tags": "Swift, Parsley"]Parsley is available via Swift Package Manager and runs on macOS and Linux.
.package(url: "https://github.com/loopwerk/Parsley", from: "0.5.0"),
Parsley can be used as a reader in the static site generator Saga, using SagaParsleyMarkdownReader.
Parsley supports adding attributes to Markdown elements using curly braces {...} with the following shorthand notations:
| Notation | HTML result |
|---|---|
.myclass |
class="myclass" |
#myid |
id="myid" |
key="value" |
key="value" |
Multiple classes are merged: {.foo .bar} becomes class="foo bar".
Attributes on code blocks are always enabled. For headings, images, and other block-level elements, you need to opt in with the .markdownAttributes option:
let html = try Parsley.html(input, options: [.markdownAttributes])
let document = try Parsley.parse(input, options: [.markdownAttributes])Attributes are placed after the language on the opening fence line:
```python {.highlight data-title="views.py"}
def hello():
print("Hello, World!")
```<pre class="highlight" data-title="views.py"><code class="language-python">def hello():
print("Hello, World!")
</code></pre>As a shorthand, a title can also be specified without curly braces:
```python title="views.py"
def hello():
print("Hello, World!")
```This is equivalent to {data-title="views.py"} and generates:
<pre data-title="views.py"><code class="language-python">def hello():
print("Hello, World!")
</code></pre>You can then use CSS to display the title, for example:
pre[data-title]::before {
content: attr(data-title);
display: block;
background: #1a1a1a;
padding: 0.5em 1em;
font-size: 0.85em;
border-bottom: 1px solid #333;
}Attributes are placed at the end of the heading line:
## My heading {.special #intro}<h2 class="special" id="intro">My heading</h2>For paragraphs, blockquotes, lists, horizontal rules, and tables, place the attributes on their own line directly after the element:
This is a paragraph.
{.note}
> A blockquote.
{.warning}
* First
* Second
{.checklist}
---
{.divider}<p class="note">This is a paragraph.</p>
<blockquote class="warning">
<p>A blockquote.</p>
</blockquote>
<ul class="checklist">
<li>First</li>
<li>Second</li>
</ul>
<hr class="divider" />When an image is the only content in a paragraph, attributes are applied directly to the <img> element:

{.hero}<p><img src="image.png" alt="Alt text" class="hero" /></p>Parsley doesn't come with a plugin system, it relies purely on cmark-gfm under the hood to render Markdown to HTML. If you want to modify the generated HTML, for example if you want to add target="blank" to all external links, SwiftSoup is a great way to achieve this.
If you want to add syntax highlighting to the code blocks, you could use a client-side JavaScript library such as Prism or highlight.js. Or for a server-side solution, check out Moon, which runs Prism in Swift:
import Parsley
import Moon
let html = try Parsley.html(markdown)
let highlighted = Moon.shared.highlightCodeBlocks(in: html)