Skip to content

Syntax highlighting for AsciiDoc with Eleventy

I created a AsciiDoc plugin for Eleventy to use .adoc files directly, like .md files. Since it uses Eleventy’s new custom template API, you cannot use the official Eleventy syntax highlighting plugin, at least directly. I’ll walk you through the steps to add syntax highlight to your Eleventy-AsciiDoc blog.

The libraries like Prism.js and Highlight.js parses the code blocks, and breaks down into keywords and symbols. Then they will wrap each keyword and symbol with a <span> together with certain class names. Through these class names, CSS styles are applied. You can do this process of parsing and wrapping keywords at client-side as well as at build-time. Allow me to show both approaches.

I will be using Prism.js to make code transformations, since it is used in the Eleventy syntax highlighting plugin. You can replace it with Highlight.js without much ado.

To test our changes, in one of your AsciiDoc files, add a sample code block:

[source,html]
----
<body>
<p>This is some HTML</p>
</body>
----

1. Client-side transformation

We will load Prism.js JavaScript and CSS files from CDN. In the base layout, usually _includes/layouts/base.njk add these lines:

  <!DOCTYPE html>
<html>
<head>
...
+ <!-- Within <head>, add Prism.js CSS files -->
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism.min.css" />

+ <!-- Optional, if you want to highlight diff syntax, add this too -->
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/diff-highlight/prism-diff-highlight.min.css" />
</head>
<body>
...

+ <!-- At the end of <body>, add Prism.js JavaScript files -->
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/components/prism-core.min.js"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/plugins/autoloader/prism-autoloader.min.js"></script>
</body>
</html>

Prism.js provides other themes too. You can replace the default CSS file with a theme based from the CDN. Look for files with path like /themes/prism-{theme-name}.min.css.

That’s all you need to get syntax highlighting. I have created a sample Eleventy setup with AsciiDoc and client-side syntax highlighting on GitHub.

The downside of this approach is that you need JavaScript to see highlighted syntax. You can do these transformations during build time and only require CSS at the client side to view transformed code. Let me explain how you can do that next.

2. Build-time transformation

We will use the official Eleventy Syntax Highlight plugin with AsciiDoc plugin to transform code blocks during build-time.

First, install syntax highlight plugin using:

$ npm install --save @11ty/eleventy-plugin-syntaxhighlight

Then, create a folder, asciidoc-templates, in the folder as you have .eleventy.js. This allows us to override any default templates with which Asciidoctor.js render nodes.

Open your .eleventy.js, and add template_dir entry in AsciiDoc plugin options:

  const eleventyAsciidoc = require("eleventy-plugin-asciidoc");

module.exports = function (eleventyConfig) {
//...

eleventyConfig.addPlugin(eleventyAsciidoc, {
+ template_dir: `${__dirname}/asciidoc-templates`,
});

//...
};

Next, create a file ./asciidoc-templates/listing.js and add this code:

const { pairedShortcode } = require("@11ty/eleventy-plugin-syntaxhighlight");

// These are default options from Eleventy Syntax Highlight plugin
const options = {
alwaysWrapLineHighlights: false,
lineSeparator: "<br>",
preAttributes: {},
codeAttributes: {},
};

module.exports = ({ node }) => {
const level = node.getLevel() + 2;
const title = node.getTitle();
const style = node.getStyle();
const content = node.getSource();

let titleEl = "";
if (title) {
titleEl = `<h${level} class="listingblock-title">${title}</h${level}>`;
}

if (style === "source") {
const lang = node.getAttribute("language");

if (lang && lang !== "text") {
const highlightedContent = pairedShortcode(content, lang, "", options);

return `${titleEl}\n${highlightedContent}`;
}
}

return `${titleEl}\n<pre>${content}</pre>`;
};

We overrode the default listing node. We are transforming the code blocks during the markup generation of listing node.

Now, only thing left is to add Prism.js CSS file. In your base layout, usually _includes/layouts/base.njk add this line:

  <!DOCTYPE html>
<html>
<head>
...
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.27.0/themes/prism.min.css" />
</head>
<body>
...
</body>
</html>

If you prefer, you can download that CSS file, and include with your CSS code. It will help you to override any styles to match your site’s styles.

With that, you have build-time syntax rendering!

You can find the sample project with build-time syntax highlighting on GitHub.

I’ll be available for long-term projects from October 2022. Meanwhile, open for smaller projects.

Get in touch