mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
import { customAlphabet } from 'nanoid'
|
|
import { bundledLanguages, createHighlighter } from 'shiki'
|
|
import { logger } from 'vuepress/utils'
|
|
import { getLanguage } from './getLanguage.js'
|
|
import { baseTransformers, getInlineTransformers } from './transformers.js'
|
|
import type { HighlighterOptions, ThemeOptions } from '../types.js'
|
|
|
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10)
|
|
const mustacheRE = /\{\{.*?\}\}/g
|
|
|
|
export async function highlight(
|
|
theme: ThemeOptions,
|
|
options: HighlighterOptions,
|
|
): Promise<(str: string, lang: string, attrs: string) => string> {
|
|
const {
|
|
defaultHighlightLang: defaultLang = '',
|
|
codeTransformers: userTransformers = [],
|
|
whitespace = false,
|
|
languages = Object.keys(bundledLanguages),
|
|
} = options
|
|
|
|
const highlighter = await createHighlighter({
|
|
themes:
|
|
typeof theme === 'object' && 'light' in theme && 'dark' in theme
|
|
? [theme.light, theme.dark]
|
|
: [theme],
|
|
langs: languages,
|
|
langAlias: options.languageAlias,
|
|
})
|
|
|
|
await options.shikiSetup?.(highlighter)
|
|
|
|
const loadedLanguages = highlighter.getLoadedLanguages()
|
|
|
|
return (str: string, language: string, attrs: string = '') => {
|
|
const lang = getLanguage(loadedLanguages, language, defaultLang)
|
|
|
|
const enabledTwoslash = attrs.includes('twoslash') && !!options.twoslash
|
|
|
|
const mustaches = new Map<string, string>()
|
|
str = removeMustache(str, mustaches).trimEnd()
|
|
|
|
try {
|
|
const highlighted = highlighter.codeToHtml(str, {
|
|
lang,
|
|
transformers: [
|
|
...baseTransformers,
|
|
...getInlineTransformers({ attrs, lang, enabledTwoslash, whitespace }),
|
|
...userTransformers,
|
|
],
|
|
meta: { __raw: attrs },
|
|
...(typeof theme === 'object' && 'light' in theme && 'dark' in theme
|
|
? { themes: theme, defaultColor: false }
|
|
: { theme }),
|
|
})
|
|
|
|
const rendered = restoreMustache(highlighted, mustaches, enabledTwoslash)
|
|
|
|
return rendered
|
|
}
|
|
catch (e) {
|
|
logger.error(e)
|
|
return str
|
|
}
|
|
}
|
|
}
|
|
|
|
function removeMustache(s: string, mustaches: Map<string, string>) {
|
|
return s.replace(mustacheRE, (match) => {
|
|
let marker = mustaches.get(match)
|
|
if (!marker) {
|
|
marker = nanoid()
|
|
mustaches.set(match, marker)
|
|
}
|
|
return marker
|
|
})
|
|
}
|
|
|
|
function restoreMustache(s: string, mustaches: Map<string, string>, twoslash: boolean) {
|
|
mustaches.forEach((marker, match) => {
|
|
s = s.replaceAll(marker, match)
|
|
})
|
|
|
|
if (twoslash)
|
|
s = s.replace(/\{/g, '{')
|
|
|
|
return `${s}\n`
|
|
}
|