解析

markdown-it 提供了三种模式:commonmark、default、zero。分别对应最严格、GFM、最宽松的解析模式。

markdown-it 的解析规则可分为块(block)和内联(inline)两种。具体可体现为 MarkdownIt.block 对应的是解析块规则的 ParserBlock, MarkdownIt.inline 对应的是解析内联规则的 ParserInline。解析时,先创建一个 Core Parser,该 Core Parser 包含一系列的缺省 rules。这些 rules 按顺序执行,每个 rules 都在前面的 Tokens 的基础上,修改或添加新的 Token。这个 rules 的执行链称为 Core Chain。

经过解析后我们得到一个数组,markdown-it 称之为 token 流。

  • Tokens 是一个简单的数组。
  • 打开的标签和关闭的标签可以隔离。
  • 将“内联容器(inline container)”作为一种特殊的 block token 对象。它有嵌套的 tokens,如粗体,斜体,文本等等。

这样做的好处是可以并行处理 block 和 inline 类型的 token 。比如通过markdown-it我们可以把# change!转化为以下内容:

[
  {
    "type": "heading_open",
    "tag": "h1",
    "attrs": null,
    "map": [
      0,
      1
    ],
    "nesting": 1,
    "level": 0,
    "children": null,
    "content": "",
    "markup": "#",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  },
  {
    "type": "inline",
    "tag": "",
    "attrs": null,
    "map": [
      0,
      1
    ],
    "nesting": 0,
    "level": 1,
    "children": [
      {
        "type": "text",
        "tag": "",
        "attrs": null,
        "map": null,
        "nesting": 0,
        "level": 0,
        "children": null,
        "content": "change!",
        "markup": "",
        "info": "",
        "meta": null,
        "block": false,
        "hidden": false
      }
    ],
    "content": "change!",
    "markup": "",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  },
  {
    "type": "heading_close",
    "tag": "h1",
    "attrs": null,
    "map": null,
    "nesting": -1,
    "level": 0,
    "children": null,
    "content": "",
    "markup": "#",
    "info": "",
    "meta": null,
    "block": true,
    "hidden": false
  }
]

渲染器

渲染器会遍历所有 token,将每个 token 传递给与 token 的 type 属性同名的 rule。markdown-it 内置了九种规则:

  • 围栏
  • 行内代码
  • 代码块
  • HTML 块
  • 行内 HTML
  • 图片
  • 硬换行
  • 软换行
  • 文本。

type 属性不在内置规则时,token 将会被被传入 renderToken 中当做普通的 token 处理。

MarkdownIt.renderer.renderMarkdownIt.renderer.renderInline 分别对应按照块规则和内联规则生成 HTML 代码。而在 MarkdownIt.renderer 中有一个特殊的属性:rules,它代表着对于 token 们的渲染规则,可以被使用者更新或扩展:

var md = require('markdown-it')();
md.renderer.rules.strong_open  = function () { return '<b>'; };
md.renderer.rules.strong_close = function () { return '</b>'; };
var result = md.renderInline(...);

比如这段代码就更新了渲染 strong_open 和 strong_close 这两种 token 的渲染规则。

插件系统

markdown-it 只做纯粹的 Markdown 解析,我们可以通过插件来实现扩展其功能。MarkdownIt.use 可以将指定的插件加载到当前的解析器实例中:

var md = require('markdown-it')()
            .use(require('markdown-it-container'), name [, options]);
下一节:markdown-it的文档和语法确实有点难理解, 我也只琢磨出了我想要的插件的写法, 所以 这篇文章目前应该叫: 为markdown-it编写一个渲染自定义块语法的插件.