-
Notifications
You must be signed in to change notification settings - Fork 109
Expand file tree
/
Copy pathcontentlayer.config.ts
More file actions
153 lines (143 loc) · 4.53 KB
/
Copy pathcontentlayer.config.ts
File metadata and controls
153 lines (143 loc) · 4.53 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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import {defineDocumentType, makeSource} from 'contentlayer2/source-files';
import fs from 'fs';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import rehypePrettyCode from 'rehype-pretty-code';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import {MDXDocument, allCoreContent} from './src/utils/contentlayer';
import {searchMetadata} from './src/data/search';
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;
}
}
}
}));
function createSearchIndex(allPosts: MDXDocument[]) {
if (
searchMetadata?.provider === 'kbar' &&
searchMetadata.kbarConfig.searchDocumentsPath
) {
fs.writeFileSync(
`public/search.json`,
JSON.stringify(allCoreContent(allPosts))
);
console.log('Search index generated...');
}
}
const prettyCodeOptions = {
keepBackground: true,
theme: JSON.parse(
fs.readFileSync(
new URL(
'./../../../src/styles/shades-of-purple.json',
import.meta.url
),
'utf-8'
)
)
};
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']})
},
onSuccess: async importData => {
const {allPosts} = await importData();
createSearchIndex(allPosts);
}
});