Unified.js is going to be around for a long time
Context
I stumbled upon this project some 4 years ago. I believe because Storybook, then Gatsby were already using it.
What is it?
From their beautiful site:
We compile content to syntax trees and syntax trees to content.
An example of this is reading a Markdown file into a JS variable that you can modify and then output as HTML.
We also provide hundreds of packages to work on the trees in between.
Yes, hundreds of little transformations, such as add target
and rel
attributes to all external links, or more complex ones, such as generate a table of contents..
You can use them analyse the content, for instance for linting purposes. Strip content, re-arrange, re-format it.
You can build on the unified collective to make all kinds of interesting things.
How do you use it?
You create a pipeline, by composing a series of simple functions, and you can then you can use it to parse, transform, and render as many inputs as you want.
The markdownToHtml()
function, accepts a markdown string and renders it to HTML.
import html from 'rehype-stringify';
import markdown from 'remark-parse';
import remark2rehype from 'remark-rehype';
import unified from 'unified';
cosnt processor = unified()
.use(markdown)
.use(remark2rehype)
.use(html);
async function markdownToHtml(input) {
return process.processString(input).toString();
}
Notice that the async
function. The pipelines are asynchronous and always return a promise.
An example using community plugins
Since this website is built with Astro, and because Astro uses the Unified.js ecosystem for all things Markdown and MDX, I added remark-emoji
to the configuration.
And the build now replaces :muscle:
by 💪!
Or even better, by adding remark-a11y-emoji
, it replaces :muscle:
by
<span role="img" aria-label="flexed biceps">💪</span>
To plug these two transformations to the above example, all you have to do is:
import a11yEmoji from '@fec/remark-a11y-emoji';
import emoji from 'remark-emoji';
cosnt processor = remark() // <-- you can also start with remark
.use(remarkEmoji) // <-- transform :beer: to emoji
.use(remarkA11yEmoji) // <-- transform to `<span...>`
.use(remark2rehype)
.use(html);
The kudos 💯
The clever functional design, the rigor in principles, and patterns. 🤩
You could tell, even 4 years ago, that this was going to be around for a while.
Not only that, the super professional management of the project(s) and community, has been allowing it to grow this fast and conquer such an important space in the industry.
I also have the feeling that this project, that claims to be ...
a collective
... has a certain human substance, strong ethical vibes, a certain ❤️ I am not really sure, but you know what I mean.
The unified collective is a federated system of organizations, consisting in turn of projects, governed by the team members steering them.
Titus
The biggest kudos to Titus who I believe is the origin of all this.
Their website now reads:
I work most-time on open source maintaining 550+ projects that are downloaded 22B+ times a year.
Are you mind blown 🤯, yet?
At the moment, Prettier, ESLint, MDX, and probably millions of websites and services out there - arguably ALL modern documentation websites - contain some Unified.js code.
How I use Unified.js
Before discovering Unified.js I used many libraries to parse (and generate) Markdown documents. I remember marked
but in an even older version of this website I found markdown-js (now 13 years old 😂 and unmaintained).
Since discovering the Unified.js ecosystem of highly pluggable micro libraries, I never used anything else. I reach for it all the time now, especially at work, usually to patch up deficiencies in tools such as Webpack or Rollup.js.
Fix builds, automate boring tasks, lint code, prevent errors, ...
Quite a 🔨 tool to carry in your tool belt.
Show me some code!
Sure! I used it extensively to build this website. So you'll find some examples of manipulation in this part of the repository.
I created the remark/
plugins to manipulate the markdown
tree:
autoAbstract()
extractsabstract
from the document and stores in a metadata object.autoImages()
replace image relative paths with absolute ones and.customComponents()
makes sure that instead of rendering<img>
tags we render a custom component.- and
autoImports()
injectsimport
statements in the MDX context so that I don't have to manually import commonly used components.
The rehype/
plugins manipulate the HTML
, also in abstract tree format, just before it is rendered. I only needed one - externalLinks()
- to add rel="noopener"
to all the external links.
If you wanna follow the narrative, check out the origin story or jump straight to my efforts to provide an advanced MDX authoring experience with Astro.