All files / markdown / plugins / gfm.ts

92.31% Branches 24/26
100.00% Functions 1/1
87.10% Lines 27/31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
 
x20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x20
x38
x38
x64
x64
x64
x232
x232
x232
x232
x232
x232
x232
x3
 
 
x3
 
 
x3
x3
x232
x232
x232
x232
x232
x232
x232
x38
x38



































I


I










// Imports
import type { MarkdownIt } from "../types.ts"
import footnote from "markdown-it-footnote"

/**
 * Enable GitHub Flavored Markdown (GFM).
 *
 * The markdown-it defaults already cover tables and strikethrough, this plugin adds footnotes and tasklists.
 * Autolinks are covered by the `linkify` option of markdown-it itself.
 *
 * {@link https://github.github.com/gfm | See GitHub Flavored Markdown specification for more information}.
 *
 * ```md
 * - [x] foo
 * ```
 * ```html
 * <ul class="contains-task-list">
 *   <li class="task-list-item"><input type="checkbox" disabled checked> foo</li>
 * </ul>
 * ```
 */
export default function gfm(engine: MarkdownIt): void {
  engine.use(footnote)
  engine.core.ruler.after("inline", "tasklists", (state) => {
    const tokens = state.tokens
    const lists = [] as typeof tokens
    for (let i = 0; i < tokens.length; i++) {
      const token = tokens[i]
      if ((token.type === "bullet_list_open") || (token.type === "ordered_list_open"))
        lists.push(token)
      if ((token.type === "bullet_list_close") || (token.type === "ordered_list_close"))
        lists.pop()
      if ((token.type !== "inline") || (!token.children?.length) || (tokens[i - 1]?.type !== "paragraph_open") || (tokens[i - 2]?.type !== "list_item_open"))
        continue
      const text = token.children[0]
      if (text.type !== "text")
        continue
      const match = text.content.match(/^\[([ xX])\](?: |$)/)
      if (!match)
        continue
      text.content = text.content.slice(match[0].length)
      const checkbox = new state.Token("html_inline", "", 0)
      checkbox.content = `<input type="checkbox" disabled${match[1] === " " ? "" : " checked"}> `
      token.children.unshift(checkbox)
      tokens[i - 2].attrJoin("class", "task-list-item")
      const list = lists.at(-1)
      if (list && (!`${list.attrGet("class")}`.includes("contains-task-list")))
        list.attrJoin("class", "contains-task-list")
    }
  })
}