Prism.js syntax highlighting in Ghost

Who doesn't like color?

The code blocks in Ghost's default theme, Casper, don't include syntax highlighting. So after a bit of Google searching, I found a solution.


Prism.js is a sort of modular JavaScript framework for loading specific types of text highlighting, ranging from programming language support to enabling line numbers, etc.

And, best of all, it's hosted on a public JavaScript CDN, cdnjs.

To enable on the blog, you need to include the prism.css file in your injected header:

(Bonus points for using the minified version (prism.min.css) which reduces the number of bytes you require your clients to download in order to execute the .css and subsequent .js files.)

And you need to declare the prism.js file in your injected footer:

(More bonus points if you use the subresource integrity (SRI) signature.)


From here, you can pick and choose which languages you want to support and paste in the associated .js files. For instance, I want to support Python, so I include the following in my footer:

<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.8.4/components/prism-python.min.js" integrity="sha256-UG8/FkBcUZvmGnPiL1hw3i/ROpxXPqIpxkEt2SVYIP4=" crossorigin="anonymous"></script>  

(This script tag is auto-generated when you hover over the cdnjs download button and choose Copy Script Tag with SRI.)


I can then do some Python!

def foo():  
  return 'bar'

Just don't forget to include which language you want to invoke by appending the string at the end of the initial code block:

def foo():  
  return 'bar'

(This is done by declaring ```python at the beginning of the code block.)


But wait, there's more!

The process up to this point is straightforward, but I would prefer to be on the edge of Prism.js's versioning. For example, when I first started writing this post, 1.8.4 was latest. But at publishing time, it has bumped to 1.9.0, which necessitates changing 👏 every 👏 damn 👏 injected 👏 link 👏.

So I'm going to brush up on my JavaScript skills and inject a function which determines the latest version, and scrapes each language's latest version.

I'd also like to just maintain a list of languages I desire to support:

langs = ['python', 'bash', 'hack', 'java']  

Which would unpack into the proper cdnjs links:

<script src=".../prism-python.min.js"></script>  
<script src=".../prism-bash.min.js"></script>  
<script src=".../prism-hack.min.js"></script>  
<script src=".../prism-java.min.js"></script>  

Why JavaScript, why?

So the idea of HTTP GET'ing the Prism.js's cdnjs page to calculate the latest version and make subsequent requests was stopped on account of violating the Same-origin Policy. sigh

I also ran into a wall when trying to dynamically add <script> tags to the page body with really weird race conditions manifesting. I read some interesting articles about how specific elements are loaded into the page and what controlling mechanisms there are, but I didn't have the time to debug further. So out with dynamic, and in with explicit.

<script>  
    ['', 'bash', 'java', 'javascript', 'php', 'powershell', 'python'].forEach(
        function(lang) {
            var element = document.createElement('script');
            if (!lang) {
                // Required module to load before each language is supported.
                element.setAttribute('src', 'https://cdn.jsdelivr.net/npm/[email protected]/prism.min.js');
            } else {
                element.setAttribute('src', `https://cdn.jsdelivr.net/npm/[email protected]/components/prism-${lang}.min.js`);
            }
            document.body.appendChild(element);
        });
</script>  

(For reference, the logic I was trying to support.)

The one piece of good news is that in all of this I found a different JavaScript CDN which does support the latest feature I was originally going to code myself, jsDeliver:

<script src="https://cdn.jsdelivr.net/npm/prismjs@latest/prism.min.js"></script>  

But even more neato is that it supports "version aliasing", which means you get the latest version, of say @1, which should contain all the bug fixes of that major version:

<script src="https://cdn.jsdelivr.net/npm/prismjs@1/prism.min.js"></script>  


Update (8/3/2018)

Highlighting broke and it turns out an additional file was needed:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-markup-templating.min.js"></script>