JavaScript
Markdown
Marked

Marked を拡張してクラスを付与する

Marked とは

Marked は Markdown を HTML に変換してくれるモジュールです。

入力例
# Foo
Bar
- 111
- 222
実装例
import marked from 'marked'
import example from './example.md'

// 変換
marked(example)
出力例
<h1 id="foo">Foo</h1>
<p>Bar</p>
<ul>
<li>111</li>
<li>222</li>
</ul>

シンプルでいいですね。

id や class を付与したい

Markdown を HTML に変換する際、汎用的なスタイルを当てるならば問題はないでしょう。
しかしある程度凝ったことをしようとするならば、変換後の HTML に class あるいは id を付与したくなります。
Markdown は内部に HTML を書くことが許容されていますので、タグを直書きしてしまう方法が恐らく正攻法でしょう。
純粋な Markdown には class や id を指定する記法がないので、正攻法以外の方法で対応するのは難しいと思います。
(いい感じに付与できる方法を思いつく方は是非コメントなどでご意見いただけると嬉しいです。)

Marked の実装を見ると、Heading 要素については kebab-case に変換した本文の内容を id 属性に代入しているようです。
こちらは英数字しか対応していないようで、本文が日本語だったりすると正しく機能しません。

レンダラーを拡張する

Marked は変換部分を拡張できますので、今回は Heading 要素のレンダラーを書き換えて class や id を付与する方法を試してみます。

入力例
# Foo {{class="title"}}
Bar
- 111
- 222
実装例
import marked from 'marked'
import example from './example.md'

// レンダラーを取得
let renderer = new marked.Renderer()

// Heading 要素のレンダラーを拡張
renderer.heading = function (text, level, raw) {
  const regex = /\s*{{(.*)}}/
  const addon = raw.match(regex) ? raw.match(regex)[1] : ''
  return `<h${level} ${addon}>${text.replace(regex, '')}</h${level}>\n`
}

// レンダラーを指定して変換
marked(example, { renderer: renderer })
出力例
<h1 class="title">Foo</h1>
<p>Bar</p>
<ul>
<li>111</li>
<li>222</li>
</ul>

上記の例では Markdown に {{}} を書き足すことで class の追加を実現しています。
純粋な Markdown では無くなってしまうので若干 Evil なアプローチではありますが、Markdown に HTML を書きたくない場合や、変換後に HTML を捻じ曲げたりするよりは、このような手法も悪くないかなと思います。