on master help sign in
build: re-structure buildPageTree as a prebuild script
bdd41e7Matilde Park
lib/buildPageTree.jsnew file+110
@@ -0,0 +1,110 @@
1+const fs = require("fs");
2+const path = require("path");
3+const matter = require("gray-matter");
4+const toml = require("@iarna/toml");
5+
6+// node ./buildPageTree.js [directory] [sort]
7+// eg. node ./buildPageTree docs weight
8+// will produce docs.json in the cache folder.
9+
10+const options = {
11+ engines: {
12+ toml: toml.parse.bind(toml),
13+ },
14+ language: "toml",
15+ delimiters: "+++",
16+};
17+
18+/**
19+ * buildPageTree is written to provide a recursive index of posts
20+ * for nested directories for creating sidebars
21+ * @param {string} path Absolute path to the subdirectory.
22+ * @returns {Object.<string,string|Object<string,string[]|number>>} Title of the directory, child pages, and child directories (recursive structure).
23+ *
24+ */
25+
26+// type PageTree =
27+// { title: string,
28+// pages: [{
29+// title: string,
30+// base: string,
31+// slug: string,
32+// weight: number,
33+// }],
34+// children: { [string]: PageTree },
35+// }
36+
37+function buildPageTree(dirPath, ordering = "", content = false) {
38+ const metadata = matter(
39+ fs.readFileSync(path.join(dirPath, "_index.md")),
40+ options
41+ );
42+ // get a list of contents at the dirPath, with file types so that the item at the dirPath can be identified as file or folder
43+ const children = fs.readdirSync(dirPath, { withFileTypes: true });
44+ // get a list of files
45+ const pages = children.filter((f) => f.isFile() && f.name !== "_index.md");
46+ // get a list of folders
47+ const subdirs = children.filter((f) => f.isDirectory());
48+ // return a pagetree datastructure
49+ return {
50+ title: metadata.data.title,
51+ pages: pages
52+ .map((page) => {
53+ // retrieve topmatter as JSON
54+ const { content, data } = matter(
55+ fs.readFileSync(path.join(dirPath, page.name)),
56+ options
57+ );
58+
59+ return {
60+ title: data.title,
61+ base: page.name,
62+ slug: page.name.replace(/.md$/, ""),
63+ ...(ordering === "weight" && { weight: data?.weight ?? 0 }),
64+ ...(ordering === "date" && {
65+ date: data?.date ?? "2000-01-01T00:00:00.000Z",
66+ }),
67+ };
68+ })
69+ .sort((a, b) => a.weight - b.weight),
70+ children: Object.fromEntries(
71+ subdirs
72+ .sort((a, b) => {
73+ const aMetadata = matter(
74+ fs.readFileSync(path.join(dirPath, a.name, "_index.md")),
75+ options
76+ );
77+ const bMetadata = matter(
78+ fs.readFileSync(path.join(dirPath, b.name, "_index.md")),
79+ options
80+ );
81+ return aMetadata?.data?.weight - bMetadata?.data?.weight || 0;
82+ })
83+ .map((subdir) => [
84+ subdir.name,
85+ buildPageTree(path.join(dirPath, subdir.name), ordering, content),
86+ ])
87+ ),
88+ };
89+}
90+
91+const process = require("process");
92+
93+const arg = process.argv?.[2];
94+const sort = process?.argv?.[3] || "weight";
95+
96+const dir = buildPageTree(path.join(process.cwd(), `content/${arg}`), sort);
97+const fileContents = `${JSON.stringify(dir)}`;
98+
99+try {
100+ fs.readdirSync("cache");
101+} catch (err) {
102+ fs.mkdirSync("cache");
103+}
104+
105+fs.writeFile(`cache/${arg}.json`, fileContents, function (err) {
106+ if (err) return console.error(err);
107+ console.log(`${arg} directory structure created.`);
108+});
109+
110+module.exports = { buildPageTree };
lib/lib.js−70
@@ -198,76 +198,6 @@ export function formatDate(dateTimeObject) {
198// }))
198// }))
199// }
199// }
200
200
201−/**
202− * buildPageTree is written to provide a recursive index of posts
203− * for nested directories for creating sidebars
204− * @param {string} path Absolute path to the subdirectory.
205− * @returns {Object.<string,string|Object<string,string[]|number>>} Title of the directory, child pages, and child directories (recursive structure).
206− *
207− */
208−
209−// type PageTree =
210−// { title: string,
211−// pages: [{
212−// title: string,
213−// base: string,
214−// slug: string,
215−// weight: number,
216−// }],
217−// children: { [string]: PageTree },
218−// }
219−
220−export function buildPageTree(path, ordering = "", content = false) {
221− const metadata = matter(fs.readFileSync(join(path, "_index.md")), options);
222− // get a list of contents at the path, with file types so that the item at the path can be identified as file or folder
223− const children = fs.readdirSync(path, { withFileTypes: true });
224− // get a list of files
225− const pages = children.filter((f) => f.isFile() && f.name !== "_index.md");
226− // get a list of folders
227− const subdirs = children.filter((f) => f.isDirectory());
228− // return a pagetree datastructure
229− return {
230− title: metadata.data.title,
231− pages: pages
232− .map((page) => {
233− // retrieve topmatter as JSON
234− const { content, data } = matter(
235− fs.readFileSync(join(path, page.name)),
236− options
237− );
238−
239− return {
240− title: data.title,
241− base: page.name,
242− slug: page.name.replace(/.md$/, ""),
243− ...(ordering === "weight" && { weight: data?.weight ?? 0 }),
244− ...(ordering === "date" && {
245− date: data?.date ?? "2000-01-01T00:00:00.000Z",
246− }),
247− };
248− })
249− .sort((a, b) => a.weight - b.weight),
250− children: Object.fromEntries(
251− subdirs
252− .sort((a, b) => {
253− const aMetadata = matter(
254− fs.readFileSync(join(path, a.name, "_index.md")),
255− options
256− );
257− const bMetadata = matter(
258− fs.readFileSync(join(path, b.name, "_index.md")),
259− options
260− );
261− return aMetadata?.data?.weight - bMetadata?.data?.weight || 0;
262− })
263− .map((subdir) => [
264− subdir.name,
265− buildPageTree(join(path, subdir.name), ordering, content),
266− ])
267− ),
268− };
269−}
270−
271export const getPage = (path) => {
201export const getPage = (path) => {
272 try {
202 try {
273 let fileContents = fs.readFileSync(`${path}.md`, "utf8");
203 let fileContents = fs.readFileSync(`${path}.md`, "utf8");
package.json+9−3
@@ -3,11 +3,17 @@
3 "version": "0.1.0",
3 "version": "0.1.0",
4 "private": true,
4 "private": true,
5 "scripts": {
5 "scripts": {
6− "dev": "npm run cache-posts && npm run rss && next dev",
6+ "dev": "npm run prebuild && next dev",
7− "build": "npm run cache-posts && npm run rss && next build",
7+ "build": "next build",
8− "export": "npm run cache-posts && npm run rss && next build && next export",
8+ "export": "npm run prebuild && next build && next export",
9 "start": "next start",
9 "start": "next start",
10 "prepare": "husky install",
10 "prepare": "husky install",
11+ "prebuild": "npm run cache-posts && npm run rss && npm run community-tree && npm run docs-tree && npm run getting-started-tree && npm run understanding-urbit-tree && npm run using-tree",
12+ "community-tree": "node lib/buildPageTree community weight",
13+ "docs-tree": "node lib/buildPageTree docs weight",
14+ "getting-started-tree": "node lib/buildPageTree getting-started weight",
15+ "understanding-urbit-tree": "node lib/buildPageTree understanding-urbit weight",
16+ "using-tree": "node lib/buildPageTree using weight",
11 "cache-posts": "node lib/cache.js",
17 "cache-posts": "node lib/cache.js",
12 "rss": "node lib/rss.js"
18 "rss": "node lib/rss.js"
13 },
19 },
pages/community/[[...slug]].js+5−9
@@ -4,12 +4,13 @@ import { useRouter } from "next/router";
4import { useState } from "react";
4import { useState } from "react";
5import classnames from "classnames";
5import classnames from "classnames";
6import { join } from "path";
6import { join } from "path";
7−import { getDocs, formatDate, buildPageTree, getPage } from "../../lib/lib";
7+import { getDocs, formatDate, getPage } from "../../lib/lib";
8import Meta from "../../components/Meta";
8import Meta from "../../components/Meta";
9import Markdown from "../../components/Markdown";
9import Markdown from "../../components/Markdown";
10import ContentArea from "../../components/ContentArea";
10import ContentArea from "../../components/ContentArea";
11import Sidebar from "../../components/Sidebar";
11import Sidebar from "../../components/Sidebar";
12import { decode } from "html-entities";
12import { decode } from "html-entities";
13+import communityTree from "../../cache/community.json";
13
14
14const breadcrumbs = (posts, paths) => {
15const breadcrumbs = (posts, paths) => {
15 const results = [
16 const results = [
@@ -89,10 +90,7 @@ export default function UsingLayout({ posts, data, params, search, markdown }) {
89}
90}
90
91
91export async function getStaticProps({ params }) {
92export async function getStaticProps({ params }) {
92− const posts = buildPageTree(
93+ const posts = communityTree;
93− join(process.cwd(), "content/community"),
94− "weight"
95− );
96
94
97 const { data, content } = getPage(
95 const { data, content } = getPage(
98 join(process.cwd(), "content/community", params.slug?.join("/") || "/")
96 join(process.cwd(), "content/community", params.slug?.join("/") || "/")
@@ -104,10 +102,8 @@ export async function getStaticProps({ params }) {
104}
102}
105
103
106export async function getStaticPaths() {
104export async function getStaticPaths() {
107− const posts = buildPageTree(
105+ const posts = communityTree;
108− join(process.cwd(), "content/community"),
106+
109− "weight"
110− );
111 const slugs = [];
107 const slugs = [];
112
108
113 const allHrefs = (thisLink, tree) => {
109 const allHrefs = (thisLink, tree) => {
pages/docs/[[...slug]].js+4−8
@@ -4,17 +4,13 @@ import { useRouter } from "next/router";
4import { useState, useEffect, useRef } from "react";
4import { useState, useEffect, useRef } from "react";
5import classnames from "classnames";
5import classnames from "classnames";
6import { join } from "path";
6import { join } from "path";
7−import {
8− getPreviousPost,
7+import { getPreviousPost, getNextPost, getPage } from "../../lib/lib";
9− getNextPost,
10− buildPageTree,
11− getPage,
12−} from "../../lib/lib";
13import Meta from "../../components/Meta";
8import Meta from "../../components/Meta";
14import Pagination from "../../components/Pagination";
9import Pagination from "../../components/Pagination";
15import Markdown from "../../components/Markdown";
10import Markdown from "../../components/Markdown";
16import ContentArea from "../../components/ContentArea";
11import ContentArea from "../../components/ContentArea";
17import Sidebar from "../../components/Sidebar";
12import Sidebar from "../../components/Sidebar";
13+import docsPageTree from "../../cache/docs.json";
18
14
19import { decode } from "html-entities";
15import { decode } from "html-entities";
20
16
@@ -193,7 +189,7 @@ export default function DocsLayout({
193}
189}
194
190
195export async function getStaticProps({ params }) {
191export async function getStaticProps({ params }) {
196− const posts = buildPageTree(join(process.cwd(), "content/docs"), "weight");
192+ const posts = docsPageTree;
197
193
198 const { data, content } = getPage(
194 const { data, content } = getPage(
199 join(process.cwd(), "content/docs", params.slug?.join("/") || "/")
195 join(process.cwd(), "content/docs", params.slug?.join("/") || "/")
@@ -221,7 +217,7 @@ export async function getStaticProps({ params }) {
221}
217}
222
218
223export async function getStaticPaths() {
219export async function getStaticPaths() {
224− const posts = buildPageTree(join(process.cwd(), "content/docs"), "weight");
220+ const posts = docsPageTree;
225 const slugs = [];
221 const slugs = [];
226
222
227 const allHrefs = (thisLink, tree) => {
223 const allHrefs = (thisLink, tree) => {
pages/getting-started/[[...slug]].js+5−9
@@ -4,10 +4,11 @@ import Link from "next/link";
4import { useRouter } from "next/router";
4import { useRouter } from "next/router";
5import classnames from "classnames";
5import classnames from "classnames";
6import { join } from "path";
6import { join } from "path";
7−import { buildPageTree, getPage } from "../../lib/lib";
7+import { getPage } from "../../lib/lib";
8import Markdown from "../../components/Markdown";
8import Markdown from "../../components/Markdown";
9import ContentArea from "../../components/ContentArea";
9import ContentArea from "../../components/ContentArea";
10import Sidebar from "../../components/Sidebar";
10import Sidebar from "../../components/Sidebar";
11+import GettingStartedTree from "../../cache/getting-started.json";
11
12
12import { decode } from "html-entities";
13import { decode } from "html-entities";
13
14
@@ -108,10 +109,7 @@ export default function UsingLayout({ posts, data, params, search, markdown }) {
108}
109}
109
110
110export async function getStaticProps({ params }) {
111export async function getStaticProps({ params }) {
111− const posts = buildPageTree(
112+ const posts = GettingStartedTree;
112− join(process.cwd(), "content/getting-started"),
113− "weight"
114− );
115
113
116 const { data, content } = getPage(
114 const { data, content } = getPage(
117 join(
115 join(
@@ -127,10 +125,8 @@ export async function getStaticProps({ params }) {
127}
125}
128
126
129export async function getStaticPaths() {
127export async function getStaticPaths() {
130− const posts = buildPageTree(
128+ const posts = GettingStartedTree;
131− join(process.cwd(), "content/getting-started"),
129+
132− "weight"
133− );
134 const slugs = [];
130 const slugs = [];
135
131
136 const allHrefs = (thisLink, tree) => {
132 const allHrefs = (thisLink, tree) => {
pages/understanding-urbit/[[...slug]].js+5−14
@@ -4,16 +4,12 @@ import Link from "next/link";
4import { useRouter } from "next/router";
4import { useRouter } from "next/router";
5import classnames from "classnames";
5import classnames from "classnames";
6import { join } from "path";
6import { join } from "path";
7−import {
8− buildPageTree,
9− getPage,
10− getPreviousPost,
7+import { getPage, getPreviousPost, getNextPost } from "../../lib/lib";
11− getNextPost,
12−} from "../../lib/lib";
13import Markdown from "../../components/Markdown";
8import Markdown from "../../components/Markdown";
14import ContentArea from "../../components/ContentArea";
9import ContentArea from "../../components/ContentArea";
15import Sidebar from "../../components/Sidebar";
10import Sidebar from "../../components/Sidebar";
16import Pagination from "../../components/Pagination";
11import Pagination from "../../components/Pagination";
12+import UnderstandingUrbitTree from "../../cache/understanding-urbit.json";
17
13
18import { decode } from "html-entities";
14import { decode } from "html-entities";
19
15
@@ -150,10 +146,7 @@ export default function UnderstandingLayout({
150}
146}
151
147
152export async function getStaticProps({ params }) {
148export async function getStaticProps({ params }) {
153− const posts = buildPageTree(
149+ const posts = UnderstandingUrbitTree;
154− join(process.cwd(), "content/understanding-urbit"),
155− "weight"
156− );
157
150
158 const { data, content } = getPage(
151 const { data, content } = getPage(
159 join(
152 join(
@@ -185,10 +178,8 @@ export async function getStaticProps({ params }) {
185}
178}
186
179
187export async function getStaticPaths() {
180export async function getStaticPaths() {
188− const posts = buildPageTree(
181+ const posts = UnderstandingUrbitTree;
189− join(process.cwd(), "content/understanding-urbit"),
182+
190− "weight"
191− );
192 const slugs = [];
183 const slugs = [];
193
184
194 const allHrefs = (thisLink, tree) => {
185 const allHrefs = (thisLink, tree) => {
pages/using/[[...slug]].js+4−3
@@ -5,10 +5,11 @@ import { useRouter } from "next/router";
5import { useState } from "react";
5import { useState } from "react";
6import classnames from "classnames";
6import classnames from "classnames";
7import { join } from "path";
7import { join } from "path";
8−import { getDocs, formatDate, buildPageTree, getPage } from "../../lib/lib";
8+import { getDocs, formatDate, getPage } from "../../lib/lib";
9import Markdown from "../../components/Markdown";
9import Markdown from "../../components/Markdown";
10import ContentArea from "../../components/ContentArea";
10import ContentArea from "../../components/ContentArea";
11import Sidebar from "../../components/Sidebar";
11import Sidebar from "../../components/Sidebar";
12+import UsingTree from "../../cache/using.json";
12
13
13import { decode } from "html-entities";
14import { decode } from "html-entities";
14
15
@@ -128,7 +129,7 @@ export default function UsingLayout({ posts, data, params, search, markdown }) {
128}
129}
129
130
130export async function getStaticProps({ params }) {
131export async function getStaticProps({ params }) {
131− const posts = buildPageTree(join(process.cwd(), "content/using"), "weight");
132+ const posts = UsingTree;
132
133
133 const { data, content } = getPage(
134 const { data, content } = getPage(
134 join(process.cwd(), "content/using", params.slug?.join("/") || "/")
135 join(process.cwd(), "content/using", params.slug?.join("/") || "/")
@@ -140,7 +141,7 @@ export async function getStaticProps({ params }) {
140}
141}
141
142
142export async function getStaticPaths() {
143export async function getStaticPaths() {
143− const posts = buildPageTree(join(process.cwd(), "content/using"), "weight");
144+ const posts = UsingTree;
144 const slugs = [];
145 const slugs = [];
145
146
146 const allHrefs = (thisLink, tree) => {
147 const allHrefs = (thisLink, tree) => {