From 285b2f67e967286019f0f29d1f0921134347a586 Mon Sep 17 00:00:00 2001 From: Nantha Sorubakanthan Date: Mon, 3 Nov 2025 19:58:32 +0100 Subject: Syntax highlighting from javascript script --- .gitignore | 2 ++ package.json | 13 ++++++++++++ scripts/highlight.mjs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 package.json create mode 100644 scripts/highlight.mjs diff --git a/.gitignore b/.gitignore index c4b387a..191d1a1 100644 --- a/.gitignore +++ b/.gitignore @@ -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); +} + -- cgit v1.2.3