Skip to content

Commit 1e11df4

Browse files
committed
feat: split out docs/react routes from shared MdxRoute file
- Create DocsReactRoute.res/.resi for /docs/react/* pages, following the same pattern as MdxRoute with React-specific sidebar categories (Overview, Main Concepts, Hooks & State Management, Guides) and breadcrumbs (Docs > rescript-react) - Register docsReactRoutes in app/routes.res via MdxFile.scanPaths and filter docs/react paths out of the legacy mdxRoutes - Fix inconsistent comparator in Mdx.sortSection: items with an order field now always sort before items without one, and items without order preserve their relative position
1 parent 5226348 commit 1e11df4

File tree

5 files changed

+200
-35
lines changed

5 files changed

+200
-35
lines changed

app/routes.res

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,20 @@ let blogArticleRoutes =
3333
route(path, "./routes/BlogArticleRoute.jsx", ~options={id: path})
3434
)
3535

36+
let docsReactRoutes =
37+
MdxFile.scanPaths(~dir="markdown-pages/docs/react", ~alias="docs/react")->Array.map(path =>
38+
route(path, "./routes/DocsReactRoute.jsx", ~options={id: path})
39+
)
40+
3641
let mdxRoutes = mdxRoutes("./routes/MdxRoute.jsx")->Array.filter(r =>
3742
!(
3843
r.path
39-
->Option.map(path => path === "blog" || String.startsWith(path, "blog/"))
44+
->Option.map(path =>
45+
path === "blog" ||
46+
String.startsWith(path, "blog/") ||
47+
path === "docs/react" ||
48+
String.startsWith(path, "docs/react/")
49+
)
4050
->Option.getOr(false)
4151
)
4252
)
@@ -56,6 +66,7 @@ let default = [
5666
...stdlibRoutes,
5767
...beltRoutes,
5868
...blogArticleRoutes,
69+
...docsReactRoutes,
5970
...mdxRoutes,
6071
route("*", "./routes/NotFoundRoute.jsx"),
6172
]

app/routes/DocsReactRoute.res

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
type loaderData = {
2+
compiledMdx: CompiledMdx.t,
3+
categories: array<SidebarLayout.Sidebar.Category.t>,
4+
entries: array<TableOfContents.entry>,
5+
title: string,
6+
description: string,
7+
filePath: string,
8+
}
9+
10+
let convertToNavItems = (items, rootPath) =>
11+
Array.map(items, (item): SidebarLayout.Sidebar.NavItem.t => {
12+
let href = switch item.Mdx.slug {
13+
| Some(slug) => `${rootPath}/${slug}`
14+
| None => rootPath
15+
}
16+
{
17+
name: item.title,
18+
href,
19+
}
20+
})
21+
22+
let getGroup = (groups, groupName): SidebarLayout.Sidebar.Category.t => {
23+
{
24+
name: groupName,
25+
items: groups
26+
->Dict.get(groupName)
27+
->Option.getOr([]),
28+
}
29+
}
30+
31+
let getAllGroups = (groups, groupNames): array<SidebarLayout.Sidebar.Category.t> =>
32+
groupNames->Array.map(item => getGroup(groups, item))
33+
34+
// Build sidebar categories from all React docs, sorted by their "order" field in frontmatter
35+
let reactTableOfContents = async () => {
36+
let groups =
37+
(await Mdx.allMdx(~filterByPaths=["markdown-pages/docs"]))
38+
->Mdx.filterMdxPages("docs/react")
39+
->Mdx.groupBySection
40+
->Dict.mapValues(values => values->Mdx.sortSection->convertToNavItems("/docs/react"))
41+
42+
getAllGroups(groups, ["Overview", "Main Concepts", "Hooks & State Management", "Guides"])
43+
}
44+
45+
let loader: ReactRouter.Loader.t<loaderData> = async ({request}) => {
46+
let {pathname} = WebAPI.URL.make(~url=request.url)
47+
let filePath = MdxFile.resolveFilePath(
48+
(pathname :> string),
49+
~dir="markdown-pages/docs/react",
50+
~alias="docs/react",
51+
)
52+
53+
let raw = await Node.Fs.readFile(filePath, "utf-8")
54+
let {frontmatter}: MarkdownParser.result = MarkdownParser.parseSync(raw)
55+
56+
let description = switch frontmatter {
57+
| Object(dict) =>
58+
switch dict->Dict.get("description") {
59+
| Some(String(s)) => s
60+
| _ => ""
61+
}
62+
| _ => ""
63+
}
64+
65+
let title = switch frontmatter {
66+
| Object(dict) =>
67+
switch dict->Dict.get("title") {
68+
| Some(String(s)) => s
69+
| _ => ""
70+
}
71+
| _ => ""
72+
}
73+
74+
let categories = await reactTableOfContents()
75+
76+
let compiledMdx = await MdxFile.compileMdx(raw, ~filePath, ~remarkPlugins=Mdx.plugins)
77+
78+
// Build table of contents entries from markdown headings
79+
let markdownTree = Mdast.fromMarkdown(raw)
80+
let tocResult = Mdast.toc(markdownTree, {maxDepth: 2})
81+
82+
let headers = Dict.make()
83+
Mdast.reduceHeaders(tocResult.map, headers)
84+
85+
let entries =
86+
headers
87+
->Dict.toArray
88+
->Array.map(((header, url)): TableOfContents.entry => {
89+
header,
90+
href: (url :> string),
91+
})
92+
->Array.slice(~start=2) // skip document entry and H1 title, keep h2 sections
93+
94+
{
95+
compiledMdx,
96+
categories,
97+
entries,
98+
title: `${title} | ReScript React`,
99+
description,
100+
filePath,
101+
}
102+
}
103+
104+
let default = () => {
105+
let {pathname} = ReactRouter.useLocation()
106+
let {compiledMdx, categories, entries, title, description, filePath} = ReactRouter.useLoaderData()
107+
108+
let breadcrumbs = list{
109+
{Url.name: "Docs", href: "/docs/react/introduction"},
110+
{
111+
Url.name: "rescript-react",
112+
href: "/docs/react/introduction",
113+
},
114+
}
115+
116+
let editHref = `https://github.com/rescript-lang/rescript-lang.org/blob/master/${filePath}`
117+
118+
let sidebarContent =
119+
<aside className="px-4 w-full block">
120+
<div className="flex justify-between items-baseline">
121+
<div className="flex flex-col text-fire font-medium">
122+
<VersionSelect />
123+
</div>
124+
<button
125+
className="flex items-center" onClick={_ => NavbarUtils.closeMobileTertiaryDrawer()}
126+
>
127+
<Icon.Close />
128+
</button>
129+
</div>
130+
<div className="mb-56">
131+
{categories
132+
->Array.map(category => {
133+
let isItemActive = (navItem: SidebarLayout.Sidebar.NavItem.t) =>
134+
navItem.href === (pathname :> string)
135+
let getActiveToc = (navItem: SidebarLayout.Sidebar.NavItem.t) =>
136+
if navItem.href === (pathname :> string) {
137+
Some({TableOfContents.title, entries})
138+
} else {
139+
None
140+
}
141+
<div key=category.name>
142+
<SidebarLayout.Sidebar.Category
143+
isItemActive
144+
getActiveToc
145+
category
146+
onClick={_ => NavbarUtils.closeMobileTertiaryDrawer()}
147+
/>
148+
</div>
149+
})
150+
->React.array}
151+
</div>
152+
</aside>
153+
154+
<>
155+
<Meta title description />
156+
<NavbarSecondary />
157+
<NavbarTertiary sidebar=sidebarContent>
158+
<SidebarLayout.BreadCrumbs crumbs=breadcrumbs />
159+
<a
160+
href=editHref className="inline text-14 hover:underline text-fire" rel="noopener noreferrer"
161+
>
162+
{React.string("Edit")}
163+
</a>
164+
</NavbarTertiary>
165+
<DocsLayout categories activeToc={title, entries}>
166+
<div className="markdown-body">
167+
<MdxContent compiledMdx />
168+
</div>
169+
</DocsLayout>
170+
</>
171+
}

app/routes/DocsReactRoute.resi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
type loaderData = {
2+
compiledMdx: CompiledMdx.t,
3+
categories: array<SidebarLayout.Sidebar.Category.t>,
4+
entries: array<TableOfContents.entry>,
5+
title: string,
6+
description: string,
7+
filePath: string,
8+
}
9+
10+
let loader: ReactRouter.Loader.t<loaderData>
11+
12+
let default: unit => React.element

app/routes/MdxRoute.res

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,6 @@ let manualTableOfContents = async () => {
9999
categories
100100
}
101101

102-
let reactTableOfContents = async () => {
103-
let groups =
104-
(await allMdx(~filterByPaths=["markdown-pages/docs"]))
105-
->filterMdxPages("docs/react")
106-
->groupBySection
107-
->Dict.mapValues(values => values->sortSection->convertToNavItems("/docs/react"))
108-
109-
// these are the categories that appear in the sidebar
110-
let categories: array<SidebarLayout.Sidebar.Category.t> = getAllGroups(
111-
groups,
112-
["Overview", "Main Concepts", "Hooks & State Management", "Guides"],
113-
)
114-
115-
categories
116-
}
117-
118102
let communityTableOfContents = async () => {
119103
let groups =
120104
(await allMdx(~filterByPaths=["markdown-pages/community"]))
@@ -163,8 +147,6 @@ let loader: ReactRouter.Loader.t<loaderData> = async ({request}) => {
163147
[]
164148
} else if pathname->String.includes("docs/manual") {
165149
await manualTableOfContents()
166-
} else if pathname->String.includes("docs/react") {
167-
await reactTableOfContents()
168150
} else if pathname->String.includes("community") {
169151
await communityTableOfContents()
170152
} else {
@@ -222,21 +204,11 @@ let loader: ReactRouter.Loader.t<loaderData> = async ({request}) => {
222204
href: "/docs/manual/" ++ "introduction",
223205
},
224206
})
225-
: pathname->String.includes("docs/react")
226-
? Some(list{
227-
{Url.name: "Docs", href: "/docs/"},
228-
{
229-
Url.name: "rescript-react",
230-
href: "/docs/react/" ++ "introduction",
231-
},
232-
})
233207
: None
234208

235209
let metaTitleCategory = {
236210
let path = (pathname :> string)
237-
let title = if path->String.includes("docs/react") {
238-
"ReScript React"
239-
} else if path->String.includes("docs/manual/api") {
211+
let title = if path->String.includes("docs/manual/api") {
240212
"ReScript API"
241213
} else if path->String.includes("docs/manual") {
242214
"ReScript Language Manual"
@@ -322,8 +294,7 @@ let default = () => {
322294
</>
323295
} else if (
324296
(pathname :> string)->String.includes("docs/manual") ||
325-
(pathname :> string)->String.includes("docs/react") ||
326-
(pathname :> string)->String.includes("docs/guidelines")
297+
(pathname :> string)->String.includes("docs/guidelines")
327298
) {
328299
<>
329300
<Meta title=title description={attributes.description->Nullable.getOr("")} />
@@ -334,8 +305,6 @@ let default = () => {
334305
if index === 0 {
335306
if (pathname :> string)->String.includes("docs/manual") {
336307
{...item, href: "/docs/manual/introduction"}
337-
} else if (pathname :> string)->String.includes("docs/react") {
338-
{...item, href: "/docs/react/introduction"}
339308
} else {
340309
item
341310
}

src/Mdx.res

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ let sortSection = mdxPages =>
8282
Array.toSorted(mdxPages, (a: attributes, b: attributes) =>
8383
switch (a.order, b.order) {
8484
| (Some(a), Some(b)) => a > b ? 1.0 : -1.0
85-
| _ => -1.0
85+
| (Some(_), None) => -1.0
86+
| (None, Some(_)) => 1.0
87+
| (None, None) => 0.0
8688
}
8789
)
8890

0 commit comments

Comments
 (0)