All files / markdown / plugins / anchors.ts

85.71% Branches 12/14
100.00% Functions 1/1
100.00% Lines 23/23
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
 
 
x20
 
 
 
 
 
 
 
 
 
 
 
x20
x1
x2
x2
x9
x9
x9
x9
x9
x9
x9
x9
x9
x9
x3
x3
x3
x3
x9
x9
x1
x1





















I









I


// Imports
import type { MarkdownIt } from "../types.ts"
import { slugify } from "@std/text/unstable-slugify"

/**
 * Add anchors to headings and autolink them.
 *
 * ```md
 * # foo
 * ```
 * ```html
 * <h1 id="foo"><a href="#foo">foo</a></h1>
 * ```
 */
export default function anchors(engine: MarkdownIt): void {
  engine.core.ruler.push("anchors", (state) => {
    const slugs = new Map<string, number>()
    for (let i = 0; i < state.tokens.length; i++) {
      const token = state.tokens[i]
      const inline = state.tokens[i + 1]
      if ((token.type !== "heading_open") || (inline?.type !== "inline"))
        continue
      const text = (inline.children ?? []).filter(({ type }) => ["text", "code_inline"].includes(type)).map(({ content }) => content).join("")
      let slug = slugify(text)
      const count = slugs.get(slug) ?? 0
      slugs.set(slug, count + 1)
      if (count)
        slug = `${slug}-${count}`
      token.attrSet("id", slug)
      const open = new state.Token("link_open", "a", 1)
      open.attrSet("href", `#${slug}`)
      const close = new state.Token("link_close", "a", -1)
      inline.children = [open, ...inline.children ?? [], close]
    }
  })
}