-
Notifications
You must be signed in to change notification settings - Fork 0
/
frontmatter.ts
43 lines (40 loc) · 1.57 KB
/
frontmatter.ts
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
const rFrontmatterFile = /^\s*\/\*\*(?<comment>.+?)\*\/\s*(?<body>.*)$/s
const rLeadingAsterisk = /^\s*(?:\* ?)?/gm
const rTagStart = /^(?=@\w+)/m
const rTag = /^@(?<key>\w+)(\s+(?<value>.*))?$/s
export function parseFrontmatter<F extends Record<string, unknown>>(
pathname: string,
src: string,
parsers: FrontmatterParsers<F>,
): ParseFrontmatterResult<F> {
const fileMatch = rFrontmatterFile.exec(src)
if (!fileMatch) throw new Error(`Could not extract module comment from "${pathname}".`)
const { comment = "", body = "" } = fileMatch.groups ?? {}
const commentContent = comment.replace(rLeadingAsterisk, "").trim()
const tagsText = commentContent.split(rTagStart)
const frontmatterRaw = Object.fromEntries(
tagsText.map((pairText) => {
const tagMatch = rTag.exec(pairText)
if (!tagMatch) throw new Error(`Error when attempting to match tag in "${pathname}"`)
const { key = "", value = "" } = tagMatch.groups ?? {}
return [key, value.trim()]
}),
)
const frontmatter = {} as F
for (const [key, parse] of Object.entries(parsers)) {
try {
frontmatter[key as keyof F] = parse(frontmatterRaw[key])
} catch (e) {
throw new Error(`Failed to parse "${key}" from "${pathname}"\n${Deno.inspect(e)}`)
}
}
return { frontmatter, body }
}
export type FrontmatterParsers<F extends Record<string, unknown>> = {
[K in keyof F]: FrontmatterParser<F[K]>
}
export type FrontmatterParser<T> = (raw: string | undefined) => T
export interface ParseFrontmatterResult<F extends Record<string, unknown>> {
frontmatter: F
body: string
}