diff options
| author | Nantha Sorubakanthan <nantha@mielota.com> | 2025-11-03 19:58:32 +0100 |
|---|---|---|
| committer | Nantha Sorubakanthan <nantha@mielota.com> | 2025-11-03 19:58:32 +0100 |
| commit | 285b2f67e967286019f0f29d1f0921134347a586 (patch) | |
| tree | 77ba381c9a9bb71054c61801edd7b2cfb09f5788 | |
| parent | f32c2e84104dbcae287c7563c835d2f20c8393f1 (diff) | |
Syntax highlighting from javascript script
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | package.json | 13 | ||||
| -rw-r--r-- | scripts/highlight.mjs | 57 |
3 files changed, 72 insertions, 0 deletions
@@ -1,2 +1,4 @@ .hugo_build.lock public/ +node_modules/ +package-lock.json diff --git a/package.json b/package.json new file mode 100644 index 0000000..0d3ac30 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "build": "hugo && npm run highlight", + "highlight": "node scripts/highlight.mjs" + }, + "dependencies": { + "dom-serializer": "^2.0.0", + "domutils": "^3.2.2", + "glob": "^11.0.3", + "htmlparser2": "^10.0.0", + "shiki": "^3.14.0" + } +} diff --git a/scripts/highlight.mjs b/scripts/highlight.mjs new file mode 100644 index 0000000..2870866 --- /dev/null +++ b/scripts/highlight.mjs @@ -0,0 +1,57 @@ +import { createHighlighter, bundledLanguages } from 'shiki'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { readFile, writeFile } from 'node:fs/promises'; +import { glob } from 'glob'; +import { parseDocument } from 'htmlparser2'; +import { findAll, findOne, textContent, replaceElement } from 'domutils'; +import render from 'dom-serializer'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +main(); + +async function main() { + const LIGHT_THEME = 'github-dark'; + const DARK_THEME = LIGHT_THEME; + + const start = Date.now(); + + const highlighter = await createHighlighter({ + themes: [LIGHT_THEME, DARK_THEME], + langs: Object.keys(bundledLanguages), + }); + + const files = await glob(join(__dirname, '../public/**/**/index.html')); + await Promise.all( + files.map((filepath) => highlightFile(highlighter, filepath, { lightTheme: LIGHT_THEME, darkTheme: DARK_THEME })) + ); + + console.log(`Highlighted ${files.length} files in ${Math.round(Date.now() - start)} ms`); +} + +async function highlightFile(highlighter, filepath, { lightTheme, darkTheme }) { + const contents = await readFile(filepath, { encoding: 'utf-8' }); + await writeFile(filepath, highlightHtmlContent(highlighter, contents, { lightTheme, darkTheme })); +} + +function highlightHtmlContent(highlighter, htmlContent, { lightTheme, darkTheme }) { + const doc = parseDocument(htmlContent); + for (const preNode of findAll((e) => e.name === 'pre', doc.children)) { + const codeNode = findOne((e) => e.name === 'code', preNode.children); + if (!codeNode) continue; + + const lang = codeNode.attribs['class']?.replace(/^language-/, '') ?? 'text'; + const code = textContent(codeNode); + const highlighted = highlighter.codeToHtml(code, { + lang, + themes: { light: lightTheme, dark: darkTheme }, + }); + + const highlightedPreNode = parseDocument(highlighted).children[0]; + replaceElement(preNode, highlightedPreNode); + } + + return render(doc); +} + |
