Files
dmz/vite-plugin/vuero-doc/markdown.ts
2025-05-24 01:47:40 +09:00

83 lines
2.2 KiB
TypeScript

import yaml from 'js-yaml'
import rehypeExternalLinks from 'rehype-external-links'
import rehypeRaw from 'rehype-raw'
import rehypeSlug from 'rehype-slug'
import rehypeAutolinkHeadings from 'rehype-autolink-headings'
import rehypeStringify from 'rehype-stringify'
import remarkParse from 'remark-parse'
import remarkGfm from 'remark-gfm'
import remarkRehype from 'remark-rehype'
import remarkFrontmatter from 'remark-frontmatter'
import rehypeShiki from '@shikijs/rehype'
import type {
ThemeRegistration,
ThemeRegistrationRaw,
BuiltinLanguage,
BuiltinTheme,
StringLiteralUnion,
} from 'shiki'
import { unified } from 'unified'
import type { Literal, Parent } from 'unist'
const langs = [
'vue',
'vue-html',
'typescript',
'bash',
'scss',
] satisfies BuiltinLanguage[]
export async function createProcessor(
themes: Partial<
Record<
string,
ThemeRegistration | ThemeRegistrationRaw | StringLiteralUnion<BuiltinTheme>
>
>,
) {
return unified()
.use(remarkParse)
.use(remarkFrontmatter)
.use(() => (tree, file) => {
if ('children' in tree) {
const parent = tree as Parent
if (parent.children[0].type === 'yaml') {
// store frontmatter in vfile
const value = (parent.children[0] as Literal).value
file.data.frontmatter = typeof value === 'string' ? yaml.load(value) : undefined
}
}
})
.use(remarkGfm)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeRaw)
.use(rehypeShiki, {
themes,
langs,
})
.use(rehypeExternalLinks, { rel: ['nofollow'], target: '_blank' })
.use(rehypeSlug)
.use(rehypeAutolinkHeadings, {
behavior: 'append',
content: {
type: 'element',
tagName: 'iconify-icon',
properties: {
className: ['iconify toc-link-anchor'],
icon: 'lucide:link',
},
children: [],
},
test: (node) => {
if (
Array.isArray(node.properties?.className)
&& node.properties?.className?.includes('toc-ignore')
) {
return false
}
return Boolean(node.properties?.id)
},
})
.use(rehypeStringify)
}