JavaScript - Markdown
Use unifed
and remark
/ rehype
plugins:
- remark: an ecosystem around markdown.
- to transform markdown into HTML.
- https://github.com/remarkjs/remark
- rehype: an ecosystem around HTML.
- to inspect and change the HTML, e.g. sanitize, minify.
- https://github.com/rehypejs/rehype
Example:
const markdown = await unified()
.use(remarkParse)
.use(remarkMath)
.use(remarkRehype)
.use(remarkGfm)
.use(rehypeKatex)
.use(rehypeFormat)
.use(rehypeStringify)
.use(rehypePrism)
.process(raw || ''); // raw is the raw markdown text.
// content is the generated HTML.
const content = markdown.toString();
// ...
// render the HTML.
<div dangerouslySetInnerHTML={{ __html: content }} />;
You can also use remark
which is a shorthand for unified
, remark-parse
and remark-stringify
.
import { remark } from 'remark';
const file = await remark().process(doc);
Some of the commonly used plugins:
remark-rehype
: turn markdown into HTML (converts Markdown AST mdast to HTML AST hast).- operations on mdast should be before remark-rehype; operations on hast should be after remark-rehype.
remark-gfm
: add support for GFM (GitHub flavored markdown)remark-toc
: generate a table of contentsremark-math
: support math ().remark-mdx
: support MDX syntax (JSX, export/import, expressions).remark-ruby
: new syntax for ruby characters{[紳][士]}^([へん][たい])
.rehype-parse
: parse HTML
Full list can be found in https://github.com/remarkjs/remark/blob/main/doc/plugins.md
remark-directive
:::
-containerDirective
, analogous todiv
s containing further blocks::
-leafDirective
, analogous to emptydiv
s:
-textDirective
, analogous tospan
s
Syntax:
# textDirective
:name[content]{key=val}
# leafDirective
:: name [content] {key=val}
# containerDirective
::: name [inline-content] {key=val}
contents, which are sometimes further block elements
:::
Example
To create a styled box:
// Create your own plugin
function StyledAlert() {
return function (tree) {
visit(tree, (node) => {
if (
node.type === 'containerDirective' ||
node.type === 'leafDirective' ||
node.type === 'textDirective'
) {
if (node.name !== 'alert') return;
const data = node.data || (node.data = {});
const tagName = node.type === 'textDirective' ? 'span' : 'div';
data.hName = tagName;
data.hProperties = h(tagName, node.attributes || {}).properties;
node.children.unshift(h('span', node.attributes.class.toUpperCase()));
}
});
};
}
// Use the plugin in unified:
const markdown = await unified()
.use(remarkParse)
.use(remarkDirective)
.use(StyledAlert)
//...
.process(page.content || '');
In markdown:
:::alert{.warning}
`remark-directive` must be before `remark-rehype`
:::
In CSS:
.warning {
background: rgb(255, 244, 229);
color: rgb(102, 60, 0);
padding: 16px;
border-radius: 4px;
margin-bottom: 20px;
}
This will be rendered like this:
WARNING
remark-directive
must be before remark-rehype