What is it
markdown-it-py is configurable, extensible, and — most important for me today — not too hard to get started with.
How do I install it
markdown-it-py alone gets you “enough”. Everything you need for CommonMark, at least. But I want more than enough. I want all the features I can reasonaly gather under one install.
What did that just install?
- provides core markdown handling sufficient for common expected behavior
- enables recognition of URLs embedded in text strings; needs additional plugins to do anything with those URLs
- provides a collection of core plugins that make
markdown-it-pyuseful to a feature-happy person such as myself
How do I use it
But I need to be just a little fancier than “Hello World.” Let’s build a little Typer application that takes a markdown path and makes an HTML fragment. While I’m at it, I can borrow from my neovim rplugin to fit everything into my Hugo site.
For the moment I’ll rely on Python to let me know if I try rendering a Markdown file that doesn’t exist.
This will turn a single
.md.txt file - the extension I’m using to slide past
Hugo’s default Markdown handling - into HTML. I even get a nice
Now I start adding capabilities. If you see this post on the site, you’ll know it worked.
Picking a preset for common patterns
markdown-it-py provides predefined parser presets, allowing you to choose
between common parsing patterns.
- the default; sticks with the core CommonMark specification; probably good enough for 80% of the Markdown that gets written
- similar to Github-flavored Markdown; better if you need tables and URL transformation
- similar to markdown-it base behavior; adds typographical replacements like
“smart quotes” to the
- basically just breaks text into paragraphs; provides a bare minimum for you to build a highly custom Markdown parser
I went with
js_default for my own baseline because it enables the most core
Using options to tune your parser
Even with presets available, there are common tweaks that some folks can’t live
with and others can’t live without.
markdown-it-py wraps those up in a single
dictionary of options.
- recursion protection; think of it as a number for “how fancy can I get with my Markdown?”
- allow raw HTML through
- transform URLs into links
- processes assorted typographic conventions including proper quote marks
- what double and single quotes look like if you enable
- ensure output is valid in the ancient XHTML dialect
- treat line breaks in source as
- CSS class prefix for code blocks;
- a function to provide syntax highlighting for code blocks
Presets have default values for each of these options.
I like fancy quotes. I expect URLs to display as links. I occasionally need to fall back to raw HTML.But most importantly on this here blog: I insist on syntax highlighting.
Adding a highlight function
Rather than decide for themselves how syntax highlighting is done, the markdown-it-py folks added a single option for us to hook in a function using our preferred approach.
The highlight function should take three arguments:
- the string of code to highlight
- the lexer name
- a dictionary of any additional attributes
You could use whatever highlighting code you want. You could even have your function call out to an external program. I use Pygments because it’s familiar.
Also, I’m going to ignore
attrs for now. I rarely add special options to my
code samples, so it’s kind of wasted on me. So far.
Better make a note or something in case I forget that I’m ignoring it.
make_html look like now, with options set and highlighting
Still a few pieces missing from my minimal toolkit. I need to dig a little deeper than I planned for a “hey friends, markdown-it-py looks like fun” post. But I at least want to render the kind of posts I would write.
For that I need to use some plugins. Good thing I installed mdit-py-plugins.
Adding parser functionality with plugins
mdit-py-plugins bundles many plugins into a single library. Today I need no plugins beyond what that library provides.
Now I can write a definition list:
And markdown-it-py produces a proper description list:
So about a week ago, I was writing the first version of this post. I was nearly done. Then I got a little too tired and deleted the wrong file — without adding it to the repo first!
Anyways, this redraft is less of a tutorial and more of a notes dump. I want
to warn folks about that with a little blurb at the top. I can use the
containers plugin for that. The plugin provides slots for validation and
deeper processing. All I want today is a
<div> with custom class. I can use
CSS for the rest.
Using the container plugin with a
name option provides that much.
note container looks like this in the markdown:
Without any additional configuration, it produces this HTML:
Stopping here because it’s good enough for what I wrote so far today. But there is plenty more to explore. markdown-it-py allows reviewing and manipulating parsed tokens directly. Plus there’s the whole MyST Markdown thing to explore.
But for now we’re good. Let me drop in the Python code that transformed this post in the context of my Hugo site, then go convert the Typer logic to Invoke.