Skip to content

Instantly share code, notes, and snippets.

@sjorsrijsdam
Last active February 23, 2024 17:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjorsrijsdam/680da864164bfd1271eab7e2a2656a02 to your computer and use it in GitHub Desktop.
Save sjorsrijsdam/680da864164bfd1271eab7e2a2656a02 to your computer and use it in GitHub Desktop.
Server-side code highlighting for Enhance.dev

Server-side highlighting of code in HTML

Basically you place you code examples in your HTML code in the <code-example> custom element with a language attribute for the language. Then, in the API handler you run the entire HTML blob through the highlightCode() function. This will use the existing @begin/parse5 package to parse the HTML. It will then traverse the DOM tree, find the <code-example> elements, do PrismJS magic on its contents and then places them back. At the custom element expansion stage the highlighted code gets put in the <pre> & <code> and adds the correct class name based on the language attribute of the <code-example> element.


Screenshot 2024-02-23 141917
<p>
    Morbi nec tincidunt nunc, eget convallis eros. Phasellus lacus orci, pharetra eu orci eu, convallis luctus magna.
    Pellentesque dictum ante a sodales suscipit. Aliquam luctus ultrices consectetur. Proin quam magna, ullamcorper
    vel quam a, ornare ornare nisl. Pellentesque eu ligula luctus, elementum massa et, ornare enim. Vestibulum
    placerat urna urna, nec iaculis mi pulvinar quis. Nam luctus, erat ullamcorper lobortis efficitur, velit justo
    blandit libero, eu imperdiet ex odio vitae tortor. In velit ipsum, finibus eget dolor eget, facilisis consectetur
    ex. Donec vitae dapibus nisl. In sollicitudin, purus ac tristique aliquet, justo neque convallis eros, eget
    sodales metus enim tristique sapien. Phasellus feugiat sodales vehicula. Pellentesque habitant morbi tristique
    senectus et netus et malesuada fames ac turpis egestas. Cras tincidunt ultrices eros et rutrum. Donec nibh nunc,
    ullamcorper vitae ex at, facilisis mollis ligula. Sed non fermentum eros.
</p>
<code-example language="javascript">
    function helloWorld () {
        console.log('Hello, world!');
    }
</code-example> 
<p>
    Fusce imperdiet consequat mattis. Cras venenatis nulla magna, nec pellentesque libero molestie at. Proin eu elit
    ac odio semper faucibus et in felis. Etiam efficitur sit amet nisi vitae varius. Mauris risus odio, efficitur
    quis nisi a, tincidunt placerat enim. Ut condimentum erat at lorem suscipit, vel semper mi imperdiet.
    Pellentesque tempor laoreet leo in tincidunt. Vestibulum ut ullamcorper urna.
</p>
<code-example language="html">
    <header>
        <h1>This a header</h1>
    </header>
</code-example> 
<p>
    Quisque erat elit, tincidunt a massa in, malesuada dictum felis. Quisque pretium bibendum massa, id gravida massa
    consectetur et. Duis at diam nisi. Duis sed nibh feugiat, congue orci et, pulvinar augue. Nunc neque neque, porta
    posuere justo sit amet, luctus pretium mauris. Donec nulla erat, suscipit id quam eget, finibus aliquet nisl.
    Donec dui lacus, euismod non tellus at, rutrum venenatis erat. Proin congue tempor purus, eu blandit purus feugiat
    sit amet. Fusce tempor leo eget orci rutrum, ac molestie turpis porttitor. Proin eget tempus nunc. Aenean in
    vestibulum leo, ac hendrerit eros.
</p>
import { highlightCode } from './highlightCode.mjs';
/**
* @type {import('@enhance/types').EnhanceApiFn}
*/
export async function get (req) {
let { slug } = req.params;
let post = await getItem(slug);
post.body = highlightCode(post.body);
return {
'json': {
post,
},
};
}
/**
* @type {import('@enhance/types').EnhanceElemFn}
*/
export default function codeExample ({ html, state }) {
return html`
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism-okaidia.min.css">
<style>
:host {
display: block;
margin: 1rem 0;
background-color: #000;
padding: 1rem;
}
</style>
<pre><code class="language-${state.attrs.language}"><slot></slot></code></pre>
`;
}
import { parse, serialize, fragment as parseFragment } from '@begin/parse5';
import Prismjs from 'prismjs';
import Normalizer from 'prismjs/plugins/normalize-whitespace/prism-normalize-whitespace.js';
function processChildNodes (childNodes) {
const normalizer = new Normalizer({
'remove-trailing': true,
'remove-indent': true,
'left-trim': true,
'right-trim': true,
});
for (let i = 0; i < childNodes.length; i++) {
let node = childNodes[i];
if (node.nodeName === 'code-example') {
let innerHTML = serialize(node);
let languageAttr = node.attrs.find(attr => attr.name === 'language');
let language = languageAttr ? languageAttr.value : 'html';
let normalizedHTML = normalizer.normalize(innerHTML);
let highlighterInnerHTML = Prismjs.highlight(normalizedHTML, Prismjs.languages[language], language);
let highlightedInnerDOM = parseFragment(highlighterInnerHTML);
for (let j = 0; j < highlightedInnerDOM.childNodes.length; j++) {
let childNode = highlightedInnerDOM.childNodes[j];
childNode.parentNode = node;
}
node.childNodes = highlightedInnerDOM.childNodes;
continue;
}
if (node.childNodes) {
processChildNodes(node.childNodes);
}
}
}
export function highlightCode (html) {
let parsedHTML = parse(html);
processChildNodes(parsedHTML.childNodes);
let serializedHTML = serialize(parsedHTML);
let split1 = serializedHTML.split('<body>');
let split2 = split1[1].split('</body>');
return split2[0];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment