-
Notifications
You must be signed in to change notification settings - Fork 57
Expand file tree
/
Copy pathretrieveMdPages.ts
More file actions
113 lines (103 loc) · 3.25 KB
/
retrieveMdPages.ts
File metadata and controls
113 lines (103 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import fs from "fs/promises";
import { join } from "path";
import { format } from "date-fns";
import matter from "gray-matter";
import { unified } from "unified";
import parse from "remark-parse";
import remarkGfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import rehypeSlug from "rehype-slug";
import remarkHeadings, { hasHeadingsData } from "@vcarl/remark-headings";
import { toString } from "mdast-util-to-string";
const loadMd = async (path: string) => {
const fullPath = join(process.cwd(), `${path}.md`);
const fileContents = await fs.readFile(fullPath, "utf8");
return matter(fileContents);
};
const stripSuffix = (filename: string, suffix: string) =>
filename.replace(new RegExp(`\.${suffix}`), "");
const remarkTextProcessor = unified().use(parse).use(remarkHeadings);
export const processMdPlaintext = (mdSource: string) => {
const vfile = remarkTextProcessor.parse(mdSource);
const html = toString(vfile).replaceAll(/<a.*> /g, "");
if (hasHeadingsData(vfile.data)) {
return { html: html, headings: vfile.data.headings };
}
return { html: html, headings: [] };
};
const remarkHtmlProcessor = unified()
.use(parse)
.use(remarkGfm)
.use(remarkHeadings)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeSlug)
.use(rehypeStringify, { allowDangerousHtml: true });
export const processMd = (mdSource: string) => {
const vfile = remarkHtmlProcessor.processSync(mdSource);
if (hasHeadingsData(vfile.data)) {
return { html: vfile.toString(), headings: vfile.data.headings };
}
return { html: vfile.toString(), headings: [] };
};
export const loadAllMd = async <DocType>(
directory: string,
): Promise<DocType[]> => {
const postsDirectory = join(process.cwd(), directory);
const slugs = await fs
.readdir(postsDirectory)
.then((files) =>
files.filter((f) => f.endsWith(".md")).map((x) => stripSuffix(x, "md")),
);
return Promise.all(
slugs.map((slug) => loadMdBySlug<DocType>(directory, slug)),
);
};
export interface Transcript {
content: string;
slug: string;
date: string;
time: string;
title: string;
description: string;
location: string;
people: string;
[k: string]: string | boolean | undefined;
}
export interface MdPage {
content: string;
title: string;
description?: string;
sidebar?: boolean;
[k: string]: string | boolean | undefined;
}
export interface BlogPost {
content: string;
slug: string;
title: string;
author: string;
date: string;
description: string;
}
export const loadMdBySlug = async <DocType>(
directory: string,
slug: string,
) => {
const { data, content } = await loadMd(join(directory, slug));
const mapped = Object.entries(data).map((pair) => {
// Next doesn't like getting Date instances, so strip them to strings
// It's really annoying that the parser doesn't let us do this
if (pair[1] instanceof Date) {
return [pair[0], format(pair[1], "yyyy-MM-dd")];
}
return pair;
});
mapped.push(["slug", slug]);
mapped.push(["content", content]);
return Object.fromEntries(mapped) as DocType;
};
export const loadRoadmap = async <T>(directory: string, slug: string) => {
const out = await loadMdBySlug(directory, slug);
// …
return out as T;
};