refactor: v2
This commit is contained in:
120
src/lib/processHtml.ts
Normal file
120
src/lib/processHtml.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import {JSDOM} from 'jsdom'
|
||||
import {createHighlighter} from 'shiki'
|
||||
|
||||
const DEFAULT_THEME = 'github-dark-dimmed'
|
||||
|
||||
const highlighter = await createHighlighter({
|
||||
themes: [DEFAULT_THEME],
|
||||
langs: [
|
||||
"bash",
|
||||
"json",
|
||||
"yaml",
|
||||
"xml",
|
||||
"html",
|
||||
"css",
|
||||
"scss",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"jsx",
|
||||
"tsx",
|
||||
"vue",
|
||||
"markdown",
|
||||
"sql",
|
||||
"python",
|
||||
"go",
|
||||
"dockerfile",
|
||||
"nginx"
|
||||
]
|
||||
})
|
||||
|
||||
const resolveLang = (lang: string) => {
|
||||
const supported = highlighter.getLoadedLanguages?.() || []
|
||||
if (!lang) return 'text'
|
||||
if (supported.includes(lang)) return lang
|
||||
return 'text'
|
||||
}
|
||||
|
||||
export const processHtmlWithJsdom = async (rawHtml: string) => {
|
||||
|
||||
const dom = new JSDOM(rawHtml)
|
||||
const document = dom.window.document
|
||||
|
||||
// 高亮处理代码块
|
||||
const codeBlocks = document.querySelectorAll('pre code')
|
||||
|
||||
for (const codeBlock of codeBlocks) {
|
||||
const preEl = codeBlock.parentElement
|
||||
if (!preEl || preEl.tagName !== 'PRE') continue
|
||||
|
||||
const codeContent = codeBlock.textContent || ''
|
||||
let lang = 'text'
|
||||
for (const cls of codeBlock.classList) {
|
||||
if (cls.startsWith('language-')) {
|
||||
lang = cls.slice('language-'.length)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lang = resolveLang(lang)
|
||||
|
||||
// 高亮
|
||||
const highlighted = highlighter.codeToHtml(codeContent, {
|
||||
lang,
|
||||
theme: DEFAULT_THEME,
|
||||
})
|
||||
|
||||
// 替换 DOM
|
||||
const wrapper = document.createElement('div')
|
||||
wrapper.innerHTML = highlighted.trim()
|
||||
|
||||
const newNode = wrapper.firstElementChild
|
||||
if (newNode && preEl.parentNode) {
|
||||
const container = document.createElement('div')
|
||||
container.className = 'code-block'
|
||||
|
||||
const header = document.createElement('div')
|
||||
header.className = 'code-header'
|
||||
|
||||
const headerLeft = document.createElement('div')
|
||||
headerLeft.className = 'code-header-left'
|
||||
|
||||
const headerRight = document.createElement('div')
|
||||
headerRight.className = 'code-header-right'
|
||||
|
||||
// 装饰点
|
||||
const actions = document.createElement('div')
|
||||
actions.className = 'code-actions'
|
||||
|
||||
const close = document.createElement('span')
|
||||
close.className = 'dot close'
|
||||
|
||||
const minimize = document.createElement('span')
|
||||
minimize.className = 'dot minimize'
|
||||
|
||||
const maximize = document.createElement('span')
|
||||
maximize.className = 'dot maximize'
|
||||
|
||||
actions.append(close, minimize, maximize)
|
||||
|
||||
// 显示语言
|
||||
const langDisplay = document.createElement('div')
|
||||
langDisplay.className = 'code-language'
|
||||
langDisplay.textContent = lang ? lang : 'text'
|
||||
|
||||
const copyBtn = document.createElement('div')
|
||||
copyBtn.className = 'code-copy-btn'
|
||||
copyBtn.textContent = '复制代码'
|
||||
|
||||
headerLeft.append(actions, langDisplay)
|
||||
headerRight.append(copyBtn)
|
||||
|
||||
header.append(headerLeft, headerRight)
|
||||
|
||||
container.append(header, newNode)
|
||||
|
||||
preEl.parentNode.replaceChild(container, preEl)
|
||||
}
|
||||
}
|
||||
|
||||
return dom.serialize()
|
||||
}
|
||||
Reference in New Issue
Block a user