Jacky Zhao
2023-06-01 42d3a7de1711bbd40a2b6857e3bf7ff17685f5d9
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { PluggableList } from "unified"
import { QuartzTransformerPlugin } from "../types"
import { Root } from 'mdast'
import { findAndReplace } from "mdast-util-find-and-replace"
import { slugify } from "../../path"
import rehypeRaw from "rehype-raw"
 
export interface Options {
  highlight: boolean
  wikilinks: boolean
}
 
const defaultOptions: Options = {
  highlight: true,
  wikilinks: true
}
 
export class ObsidianFlavoredMarkdown extends QuartzTransformerPlugin {
  name = "ObsidianFlavoredMarkdown"
  opts: Options
 
  constructor(opts?: Options) {
    super()
    this.opts = { ...defaultOptions, ...opts }
  }
 
  markdownPlugins(): PluggableList {
    const plugins: PluggableList = []
 
    if (this.opts.wikilinks) {
      plugins.push(() => {
        // Match wikilinks 
        // !?               -> optional embedding
        // \[\[             -> open brace
        // ([^\[\]\|\#]+)   -> one or more non-special characters ([,],|, or #) (name)
        // (#[^\[\]\|\#]+)? -> # then one or more non-special characters (heading link)
        // (|[^\[\]\|\#]+)? -> | then one or more non-special characters (alias)
        const backlinkRegex = new RegExp(/!?\[\[([^\[\]\|\#]+)(#[^\[\]\|\#]+)?(\|[^\[\]\|\#]+)?\]\]/, "g")
        return (tree: Root, _file) => {
          findAndReplace(tree, backlinkRegex, (value: string, ...capture: string[]) => {
            if (value.startsWith("!")) {
 
            } else {
              const [path, rawHeader, rawAlias] = capture
              const header = rawHeader?.slice(1).trim() ?? ""
              const alias = rawAlias?.slice(1).trim() ?? path
              const url = slugify(path.trim() + header)
              return {
                type: 'link',
                url,
                children: [{
                  type: 'text',
                  value: alias
                }]
              }
            }
          })
        }
      }
      )
    }
 
    if (this.opts.highlight) {
      plugins.push(() => {
        // Match highlights 
        const highlightRegex = new RegExp(/==(.+)==/, "g")
        return (tree: Root, _file) => {
          findAndReplace(tree, highlightRegex, (_value: string, ...capture: string[]) => {
            const [inner] = capture
            return {
              type: 'html',
              value: `<span class="text-highlight">${inner}</span>`
            }
          })
        }
      })
    }
 
    return plugins
  }
 
  htmlPlugins(): PluggableList {
    return [rehypeRaw]
  }
}