Skip to content
This repository was archived by the owner on Feb 15, 2022. It is now read-only.

Commit 3264d27

Browse files
author
kanishka-work
authored
Cleanup markdown table of contents code (#322)
1 parent a4becaf commit 3264d27

File tree

6 files changed

+66
-60
lines changed

6 files changed

+66
-60
lines changed

CONTRIBUTING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,3 @@ Go to `http://localhost:3000`
151151
## Architecture
152152

153153
We have prepared some diagrams and explanations to orient new developers in the wiki area. The site expands upon the default build process in NextJS to accommodate more sophisticated markdown transformations.
154-

bindings/GrayMatter.res

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ type output = {data: pageContent, content: string}
88
@module("gray-matter") external matter: string => output = "default"
99

1010
let forceInvalidException: JsYaml.forceInvalidException<pageContent> = c => {
11-
let length = Js.Option.getWithDefault("", c.pageDescription) |> Js.String.length
11+
let length = Js.Option.getWithDefault("", c.pageDescription)->Js.String.length
1212
let _ = (Js.String.length(c.title), length)
1313
}
14+
15+
let ofMarkdown = fileContents => {
16+
let parsed = matter(fileContents)
17+
forceInvalidException(parsed.data)
18+
parsed
19+
}

bindings/GrayMatter.resi

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,4 @@ type pageContent = {
55

66
type output = {data: pageContent, content: string}
77

8-
let matter: string => output
9-
10-
let forceInvalidException: JsYaml.forceInvalidException<pageContent>
8+
let ofMarkdown: string => output

common/MdastToc.res

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ let transformer = (rootnode: Unified.rootnode, file: Unified.vfile) => {
88
let rec collect = (nodes, inProgress) => {
99
switch nodes {
1010
| list{} =>
11-
switch inProgress {
12-
| None => list{}
13-
| Some(_, entry) => list{entry}
14-
}
11+
inProgress
12+
->Belt.Option.map(((_, entry)) => entry)
13+
->Belt.Option.mapWithDefault(list{}, e => list{e})
1514
| list{h: Unified.headingnode, ...tail} =>
1615
let d = h.depth
1716
if d >= 2 || d <= 3 {
@@ -22,13 +21,14 @@ let transformer = (rootnode: Unified.rootnode, file: Unified.vfile) => {
2221
}
2322
switch inProgress {
2423
| None => collect(tail, Some(d, entry))
25-
| Some(lastRootDepth, inProgress) if d <= lastRootDepth => list{
26-
inProgress,
24+
| Some(lastRootDepth, completed) if d <= lastRootDepth => list{
25+
completed,
2726
...collect(tail, Some(d, entry)),
2827
}
2928
| Some(lastRootDepth, inProgress) =>
3029
let inProgress = {
3130
...inProgress,
31+
// TODO: use add and perform reverse when inProgress is complete
3232
children: Belt.List.concat(inProgress.children, list{entry}),
3333
}
3434
collect(tail, Some(lastRootDepth, inProgress))
@@ -42,17 +42,16 @@ let transformer = (rootnode: Unified.rootnode, file: Unified.vfile) => {
4242
}
4343
}
4444
}
45-
let headings = collect(
46-
Array.to_list(
47-
Belt.Array.keepMap(rootnode.children, ch =>
48-
switch ch {
49-
| {\"type": "heading", depth: Some(_)} => Some(Unified.asHeadingNode(ch))
50-
| _ => None
51-
}
52-
),
53-
),
54-
None,
55-
)
45+
let headings =
46+
rootnode.children
47+
->Belt.Array.keepMap(ch =>
48+
switch ch {
49+
| {\"type": "heading", depth: Some(_)} => Some(Unified.asHeadingNode(ch))
50+
| _ => None
51+
}
52+
)
53+
->Array.to_list
54+
->collect(None)
5655

5756
file.toc = headings
5857
}

components/MarkdownPage.res

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,24 @@ module TableOfContents = {
1515
toc: Unified.MarkdownTableOfContents.t,
1616
}
1717

18+
exception UnexpectedTOCHeadingDepth(int)
19+
20+
let headingLink = (depth, id, label, ~idx=?, ()) => {
21+
let indent = switch depth {
22+
// NOTE: we aren't building a dynamic classname string to be
23+
// compatible with the tailwind css purging logic
24+
| 2 => ""
25+
| 3 => "pl-6"
26+
| _ => raise(UnexpectedTOCHeadingDepth(depth))
27+
}
28+
let href = j`#${id}`
29+
let className = `${indent} block text-gray-600 hover:text-gray-900 pr-2 py-2 text-sm font-medium`
30+
switch idx {
31+
| None => <a href className> {s(label)} </a>
32+
| Some(idx) => <a key={Js.Int.toString(idx)} href className> {s(label)} </a>
33+
}
34+
}
35+
1836
@react.component
1937
let make = (~content) =>
2038
<div
@@ -26,25 +44,20 @@ module TableOfContents = {
2644
{content.toc
2745
->Belt.List.mapWithIndex((idx, hdg) =>
2846
<div key={Js.Int.toString(idx)} className="space-y-1">
29-
// Expanded: "text-gray-400 rotate-90", Collapsed: "text-gray-300"
30-
<a
31-
href={"#" ++ hdg.id}
32-
className="block text-gray-600 hover:text-gray-900 pr-2 py-2 text-sm font-medium">
33-
{s(hdg.label)}
34-
</a>
47+
{
48+
// Expanded: "text-gray-400 rotate-90", Collapsed: "text-gray-300"
49+
headingLink(2, hdg.id, hdg.label, ())
50+
}
3551
{hdg.children
36-
->Belt.List.mapWithIndex((idx, sub) =>
37-
<a
38-
href={"#" ++ sub.id}
39-
className="block pl-6 pr-2 py-2 text-sm font-medium text-gray-600 hover:text-gray-900"
40-
key={Js.Int.toString(idx)}>
41-
{s(sub.label)}
42-
</a>
43-
)
44-
->Belt.List.toArray |> React.array}
52+
->Belt.List.mapWithIndex((idx, sub) => {
53+
headingLink(3, sub.id, sub.label, ~idx, ())
54+
})
55+
->Belt.List.toArray
56+
->React.array}
4557
</div>
4658
)
47-
->Belt.List.toArray |> React.array}
59+
->Belt.List.toArray
60+
->React.array}
4861
</nav>
4962
</div>
5063
</div>

pages/resources/[tutorial].res

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,19 @@ let getStaticProps = ctx => {
4444
let baseDirectory = "data/tutorials/"
4545
// TODO: find the location of the tutorial
4646
let contentFilePath = baseDirectory ++ tutorial ++ "/" ++ tutorial ++ ".md"
47-
let fileContents = Fs.readFileSync(contentFilePath)
48-
let parsed = GrayMatter.matter(fileContents)
49-
// TODO: move this into GrayMatter or another module
50-
GrayMatter.forceInvalidException(parsed.data)
51-
let source = parsed.content
47+
let parsed = Fs.readFileSync(contentFilePath)->GrayMatter.ofMarkdown
5248

53-
let resPromise = Unified.process(
54-
Unified.use(
55-
Unified.use(
56-
Unified.use(
57-
Unified.use(
58-
Unified.use(Unified.use(Unified.unified(), Unified.remarkParse), Unified.remarkSlug),
59-
MdastToc.plugin,
60-
),
61-
Unified.remark2rehype,
62-
),
63-
Unified.rehypeHighlight,
64-
),
65-
Unified.rehypeStringify,
66-
),
67-
source,
68-
)
49+
let resPromise =
50+
parsed.content->Unified.process(
51+
Unified.unified()
52+
->Unified.use(Unified.remarkParse)
53+
->Unified.use(Unified.remarkSlug)
54+
->Unified.use(MdastToc.plugin)
55+
->Unified.use(Unified.remark2rehype)
56+
->Unified.use(Unified.rehypeHighlight)
57+
->Unified.use(Unified.rehypeStringify),
58+
_,
59+
)
6960

7061
Js.Promise.then_((res: Unified.vfile) => {
7162
let props = {
@@ -86,7 +77,7 @@ let getStaticProps = ctx => {
8677

8778
let getStaticPaths: Next.GetStaticPaths.t<Params.t> = () => {
8879
// TODO: move this logic into a module dedicated to fetching tutorials
89-
// TODO: throw exception if any tutorials have the same filename or add a more parts to the tutorials path
80+
// TODO: throw exception if any tutorials have the same filename or add more parts to the tutorials path
9081
// TODO: throw exception if any entry is not a directory
9182
let markdownFiles = Fs.readdirSyncEntries("data/tutorials/")
9283

0 commit comments

Comments
 (0)