Markdown-It 的解析過程

Markdown-It 可能是目前擴(kuò)展性和活躍度最好的 Markdown Parser 了。出于各種原因,我們需要掌握 Markdown-It 對(duì) Markdown 的解析過程。

對(duì)于開發(fā)者(Plug-Ins 或者貢獻(xiàn)者),它提供了文檔,不過這個(gè)文檔可是建立在你首先閱讀源程序的基礎(chǔ)上。我寫這篇文章算是一個(gè)前傳吧,這樣閱讀源程序也能輕松點(diǎn)。

當(dāng)你創(chuàng)建了一個(gè) md = require('markdown-it')() 對(duì)象之后,就可以用它來(lái)渲染 MD 文檔了,例如: md.render("# I'm H1 ")。這個(gè)渲染過程分為主要的兩步:

  1. 將 MD 文檔 Parsing 為 Tokens。
  2. 渲染這個(gè) Tokens。

Parsing

Parsing 的過程是,首先創(chuàng)建一個(gè) Core Parser,這個(gè) Core Parser 包含一系列的缺省 Rules。這些 Rules 將順序執(zhí)行,每個(gè) Rule 都在前面的 Tokens 的基礎(chǔ)上,要么修改原來(lái)的 Token,要么添加新的 Token。這個(gè) Rules 的鏈條被稱為 Core Chain。缺省 Core Rules 如下:

  • normalize: MD 文檔的換行符統(tǒng)一化;將空字符 \u0000 轉(zhuǎn)換為 \uFFFD
  • block: 識(shí)別出哪些是 Block Token(Table, blockquote, Code, Fence 等),哪些是 Inline Token。如果是 Block Token,則啟動(dòng) Block Chain 來(lái)處理。
  • inline: ?針對(duì) Block Rule 識(shí)別出來(lái)的 'inline' 類型的 token 進(jìn)行處理
  • linkify: 檢測(cè) text 類型的 token 中是否有可是別的 URL(http 或者 mailto),如果有,則將原本完整的 text token 分為 text, link, text 三部分(實(shí)際不只三個(gè) tokens, 因?yàn)?link_open, link_close 這些 tokens 都會(huì)被產(chǎn)生)
  • replacements: ?完成諸如 (c) (C) → ? ,+- → ±的替換,同時(shí)躲開 link 中的包含的對(duì)象文字
  • smartquotes: 完成引號(hào)的排印化處理

Block rule 的執(zhí)行過程,會(huì)啟動(dòng) Block Chain,這又是一堆 Rules 的執(zhí)行,缺省包含:

var _rules = [
  // First 2 params - rule name & source. Secondary array - list of rules,
  // which can be terminated by this one.
  [ 'table',      require('./rules_block/table'),      [ 'paragraph', 'reference' ] ],
  [ 'code',       require('./rules_block/code') ],
  [ 'fence',      require('./rules_block/fence'),      [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
  [ 'blockquote', require('./rules_block/blockquote'), [ 'paragraph', 'reference', 'list' ] ],
  [ 'hr',         require('./rules_block/hr'),         [ 'paragraph', 'reference', 'blockquote', 'list' ] ],
  [ 'list',       require('./rules_block/list'),       [ 'paragraph', 'reference', 'blockquote' ] ],
  [ 'reference',  require('./rules_block/reference') ],
  [ 'heading',    require('./rules_block/heading'),    [ 'paragraph', 'reference', 'blockquote' ] ],
  [ 'lheading',   require('./rules_block/lheading') ],
  [ 'html_block', require('./rules_block/html_block'), [ 'paragraph', 'reference', 'blockquote' ] ],
  [ 'paragraph',  require('./rules_block/paragraph') ]
];

在 Block Chain 執(zhí)行完了,就是 Inline Rule ?執(zhí)行,也就啟動(dòng)了 Inline Chain,包含:

var _rules = [
  [ 'text',            require('./rules_inline/text') ],
  [ 'newline',         require('./rules_inline/newline') ],
  [ 'escape',          require('./rules_inline/escape') ],
  [ 'backticks',       require('./rules_inline/backticks') ],
  [ 'strikethrough',   require('./rules_inline/strikethrough').tokenize ],
  [ 'emphasis',        require('./rules_inline/emphasis').tokenize ],
  [ 'link',            require('./rules_inline/link') ],
  [ 'image',           require('./rules_inline/image') ],
  [ 'autolink',        require('./rules_inline/autolink') ],
  [ 'html_inline',     require('./rules_inline/html_inline') ],
  [ 'entity',          require('./rules_inline/entity') ]
];

var _rules2 = [
  [ 'balance_pairs',   require('./rules_inline/balance_pairs') ],
  [ 'strikethrough',   require('./rules_inline/strikethrough').postProcess ],
  [ 'emphasis',        require('./rules_inline/emphasis').postProcess ],
  [ 'text_collapse',   require('./rules_inline/text_collapse') ]
];

所以這個(gè) Parsing 的過程 ?是一個(gè)簡(jiǎn)單的樹形過程,Core Rules 執(zhí)行到 Block 和 Inline ?Rule 的時(shí)候,會(huì)分別再執(zhí)行 Block Chain 和 Inline Chain。整個(gè) Parsing 過程是同步的。

Markdown-It 的 API 允許 Plugin 作者選擇在?這些缺省的 Rules 的前后添加新的 Rule 函數(shù),從而實(shí)現(xiàn)對(duì)特定的 Token 的再處理(增刪改 Token 都可以)。可以說, Plugin 作者的靈活性是足夠大的。

Rendering

在所有 Tokens 都獲得之后,就可以渲染了。渲染就是把特定 Token 轉(zhuǎn)變?yōu)樘囟ǖ?HTML 的過程。

?Markdown-It 允許你為特定的 Token Type 掛載自己的渲染函數(shù),這個(gè)函數(shù)稱為 Renderer Rule。?Markdown-It 已經(jīng)定義了幾個(gè) 缺省的 Renderer Rules:

code_inline
code_block
fence
image
hardbreak
text
html_block
html_inline

上面這些名字,其實(shí)是對(duì)應(yīng)的 token.type 的值。渲染時(shí),如果遇到匹配的 token.type,那么就會(huì)用對(duì)應(yīng)的 Renderer Rule 來(lái)渲染。

如果?沒有找到對(duì)應(yīng)的 Renderer Rule,那么一個(gè)缺省的 render 函數(shù)會(huì)被調(diào)用。?

Renderer Rules 和 Parsing Rules 是兩個(gè)概念,不要混淆。?由于 Markdown-It 靈活的擴(kuò)展機(jī)制(你可以創(chuàng)建任何類型的 Token),因此到底會(huì)出現(xiàn)多少種 token types 其實(shí)是依賴于你裝了多少 Plugins。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容