1 |
import docsPageListJson from "@/docs_index.json"; |
2 |
import indexJson from "@/index.json"; |
3 |
import { |
4 |
BOT_INVITE_REQUEST_URL, |
5 |
DISCORD_URL, |
6 |
DONATION_URL, |
7 |
SUPPORT_EMAIL_ADDRESS, |
8 |
} from "./links"; |
9 |
import { toTitleCase } from "./utils"; |
10 |
|
11 |
export type OldDocsPage = { |
12 |
name: string; |
13 |
url?: string; |
14 |
children?: DocsPageWithoutChildren[]; |
15 |
}; |
16 |
|
17 |
export type DocsPageWithoutChildren = Omit<OldDocsPage, "children">; |
18 |
|
19 |
export type DocsPage = { |
20 |
type: "directory" | "page"; |
21 |
name: string; |
22 |
href: string; |
23 |
children?: DocsPage[]; |
24 |
data?: { |
25 |
title?: string; |
26 |
short_name?: string; |
27 |
}; |
28 |
}; |
29 |
|
30 |
export const pages = [ |
31 |
{ |
32 |
name: "Home", |
33 |
url: "/", |
34 |
}, |
35 |
{ |
36 |
name: "FAQ", |
37 |
url: "/faq", |
38 |
}, |
39 |
{ |
40 |
name: "Invite", |
41 |
url: BOT_INVITE_REQUEST_URL, |
42 |
}, |
43 |
{ |
44 |
name: "Support", |
45 |
url: `mailto:${SUPPORT_EMAIL_ADDRESS}`, |
46 |
}, |
47 |
{ |
48 |
name: "Donate", |
49 |
url: DONATION_URL, |
50 |
}, |
51 |
{ |
52 |
name: "Discord", |
53 |
url: DISCORD_URL, |
54 |
}, |
55 |
]; |
56 |
|
57 |
export function resolveDocsURL(url: string) { |
58 |
return url.startsWith("/") ? url : `/${url}`; |
59 |
} |
60 |
|
61 |
export type Index = { |
62 |
title?: string; |
63 |
description?: string; |
64 |
data: string; |
65 |
}; |
66 |
|
67 |
let index: Index[] | null = null, |
68 |
lowercasedIndex: Index[] | null = null; |
69 |
|
70 |
function loadIndex() { |
71 |
if (index !== null) { |
72 |
return; |
73 |
} |
74 |
|
75 |
index = indexJson; |
76 |
lowercasedIndex = |
77 |
index?.map(entry => ({ |
78 |
data: entry.data.toLowerCase(), |
79 |
title: entry.title?.toLowerCase(), |
80 |
description: entry.description?.toLowerCase(), |
81 |
})) ?? null; |
82 |
} |
83 |
|
84 |
export const getIndex = (lower = false) => { |
85 |
if (!index) { |
86 |
loadIndex(); |
87 |
} |
88 |
|
89 |
return lower ? lowercasedIndex! : index!; |
90 |
}; |
91 |
|
92 |
let docsPages: OldDocsPage[] | null = null; |
93 |
|
94 |
export const getDocsPages = () => { |
95 |
if (!docsPages) { |
96 |
loadDocsPages(); |
97 |
} |
98 |
|
99 |
return docsPages!; |
100 |
}; |
101 |
|
102 |
const excludedFromGrouping = ["features", "getting-started"]; |
103 |
const forceGrouping = ["extensions"]; |
104 |
const hoistedPages = [ |
105 |
"getting-started", |
106 |
"features", |
107 |
"features/screenshots", |
108 |
].map(a => `/${a}`); |
109 |
|
110 |
function loadDocsPages() { |
111 |
if (docsPages) { |
112 |
return; |
113 |
} |
114 |
|
115 |
docsPages = []; |
116 |
|
117 |
const flattenedPages = indexJson.map( |
118 |
page => |
119 |
({ |
120 |
name: page.short_name ?? "Unnamed", |
121 |
url: page.url, |
122 |
}) satisfies DocsPageWithoutChildren, |
123 |
); |
124 |
const pages: Record<string, DocsPageWithoutChildren[]> = {}; |
125 |
|
126 |
for (const page of flattenedPages) { |
127 |
const splitted = page.url.split("/"); |
128 |
const [, firstDirectory] = splitted; |
129 |
|
130 |
const key = |
131 |
(forceGrouping.includes(firstDirectory) || |
132 |
(!excludedFromGrouping.includes(firstDirectory) && |
133 |
firstDirectory !== "")) && |
134 |
splitted.length > 3 |
135 |
? toTitleCase(firstDirectory) |
136 |
: "_"; |
137 |
|
138 |
pages[key] ??= []; |
139 |
pages[key].push({ |
140 |
name: page.name, |
141 |
url: |
142 |
page.url !== "/" && page.url.endsWith("/") |
143 |
? page.url.substring(0, page.url.length - 1) |
144 |
: page.url, |
145 |
}); |
146 |
} |
147 |
|
148 |
if (pages._) { |
149 |
docsPages?.push( |
150 |
...pages._.sort( |
151 |
(a, b) => a.name.charCodeAt(0) - b.name.charCodeAt(0), |
152 |
), |
153 |
); |
154 |
} |
155 |
|
156 |
for (const key in pages) { |
157 |
if (key === "_") { |
158 |
continue; |
159 |
} |
160 |
|
161 |
docsPages?.push({ |
162 |
name: key, |
163 |
children: pages[key].sort( |
164 |
(a, b) => a.name.charCodeAt(0) - b.name.charCodeAt(0), |
165 |
), |
166 |
}); |
167 |
} |
168 |
|
169 |
docsPages.sort((a, b) => { |
170 |
const aIndex = hoistedPages.indexOf(a.url ?? ""); |
171 |
const bIndex = hoistedPages.indexOf(b.url ?? ""); |
172 |
|
173 |
if (a.url === "/") { |
174 |
return -1; |
175 |
} |
176 |
|
177 |
if (b.url === "/") { |
178 |
return 1; |
179 |
} |
180 |
|
181 |
if (aIndex !== -1 && bIndex !== -1) { |
182 |
return aIndex - bIndex; |
183 |
} |
184 |
|
185 |
if (aIndex !== -1) { |
186 |
return -1; |
187 |
} |
188 |
|
189 |
if (bIndex !== -1) { |
190 |
return 1; |
191 |
} |
192 |
|
193 |
return a.name.localeCompare(b.name); |
194 |
}); |
195 |
} |
196 |
|
197 |
export const getAllDocsPages = () => { |
198 |
return docsPageListJson as unknown as { |
199 |
type: "directory"; |
200 |
name: "/"; |
201 |
children: DocsPage[]; |
202 |
}; |
203 |
}; |
204 |
|
205 |
export const flatten = ( |
206 |
pages: DocsPage[] = getAllDocsPages().children, |
207 |
): DocsPage[] => { |
208 |
return pages.reduce<DocsPage[]>((acc, page) => { |
209 |
if (page.children) { |
210 |
acc.push(page, ...flatten(page.children)); |
211 |
} else { |
212 |
acc.push(page); |
213 |
} |
214 |
return acc; |
215 |
}, []); |
216 |
}; |
217 |
|
218 |
export const isBlogPath = (path: string) => { |
219 |
return path.startsWith("/blog/") || path === "/blog"; |
220 |
}; |