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 > >, ) { 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) }