● Zion Boggan
repos/Darwin/face/src/lib/markdown-lite.ts
zionboggan.com ↗
60 lines · typescript
History for this file →
1
 
2
function escapeHtml(s: string): string {
3
  return s
4
    .replace(/&/g, "&")
5
    .replace(/</g, "&lt;")
6
    .replace(/>/g, "&gt;");
7
}
8
 
9
function inline(s: string): string {
10
  return escapeHtml(s)
11
    .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
12
    .replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, "$1<em>$2</em>")
13
    .replace(/`([^`]+)`/g, '<code class="ml-code">$1</code>');
14
}
15
 
16
export function renderMarkdownLite(src: string): string {
17
  if (!src) return "";
18
  const lines = src.replace(/\r\n/g, "\n").split("\n");
19
  const out: string[] = [];
20
  let list: { type: "ul" | "ol"; items: string[] } | null = null;
21
  let para: string[] = [];
22
 
23
  const flushPara = () => {
24
    if (para.length) {
25
      out.push(`<p>${inline(para.join(" "))}</p>`);
26
      para = [];
27
    }
28
  };
29
  const flushList = () => {
30
    if (list) {
31
      const inner = list.items.map((it) => `<li>${inline(it)}</li>`).join("");
32
      out.push(`<${list.type} class="ml-list">${inner}</${list.type}>`);
33
      list = null;
34
    }
35
  };
36
 
37
  for (const raw of lines) {
38
    const line = raw.trimEnd();
39
    const ulm = line.match(/^\s*[-*+]\s+(.*)$/);
40
    const olm = line.match(/^\s*\d+\.\s+(.*)$/);
41
    if (ulm) {
42
      flushPara();
43
      if (!list || list.type !== "ul") { flushList(); list = { type: "ul", items: [] }; }
44
      list.items.push(ulm[1]);
45
    } else if (olm) {
46
      flushPara();
47
      if (!list || list.type !== "ol") { flushList(); list = { type: "ol", items: [] }; }
48
      list.items.push(olm[1]);
49
    } else if (line.trim() === "") {
50
      flushPara();
51
      flushList();
52
    } else {
53
      flushList();
54
      para.push(line.replace(/^#{1,6}\s*/, ""));
55
    }
56
  }
57
  flushPara();
58
  flushList();
59
  return out.join("");
60
}