このブログではサブセクションを作る場合、直接<section>
タグを書き、その内容を再帰的にmarkedで処理してやっていた。あまり困ってはいないが、普通に書かれたMarkdownをうまく処理して、好みのHTMLコードで出力してやる作業を少しずつ重ねてきたので、ついでに自動セクショニングらしきものを実装した。
自動セクショニングは以下の条件で発動させたい:
hr
要素)が出現1ではsection
開始タグが、2ではsection
タグの終了と開始が、3と4ではsection
終了タグが、それぞれ追加される。見出しは常にh1
要素でマークアップする。またMarkdownの性質上、何かしらのテンプレートに流し込まれることが多く、そのテンプレートがセクションかセクショニング・ルートを持っている(body
やarticle
、またはblockquote
要素が直近に存在する)ので、トップ・レベルは例外としてセクショニングしない。
# Section 1
This is a level 1 section.
## Section 1-1
This is a level 2 section.
## Section 1-2
This is a level 2 section.
### Section 1-2-1
This is a level 3 section.
---
## Section 1-3
This is a level 2 section.
* * *
This is a level 1 section.
こういうMarkdownを以下のようなHTMLにしたい。
<h1>Section 1</h1>
<p>This is a level 1 section.</p>
<section>
<h1>Section 1-1</h1>
<p>This is a level 2 section.</p>
</section>
<section>
<h1>Section 1-2</h1>
<p>This is a level 2 section.</p>
<section>
<h1>Section 1-2-1</h1>
<p>This is a level 3 section.</p>
</section>
<hr>
</section>
<section>
<h1>Section 1-3</h1>
<p>This is a level 2 section.</p>
</section>
<hr>
<p>This is a level 1 section.</p>
実装は、グローバルで現在の見出しレベルを管理し、それを増減しつつ比較することで、出力すべきタグを決めていく。markedのブロック・レンダラーのうち、heading
とhr
を使う。
const marked = require("marked");
let currentLevel = 1;
const markupHeading = (text, level) => {
if (currentLevel === 1 && level === 1) {
return `<h1>${text}</h1>
`;
}
if (currentLevel > 1 && level === currentLevel) {
return `</section>
<section>
<h1>${text}</h1>
`;
}
if (currentLevel > 1 && level < currentLevel) {
currentLevel = level;
return `</section>
<h1>${text}</h1>
`;
}
currentLevel = level;
return `<section>
<h1>${text}</h1>
`;
};
const markupThematicBreak = () => {
if (currentLevel === 1) {
return `<hr>
`;
}
currentLevel = currentLevel - 1;
return `</section>
<hr>
`;
};
const renderer = new marked.Renderer();
renderer.heading = markupHeading;
renderer.hr = markupThematicBreak;
module.exports = t =>
marked(t, {
renderer: renderer
});
既に判明している問題は、サブセクションでテーマの区切りが使えないこと、引用やリスト内で見出しが使えないこと、の2つだ。両者ともに使う機会は少ないと思うので目をつぶっている。トップ・レベルでレベル1見出しを複数使えないという制限もあるが、Markdownの使われ方を考慮すると、この点は問題ないだろう。
副作用として生HTMLをmarkedに任せられるようになった。そのため野良Markdownを食わせても、HTMLをそのまま食わせても、うまく扱える。どこかに書いたMarkdown (らしきテキスト)をそのまま処理できるようになったので、相互運用性が高まった……かもしれない。