Repository navigation
Expand file tree
/
Copy pathcontentlayer.config.ts
More file actions
126 lines (117 loc) · 3.94 KB
/
Copy pathcontentlayer.config.ts
File metadata and controls
126 lines (117 loc) · 3.94 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
114
115
116
117
118
119
120
121
122
123
124
125
126
import {defineDocumentType, makeSource} from 'contentlayer2/source-files';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypePrettyCode from 'rehype-pretty-code';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import shadesOfPurple from './src/styles/shades-of-purple.json';
import GithubSlugger from 'github-slugger';
import {visit} from 'unist-util-visit';
// A fence opener/closer is a run of 3+ backticks or tildes at the start of a line.
const regXFenceLine = /^\s*(`{3,}|~{3,})/;
// Remove fenced code blocks so `# comment` lines inside them are not mistaken for
// headings. Walks line by line: a naive /```[\s\S]*?```/ regex also matches inline
// backtick runs in prose (e.g. `"true```), which flips every later fence pairing.
function stripFencedCodeBlocks(markdown: string): string {
const keptLines: string[] = [];
let openFence: string | undefined;
for (const line of markdown.split('\n')) {
const fence = line.match(regXFenceLine)?.[1];
if (openFence) {
const closesOpenFence =
fence !== undefined &&
fence[0] === openFence[0] &&
fence.length >= openFence.length &&
line.trim() === fence;
if (closesOpenFence) {
openFence = undefined;
}
} else if (fence) {
openFence = fence;
} else {
keptLines.push(line);
}
}
return keptLines.join('\n');
}
export const Post = defineDocumentType(() => ({
name: 'Post',
filePathPattern: `**/*.mdx`,
contentType: 'mdx',
fields: {
title: {type: 'string', required: false},
date: {type: 'date', required: false},
seoPriority: {type: 'number', required: false},
// Preview posts are excluded in the sitemap and the search index. They return a 404
// unless the ?preview query parameter is present. This is useful for sharing early
// docs pages internally or with select early customers.
preview: {type: 'boolean', required: false}
},
computedFields: {
url: {type: 'string', resolve: post => `/${post._raw.flattenedPath}`},
headings: {
type: 'json',
resolve: async doc => {
const regXHeader = /^ *(?<flag>#{1,6})\s+(?<content>.+)/gm;
const slugger = new GithubSlugger();
const headings = Array.from(
stripFencedCodeBlocks(doc.body.raw).matchAll(regXHeader)
).map(({groups}) => {
const flag = groups?.flag;
// Handles headings with links eg:
// Converts '##[Getting started](/getting-started)' to 'Getting started'
const match = groups?.content?.match(
/\[([^\]]+)\]\([^)]+\)/
);
const title = match ? match[1] : groups?.content;
return {
level: flag?.length,
title: title,
id: title ? slugger.slug(title) : undefined
};
});
return headings;
}
}
}
}));
const prettyCodeOptions = {
keepBackground: true,
theme: shadesOfPurple
};
const rehypePlugins: any = [
// Extract raw code content from code elements within pre elements
() => (tree: any) => {
visit(tree, (node: any) => {
if (node?.type === 'element' && node?.tagName === 'pre') {
const [codeEl] = node.children;
if (codeEl.tagName !== 'code') return;
node.raw = codeEl.children?.[0].value;
}
});
},
[rehypePrettyCode, prettyCodeOptions],
// Add raw code content as a property to pre elements within div elements containing data-rehype-pretty-code-fragment attribute
() => (tree: any) => {
visit(tree, (node: any) => {
if (node?.type === 'element' && node?.tagName === 'div') {
if (!('data-rehype-pretty-code-fragment' in node.properties))
return;
for (const child of node.children)
if (child.tagName === 'pre')
child.properties['raw'] = node.raw;
}
});
},
rehypeSlug,
[rehypeAutolinkHeadings]
];
export default makeSource({
contentDirPath: 'docs',
documentTypes: [Post],
mdx: {
remarkPlugins: [remarkGfm],
rehypePlugins: rehypePlugins,
// Externalize mermaid to prevent bundling its problematic Unicode regex patterns
esbuildOptions: options => ({...options, external: [...(options.external ?? []), 'mermaid']})
}
});