Prism serverside syntax highlighter

Store highlighted code on the server

2024-12-22
DockerImageOpen source

Github workflow status

GitHub: https://github.com/sikora507/PrismJsContainerized

Docker Hub: https://hub.docker.com/r/sikora507/prismjs-containerized

docker pull sikora507/prismjs-containerized

Problem

When using a tool like Prism the code is first loaded from the server and then the syntax is being highlighted. That can cause a short flash when the style of the loaded content suddenly chantes while it's being transformed on the client side.

Backend alternatives

There were no great backend alternatives after doing some research and testing. For C# developers there are some NuGet packages like ColorfulCode but they fully render style attributes on the backend.

Solution

I wanted to have the power of Prism library with immediate syntax highlight, straight from the server. I wanted to use css classes instead of style attributes, which would allow me to support light and dark themes and would require less data to load.

So I've containerized Prism in a docker image with Node.js and am using it as a standalone service. My main backend calls this service and gets the syntax highlighted back, which I can then save to the database. By referencing Prism CSS file and getting already decorated code, I have pretty syntax loaded immediately with the rest of the site content.

This site is an example

I am using the containerized backend Prism service to highlight the code on this website before saving it to the database.

C# service utilizing containerized Prism JS endpoint

using System.Net.Http.Headers;
using Microsoft.Extensions.Options;
using Prognetdev.Configuration;

namespace Prognetdev.Services;
public interface ISyntaxHighlighterService
{
    Task<string> Highlight(string code);
}

public class SyntaxHighlighterService : ISyntaxHighlighterService
{
    private readonly HttpClient _httpClient;
    
    public SyntaxHighlighterService(
        IOptions<SyntaxHighlighterOptions> options, 
        HttpClient httpClient)
    {
        // configure http client base address from options
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri(options.Value.Url);
    }
    
    public async Task<string> Highlight(string code)
    {
        // call /highlight endpoint
        var request = new HttpRequestMessage(HttpMethod.Post, 
            "/highlight");
        request.Content = new StringContent(code);
        request.Content.Headers.ContentType = 
            new MediaTypeHeaderValue("text/html");
        
        var response = await _httpClient.SendAsync(request);
        if (response.IsSuccessStatusCode)
        {
            // Assuming the service returns
            // the highlighted HTML content directly
            var highlightedCode = 
                await response.Content.ReadAsStringAsync();
            return highlightedCode;
        }
        // Handle error or unsuccessful status code as needed
        throw new Exception(
            "Failed to highlight code. Status code: " 
            + response.StatusCode);
    }
}