Nightly Build / build (push) Successful in 8s
Vendor highlight.js (core + python, javascript, typescript, json, yaml, bash) and highlight the file viewer's text kind (computed once per load) plus fenced blocks in renderMarkdown. Colors come from new --syn-* CSS variables aliased to the existing palette, so dark mode follows.
73 lines
2.9 KiB
JavaScript
73 lines
2.9 KiB
JavaScript
import hljs from '../vendor/hljs/core.min.js';
|
|
import DOMPurify from 'dompurify';
|
|
import python from '../vendor/hljs/languages/python.min.js';
|
|
import javascript from '../vendor/hljs/languages/javascript.min.js';
|
|
import typescript from '../vendor/hljs/languages/typescript.min.js';
|
|
import json from '../vendor/hljs/languages/json.min.js';
|
|
import yaml from '../vendor/hljs/languages/yaml.min.js';
|
|
import bash from '../vendor/hljs/languages/bash.min.js';
|
|
|
|
hljs.registerLanguage('python', python);
|
|
hljs.registerLanguage('javascript', javascript);
|
|
hljs.registerLanguage('typescript', typescript);
|
|
hljs.registerLanguage('json', json);
|
|
hljs.registerLanguage('yaml', yaml);
|
|
hljs.registerLanguage('bash', bash);
|
|
|
|
// File extension → hljs language. Extensions not listed here render as plain
|
|
// text (never auto-detected: guessing wrong is worse than no colors).
|
|
const LANG_FOR_EXT = {
|
|
py: 'python',
|
|
js: 'javascript', mjs: 'javascript', cjs: 'javascript', jsx: 'javascript',
|
|
ts: 'typescript', tsx: 'typescript',
|
|
json: 'json',
|
|
yml: 'yaml', yaml: 'yaml',
|
|
sh: 'bash', bash: 'bash', zsh: 'bash', fish: 'bash',
|
|
};
|
|
|
|
// Markdown fence info string → hljs language (common aliases included).
|
|
const LANG_FOR_FENCE = {
|
|
py: 'python', python: 'python',
|
|
js: 'javascript', jsx: 'javascript', mjs: 'javascript', cjs: 'javascript', javascript: 'javascript',
|
|
ts: 'typescript', tsx: 'typescript', typescript: 'typescript',
|
|
json: 'json',
|
|
yml: 'yaml', yaml: 'yaml',
|
|
sh: 'bash', bash: 'bash', shell: 'bash', zsh: 'bash',
|
|
};
|
|
|
|
export function codeLangForExt(ext) {
|
|
return LANG_FOR_EXT[ext] ?? null;
|
|
}
|
|
|
|
/**
|
|
* Highlight `code` as `lang`, returning sanitized HTML (hljs escapes the input
|
|
* itself; DOMPurify is belt-and-braces). Returns null when the language is not
|
|
* registered, so callers can fall back to plain text.
|
|
*/
|
|
export function highlightCode(code, lang) {
|
|
if (!lang || !hljs.getLanguage(lang)) return null;
|
|
return DOMPurify.sanitize(hljs.highlight(code, { language: lang }).value);
|
|
}
|
|
|
|
/**
|
|
* Post-process sanitized marked HTML: highlight every `<pre><code
|
|
* class="language-…">` whose fence maps to a registered language, in place.
|
|
* Unknown fences are left as the plain (already escaped) text marked emitted.
|
|
*/
|
|
export function highlightMarkdownCodeBlocks(htmlStr) {
|
|
if (!htmlStr.includes('language-')) return htmlStr;
|
|
const tpl = document.createElement('template');
|
|
tpl.innerHTML = htmlStr;
|
|
let changed = false;
|
|
for (const code of tpl.content.querySelectorAll('pre > code[class*="language-"]')) {
|
|
const fence = [...code.classList].find(c => c.startsWith('language-'))?.slice(9);
|
|
const lang = LANG_FOR_FENCE[fence];
|
|
const out = lang && highlightCode(code.textContent ?? '', lang);
|
|
if (!out) continue;
|
|
code.innerHTML = out;
|
|
code.classList.add('hljs');
|
|
changed = true;
|
|
}
|
|
return changed ? tpl.innerHTML : htmlStr;
|
|
}
|