1 |
const { lstatSync, read, readdirSync, existsSync } = require("fs"); |
2 |
const { readFile, writeFile, cp } = require("fs/promises"); |
3 |
const { glob } = require("glob"); |
4 |
const path = require("path"); |
5 |
|
6 |
(async () => { |
7 |
const pages = await glob("app/**/page.{tsx,mdx,md}"); |
8 |
const index = []; |
9 |
|
10 |
async function loadDocsIndex( |
11 |
directory = path.resolve(__dirname, "app/(docs)"), |
12 |
href = "/", |
13 |
) { |
14 |
const data = []; |
15 |
const files = readdirSync(directory); |
16 |
|
17 |
for (const filename of files) { |
18 |
console.log("INDEXING", filename, directory); |
19 |
const file = path.resolve(directory, filename); |
20 |
const stat = lstatSync(file); |
21 |
|
22 |
if (stat.isDirectory()) { |
23 |
const info = await loadDocsIndex( |
24 |
file, |
25 |
`${href}${href === "/" ? "" : "/"}${filename}`, |
26 |
); |
27 |
const i = info.children.findIndex( |
28 |
c => |
29 |
c.name === "page.mdx" || |
30 |
c.name === "page.md" || |
31 |
c.name === "page.tsx", |
32 |
); |
33 |
const removed = i !== -1 ? info.children.splice(i, 1)[0] : null; |
34 |
|
35 |
data.push( |
36 |
i !== -1 |
37 |
? { |
38 |
...info, |
39 |
children: info.children, |
40 |
type: "page", |
41 |
data: info.data ?? removed.data, |
42 |
} |
43 |
: info, |
44 |
); |
45 |
|
46 |
continue; |
47 |
} |
48 |
|
49 |
if ( |
50 |
filename !== "page.tsx" && |
51 |
filename !== "page.md" && |
52 |
filename !== "page.mdx" |
53 |
) |
54 |
continue; |
55 |
|
56 |
const isMDX = file.endsWith(".mdx") || file.endsWith(".md"); |
57 |
const info = isMDX |
58 |
? await generateIndexForMDXPage(file) |
59 |
: await generateIndexForTSXPage(file); |
60 |
|
61 |
data.push({ |
62 |
type: "page", |
63 |
name: path.basename(filename), |
64 |
url: file |
65 |
.replace(/[\/\\]\([a-z0-9A-Z_-]+\)/gi, "") |
66 |
.replace(/^app[\/\\]/gi, "") |
67 |
.replace(/page\.(tsx|md|mdx)$/gi, "") |
68 |
.replace(/\\/g, "/"), |
69 |
path: file.replace(/\\/g, "/"), |
70 |
data: { |
71 |
title: info.title, |
72 |
short_name: info.short_name, |
73 |
}, |
74 |
}); |
75 |
|
76 |
console.log("NESTED INDEXED", file); |
77 |
} |
78 |
|
79 |
const name = path.basename(directory); |
80 |
|
81 |
const dirdata = existsSync(path.join(directory, "metadata.json")) |
82 |
? JSON.parse( |
83 |
await readFile( |
84 |
path.join(directory, "metadata.json"), |
85 |
"utf-8", |
86 |
), |
87 |
) |
88 |
: null; |
89 |
const children = [...(dirdata?.entries ?? []), ...data]; |
90 |
|
91 |
children.sort((a, b) => { |
92 |
if (dirdata?.sort_as) { |
93 |
const aIndex = dirdata.sort_as.indexOf(a.name); |
94 |
const bIndex = dirdata.sort_as.indexOf(b.name); |
95 |
|
96 |
if (aIndex !== -1 && bIndex !== -1) { |
97 |
return aIndex - bIndex; |
98 |
} else if (aIndex !== -1) { |
99 |
return -1; |
100 |
} else if (bIndex !== -1) { |
101 |
return 1; |
102 |
} |
103 |
} |
104 |
|
105 |
return a.name.localeCompare(b.name); |
106 |
}); |
107 |
|
108 |
return { |
109 |
type: "directory", |
110 |
name: name === "(docs)" ? "/" : name, |
111 |
children: children, |
112 |
href, |
113 |
data: dirdata |
114 |
? { |
115 |
title: dirdata.title, |
116 |
short_name: dirdata.short_name, |
117 |
} |
118 |
: undefined, |
119 |
}; |
120 |
} |
121 |
|
122 |
for (const page of pages) { |
123 |
const isMDX = page.endsWith(".mdx") || page.endsWith(".md"); |
124 |
|
125 |
index.push( |
126 |
isMDX |
127 |
? await generateIndexForMDXPage(page) |
128 |
: await generateIndexForTSXPage(page), |
129 |
); |
130 |
|
131 |
console.log("DONE ", page); |
132 |
} |
133 |
|
134 |
await writeFile(path.join(__dirname, "index.json"), JSON.stringify(index)); |
135 |
|
136 |
await writeFile( |
137 |
path.join(__dirname, "docs_index.json"), |
138 |
JSON.stringify(await loadDocsIndex(), null, 4), |
139 |
); |
140 |
|
141 |
try { |
142 |
await cp( |
143 |
path.join(__dirname, "index.json"), |
144 |
path.join(__dirname, ".next/server/index.json"), |
145 |
); |
146 |
} catch (error) { |
147 |
console.error(error); |
148 |
} |
149 |
})(); |
150 |
|
151 |
async function generateIndexForMDXPage(page) { |
152 |
const contents = await readFile(page, { |
153 |
encoding: "utf-8", |
154 |
}); |
155 |
let [frontmatter, data] = contents |
156 |
.substring(contents.startsWith("---") ? 3 : 0) |
157 |
.split("\n---\n"); |
158 |
|
159 |
if (!contents.trimStart().startsWith("---")) { |
160 |
data = frontmatter; |
161 |
frontmatter = null; |
162 |
} |
163 |
|
164 |
const entries = frontmatter |
165 |
?.split("\n") |
166 |
.filter(Boolean) |
167 |
.map(entry => |
168 |
entry |
169 |
.split(/:(.*)/s) |
170 |
.filter(Boolean) |
171 |
.map((a, i) => { |
172 |
const trimmed = a.trim(); |
173 |
return i === 1 && |
174 |
trimmed.startsWith('"') && |
175 |
trimmed.endsWith('"') |
176 |
? trimmed.substring(1, trimmed.length - 1).trim() |
177 |
: trimmed; |
178 |
}), |
179 |
); |
180 |
|
181 |
const frontmatterData = entries ? Object.fromEntries(entries) : null; |
182 |
|
183 |
return { |
184 |
...frontmatterData, |
185 |
data: (data ?? "") |
186 |
.replace(/^(([\s\r\n]*)import([^.]+);)+/gi, "") |
187 |
.replace(/^(([\s\r\n]*)export([^.]+);)+/gi, "") |
188 |
.replace(/([\s\r\n]*)export default ([^;]+);$/gi, "") |
189 |
.replace(/<\/?[^>]+(>|$)/g, ""), |
190 |
url: |
191 |
"/" + |
192 |
page |
193 |
.replace(/[\/\\]\([a-z0-9A-Z_-]+\)/gi, "") |
194 |
.replace(/^app[\/\\]/gi, "") |
195 |
.replace(/page\.(tsx|md|mdx)$/gi, "") |
196 |
.replace(/\\/g, "/"), |
197 |
path: page.replace(/\\/g, "/"), |
198 |
}; |
199 |
} |
200 |
|
201 |
async function generateIndexForTSXPage(page) { |
202 |
throw new Error("Not implemented"); |
203 |
} |