aboutsummaryrefslogtreecommitdiff
path: root/docs/src/app.ffi.mjs
blob: f5e7f2906ba5307020e463830ccff4791d7354a3 (plain)
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
86
87
88
89
90
91
92
93
94
95
import { fromMarkdown } from "mdast-util-from-markdown";
import { attribute } from "../lustre/lustre/attribute.mjs";
import { element, text } from "../lustre/lustre/element.mjs";
import { List } from "./gleam.mjs";
import * as Markdown from "./app/ui/markdown.mjs";

// Parsing markdown and then walking the AST is expensive so we're going to trade
// some memory for speed and cache the results.
const cache = new Map();

// fn parse_markdown(md: String) -> #(List(Element(msg)), List(Element(msg)))
export const parse_markdown = (md) => {
  if (cache.has(md)) return cache.get(md);

  const ast = fromMarkdown(md);
  const summary = [];
  const content = ast.children.map(function to_lustre_element(node) {
    switch (node.type) {
      case "code":
        return Markdown.code(node.value);

      case "emphasis":
        return Markdown.emphasis(
          List.fromArray(node.children.map(to_lustre_element))
        );

      case "heading": {
        const [title, rest] = node.children[0].value.split("|");
        const tags = List.fromArray(rest ? rest.trim().split(" ") : []);
        const id =
          /^[A-Z]/.test(title.trim()) && node.depth === 3
            ? `${title.toLowerCase().trim().replace(/\s/g, "-")}-type`
            : `${title.toLowerCase().trim().replace(/\s/g, "-")}`;

        if (node.depth > 1) {
          summary.push(
            element(
              "a",
              List.fromArray([
                attribute("href", `#${id}`),
                attribute("class", "text-sm text-gray-400 no-underline"),
                attribute("class", "hover:text-gray-700 hover:underline"),
                attribute(
                  "class",
                  node.depth === 2 ? `mt-4 first:mt-0` : `ml-2`
                ),
              ]),
              List.fromArray([text(title.trim())])
            )
          );
        }

        return Markdown.heading(node.depth, title.trim(), tags, id);
      }

      case "inlineCode":
        return Markdown.inline_code(node.value);

      case "link":
        return Markdown.link(node.url, List.fromArray(node.children));

      case "list":
        return Markdown.list(
          !!node.ordered,
          List.fromArray(node.children.map(to_lustre_element))
        );

      case "listItem":
        return Markdown.list_item(
          List.fromArray(node.children.map(to_lustre_element))
        );

      case "paragraph":
        return Markdown.paragraph(
          List.fromArray(node.children.map(to_lustre_element))
        );

      case "strong":
        return Markdown.strong(
          List.fromArray(node.children.map(to_lustre_element))
        );

      case "text":
        return Markdown.text(node.value);

      default:
        return Markdown.text("");
    }
  });

  const result = [List.fromArray(content), List.fromArray(summary)];
  cache.set(md, result);

  return result;
};