为什么换Markdown引擎

由于Hexo默认的Marked引擎并不能正确解析GFM模式,比如换行的时候GFM是空一行才会分段,而Marked的实现则是一个换行即可。平时用GFM比较多,强迫症是不能接受的,于是开始了踩坑之旅。

Markdown-it

换上比较火的markdown-it引擎,这个引擎提供了三种模式,CommonMark,GFM和zero。其中CommonMark和GFM就不解释了,zero模式就是只支持非常少的几个标记。于是很自然的选了GFM。

好景不长,几分钟后我发现文章目录失效了。

于是我查到了这个issue,目录锚点失效

和我遇到的情况一样,JS解析锚点报错,排查到原因是Markdown在解析成HTML的时候根本就没有在标题上生成对应的id,导致目录锚点失效。

于是我去查了Markdown-it关于标题id的实现,发现在这个issue下一帮歪果仁讨论。似乎大家认为要有这个功能,但是markdown-it官方认为这个不该有。于是最后有个人说

FYI, I just published this plugin https://github.com/MoOx/markdown-it-toc-and-anchor

Hack it

于是我开始研究如何将这个插件放到hexo里。(记不清了,后来并没有用它,而是用了markdown-it-named-headings)

分析了一下node_modules发现其中依赖了’markdown-it’和’hexo-renderer-markdown-it’,再分析了一下’hexo-markdown-it’的源码,其中renderer.jsnode_modules/hexo-renderer-markdown-it/lib/renderer.js)如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module.exports = function (data, options) {
var MdIt = require('markdown-it');
var cfg = this.config.markdown;
var opt = (cfg) ? cfg : 'default';
var parser = (opt === 'default' || opt === 'CommonMark' || opt === 'zero') ?
new MdIt(opt) :
new MdIt(opt.render);

if (opt.plugins) {
parser = opt.plugins.reduce(function (parser, pugs) {
return parser.use(require(pugs));
}, parser);
}

if (opt.anchors) {
parser = parser.use(require('./anchors'), opt.anchors);
}

return parser.render(data.text);
};

根据markdown-it-named-headings的文档,我尝试在实例化parser之后引入插件

1
parser.use(require('markdown-it-named-headings'))

居然成功了…

我将这个问题反馈给’hexo-renderer-markdown-it’的开发者https://github.com/celsomiranda/hexo-renderer-markdown-it/issues/40

然而这个开发者好像有大半年都不上Github了,而我也懒得发布一个NPM模块,一旦npm update这些改动就失效了,特此记录,以免忘了。。。

后记

Markdown-it的实现中,GFM不支持直接使用HTML,CommonMark不支持表格和删除线等标记。我非常难受,这两点我都必须要用,于是我去看了markdown-it的源码,发现可以手动去改几个参数,让GFM支持HTML,这样就可以愉快的使用了。但为了培养自己GFM的好习惯,还是放弃了更改。

此事让我了解到,果然Markdown也是有各种实现,有点类似CSS样式,浏览器的支持程度都有差异,这对于开发者,使用者来说,简直就是逼死强迫症。这个故事告诉我们,规范是多么重要。希望IE早日合群- -

如有疏漏,欢迎评论指出,或者前往Github提出issue~谢谢