このウェブログではHTMLを直接ぺちぺち書くことが多いが、たまにiPhoneで書く時にはMarkdownで書いている。Markdownの処理系にはここ数年はmarkedを使っており(宗教上の理由でCommonMark系は使うことができない)、あまり困ってはいないのだが、section
やfigure
要素を使うとその中のMarkdown文字列がうまく処理できないという問題がある。
例えばfigure
要素を使ってtable
要素を図としてマークアップしたいとする。その場合、markedでは表の記法が拡張されているので、以下のように書くことになる。
<figure>
|Name|Num|
|-|-|
|foo|128|
|bar|256|
<figcaption>表1</figcaption>
</figure>
このMarkdown文字列をmarkedで処理しても何も起きず、そのまま出てくる。つまり何らかのHTMLタグで括られた間はインライン系の記法しか処理されない。
markedには処理をフックする機構が用意されているので、それを使うとうまく処理することできる。こういったHTML文字列の場合、Renderer.html
でそういった文字列を処理する関数を指定してやれば良い。
// node this.js < test.md
var marked = require("marked");
var renderer = new marked.Renderer();
function processHTML(html) {
var attributes;
var contents;
var tag;
var tags = ["aside", "figure", "section"];
var tokens = html.trim().match(/^<(\w+)(.*?)>([\s\S]*)<\/\1>/);
if (!tokens) {
return html;
}
tag = tokens[1];
if (tags.indexOf(tag) === -1) {
return html;
}
attributes = tokens[2];
contents = marked(tokens[3].replace(/>/g, ">"), {
renderer: renderer
}).trim();
return "<" + tag + attributes + ">\n" + contents + "\n</" + tag + ">\n";
}
renderer.html = processHTML;
console.log(marked(fs.readFileSync(process.stdin.fd, "utf8"), {
renderer: renderer
}));
空白文字を削った一番外側のHTMLタグを拾い、その内容を更にmarkedで処理するようにしているわけだ。インライン記法は処理できるので、markedがサポートしていないfigure
やsection
、aside
要素のみを処理するようにしている。>
の文字参照だけを解除しているのは、引用記法がうまく処理できなくなるからで、そもそもの正規表現も含め、この辺りはもうちょっとちゃんとした方が良さそうだ。
この関数を利用すると、最初に挙げたMarkdown文字列の例は以下のように期待通りに処理される(インデントは読みやすいように追加した)。
<figure>
<table>
<thead>
<tr>
<th>Name</th>
<th>Num</th>
</tr>
</thead>
<tbody>
<tr>
<td>foo</td>
<td>128</td>
</tr>
<tr>
<td>bar</td>
<td>256</td>
</tr>
</tbody>
</table>
<figcaption>表1</figcaption>
</figure>
本当はこういったアドホックなプラグイン志向でMarkdownを拡張するのは好きではない。Markdownは拡張性なしの安定志向のもの(それこそオリジナルで良い)を使い、プリプロセッサーでtable
やdl
要素の独自記法を処理して渡す方が良いと考えている。そうしてプリプロセッサーで処理済みのものをデータとして保存しておけば、ほぼあらゆるMarkdown処理系で正常に処理できることが期待できるからだ。