はじめに
Markdownって便利ですよね。このブログもMarkdownで書いているんですが、これをWebサイトで表示するにはHTMLに変換する必要があります。
そこで登場するのがMarkedというライブラリ。正直なところ、MarkdownパーサーはいくつかあるんですがMarkedは「速さ」と「軽さ」を両立している点で、個人的にはかなり推しています。
2011年から開発が続いている老舗ライブラリで、GitHub上では34,000スター以上を獲得。週間ダウンロード数は900万を超えるという、まさにMarkdownパーサーの定番といえる存在なんですよね。
とはいえ、読むより触った方が早いと思います。記事の中にMarkdownを打ち込むとその場でHTMLに変換されるエディタを置いてあるので、先に挙動を見たい方はこちらからどうぞ。
Markedの特徴・メリット
1. とにかく高速
Markedは「low-level markdown compiler」として設計されています。キャッシングやブロッキングなしで解析を行うため、処理速度が非常に速い。大量のMarkdownファイルを扱う場面でも、パフォーマンスの心配をしなくて済むのは大きいですね。
2. 高い仕様準拠率
- Markdown 1.0: 100%対応
- CommonMark 0.31: 98%対応
- GitHub Flavored Markdown 0.29: 97%対応
GFM対応なので、テーブルやタスクリスト、取り消し線なんかも普通に使えます。GitHubのREADMEで書いたMarkdownがそのまま動くのは、地味にありがたい。
3. 環境を選ばない
ブラウザでもNode.jsでも、CLIでも動作します。フロントエンドでリアルタイムプレビューを作りたいときも、ビルド時に静的HTMLを生成したいときも、同じライブラリで対応できるのはメンテナンス的にも楽なんですよね。
4. 軽量で依存関係が少ない
余計な依存パッケージがほとんどないので、bundle sizeを気にするプロジェクトでも安心して導入できます。
インストール方法
npmでインストール
npm install marked
CLIとして使う場合
npm install -g marked
CDNから読み込む場合
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
Markedのサンプルを動かす
書き方の説明に入る前に、まずMarkedが実際に動くところを触ってみてください。
このサンプルは、テキストエリアに入力したMarkdownをmarked.parse()に渡してHTML文字列に変換し、
そのままinnerHTMLへ流し込んでプレビューするという、Markedの一番シンプルな使い方をそのまま体験できる形にしてあります。
左のテキストエリアに書いたMarkdownが、1文字打つたびにHTMLへ変換されて右側に表示されます。
見出しを###に変えてみる、- でリストを増やしてみる、テーブルの行を足してみる——
そのたびに右側の表示が即座に切り替わるのが分かるはずです。
上のチェックボックスをオンにすると、marked.parse()の第2引数に渡しているbreaksオプションが有効になり、
一番下の2行のように段落の途中で改行しただけの箇所が<br>に変わります。
オフのままだと、この2行はHTMLの仕様どおり1行につながって表示されます。
要点だけを静的なコードで抜き出すと、次のようになります。
import { marked } from 'marked'
const markdown = [
'# Markedの動作確認',
'',
'**太字**・*斜体*・[リンク](https://marked.js.org/)。',
'',
'- リスト1',
'- リスト2',
'',
'改行して書いた1行目',
'そのまま続けた2行目',
].join('\n')
// marked.parse() は同期で「文字列」を返す。breaks: true にすると
// 段落内の改行がそのまま <br> に変換される
const html = marked.parse(markdown, { breaks: true })
document.getElementById('out').innerHTML = html
実際にブラウザ上で動かせるサンプルはこちらです。テキストを書き換えると即座に反映されます。
注目してほしいのは、テーブルが何も設定せずに変換されている点です。Markedはgfmオプションが既定で有効なので、GitHubで書き慣れたテーブルや取り消し線がそのまま通ります。
そしてmarked.parse()の戻り値はただの文字列なので、awaitも.then()も要りません。innerHTMLに入れるだけでプレビューが完成します。
基本的な使い方
Node.jsでの使用
import { marked } from 'marked';
const markdown = `
# タイトル
これは**太字**で、これは*イタリック*です。
- リスト1
- リスト2
- リスト3
`;
const html = marked.parse(markdown);
console.log(html);
ブラウザでの使用
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
</head>
<body>
<div id="content"></div>
<script>
const markdown = '# Hello World\n\nMarkedで変換しました。';
document.getElementById('content').innerHTML = marked.parse(markdown);
</script>
</body>
</html>
CLIでの使用
# ファイルを変換
marked -i readme.md -o readme.html
# 標準入力から変換
echo "# Hello" | marked
実践的なユースケース
GFMオプションを有効にしてテーブルを使う
import { marked } from 'marked';
marked.setOptions({
gfm: true, // GitHub Flavored Markdown
breaks: true, // 改行をbrタグに変換
});
const tableMarkdown = `
| 機能 | Marked | 他のライブラリ |
|------|--------|---------------|
| 速度 | 高速 | 普通 |
| サイズ | 軽量 | 重い |
`;
console.log(marked.parse(tableMarkdown));
シンタックスハイライトとの連携
コードブロックにシンタックスハイライトを適用したい場合は、highlight.jsなどと組み合わせます。
import { marked } from 'marked';
import hljs from 'highlight.js';
marked.setOptions({
highlight: function(code, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return code;
}
});
カスタムレンダラーで出力をカスタマイズ
リンクを新しいタブで開くようにする例です。
import { marked } from 'marked';
const renderer = new marked.Renderer();
renderer.link = function(href, title, text) {
const titleAttr = title ? ` title="${title}"` : '';
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
};
marked.setOptions({ renderer });
セキュリティ対策
これ、意外と見落としがちなんですが、Markedは出力HTMLのサニタイズを自動では行いません。ユーザー入力を扱う場合は、必ずDOMPurifyなどでサニタイズしましょう。
import { marked } from 'marked';
import DOMPurify from 'dompurify';
const unsafeMarkdown = userInput; // ユーザーからの入力
const rawHtml = marked.parse(unsafeMarkdown);
const safeHtml = DOMPurify.sanitize(rawHtml);
document.getElementById('content').innerHTML = safeHtml;
XSS対策は30代エンジニアとして、もう当たり前のように意識しておきたいところですね。
まとめ
Markedは、MarkdownをHTMLに変換するライブラリとしては、正直一択と言っていいくらいの完成度だと思います。
- 高速で軽量
- 主要なMarkdown仕様にほぼ100%対応
- ブラウザ・Node.js・CLIどこでも動く
- カスタマイズ性も高い
ブログシステムやドキュメントサイトを作るとき、Markdownエディタのプレビュー機能を実装するとき、README.mdをWebページに表示したいときなど、使いどころは多いですね。
10年以上メンテナンスが続いている安定感も、本番環境で使うライブラリ選びでは重要なポイント。個人的には、Markdownを扱う案件では真っ先に検討するライブラリです。
公式サイト: https://marked.js.org/