aboutsummaryrefslogtreecommitdiff
path: root/source/library/graphql/markdown.ts
blob: a2ca6f73a28d26e0b327f535654664f28de68fe0 (plain) (blame)
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
/*** EXPORT ------------------------------------------- ***/

export function markdown(input: string): string {
  const inlineCode: string[] = [];
  const inlineCodePattern = /`([^`]+)`/g;
  const refPattern = /\s*\[(\w+)\]\s+<([^>]+)>$/gm;
  const refs = new Map<string, string>();

  let processed = input.replace(inlineCodePattern, (_match, content) => {
    const index = inlineCode.length;
    inlineCode.push(content);
    return `<!--INLINE:CODE:${index}-->`;
  });

  for (const match of processed.matchAll(refPattern)) {
    const [, ref, url] = match;
    refs.set(ref, url);
  }

  processed = processed
    .replace(/\[([^\]]+)\]\(([^)]+)\)/g, `<a href="$2" target="_blank">$1</a>`)
    .replace(/(\*\*|__)(.*?)\1/g, `<strong style="white-space: nowrap;">$2</strong>`)
    .replace(/'/g, "’")
    .replace(/(\.\.\.)/g, "…")
    .replace(/---/g, "<hr/>");

  for (const block in inlineCode) {
    processed = processed.replace(`<!--INLINE:CODE:${block}-->`, `<code>${escapeHtml(inlineCode[block])}</code>`);
  }

  return processed.trimEnd();
}

/*** HELPER ------------------------------------------- ***/

function escapeHtml(str: string): string {
  return str
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}