UNPKG

@scalar/code-highlight

Version:

Central methods and themes for code highlighting in Scalar projects

102 lines (99 loc) 3.64 kB
import { toText } from 'hast-util-to-text'; import { createLowlight } from 'lowlight'; import { visit } from 'unist-util-visit'; import { lowlightLanguageMappings } from '../constants.js'; const emptyOptions = {}; /** * Lowlight syntax highlighting plugin for rehype pipelines * * Derived from: @url https://github.com/rehypejs/rehype-highlight/blob/main/lib/index.js */ function rehypeHighlight(options) { const settings = options || emptyOptions; const aliases = settings.aliases; const detect = options?.detect ?? false; const languages = settings.languages; const plainText = settings.plainText; const prefix = settings.prefix; const subset = settings.subset; let name = 'hljs'; // Create a lowlight instance if not provided const lowlight = options?.lowlight ?? createLowlight(languages); if (aliases) { lowlight.registerAlias(aliases); } if (prefix) { const pos = prefix.indexOf('-'); name = pos > -1 ? prefix.slice(0, pos) : prefix; } /** Transform.*/ return (tree, file) => { visit(tree, 'element', (node, _, parent) => { if (node.tagName !== 'code' || !parent || parent.type !== 'element' || parent.tagName !== 'pre') { return; } const lang = language(node); if (lang === 'no-highlight' || (!lang && !detect) || (lang && plainText?.includes(lang))) { return; } if (!Array.isArray(node.properties.className)) { node.properties.className = []; } if (!node.properties.className.includes(name)) { node.properties.className.unshift(name); } let result; try { result = lang ? lowlight.highlight(lang, toText(parent), { prefix }) : lowlight.highlightAuto(toText(parent), { prefix, subset }); } catch (error) { const cause = error; if (lang && /Unknown language/.test(cause.message)) { file.message('Cannot highlight as `' + lang + '`, it’s not registered', { ancestors: [parent, node], cause, place: node.position, ruleId: 'missing-language', source: 'rehype-highlight', }); /* c8 ignore next 5 -- throw arbitrary hljs errors */ return; } throw cause; } if (!lang && result.data?.language) { node.properties.className.push('language-' + result.data.language); } if (result.children.length > 0) { node.children = result.children; } }); }; } /** Get the programming language of `node` or an empty string */ function language(node) { const list = node.properties.className; if (!Array.isArray(list)) { return ''; } const name = list.reduce((result, _item) => { if (result) { return result; } const item = String(_item); if (item === 'no-highlight' || item === 'nohighlight') { return 'no-highlight'; } if (item.slice(0, 5) === 'lang-') { return item.slice(5); } if (item.slice(0, 9) === 'language-') { return item.slice(9); } return result; }, ''); return lowlightLanguageMappings[name || ''] || name; } export { rehypeHighlight };