---
title: "Blogging with Asciidoctor and Hugo"
date: 2019-09-03T13:07:43+08:00
categories:
    - Hugo
tags:
    - writing
    - ssg
    - hugo
---

= Blogging with Asciidoctor and Hugo
Gabriel Arazas <foo.dogsquared@gmail.com>
2019-08-27

:asciidoctor_site: https://asciidoctor.org/

Finally, I've found a great blogging workflow with Hugo and Asciidoctor. 

With the extensive built-in feature set of Hugo and the feature-rich 
text formatting options that Asciidoctor offers, it creates a blogging experience 
composed of easy content management and fun writing experience. 

As of this writing, there's only a 
https://rgielen.net/posts/2019/creating-a-blog-with-hugo-and-asciidoctor/[handful] 
https://opensource.com/article/17/8/asciidoc-web-development[of] 
https://www.bryanklein.com/blog/hugo-asciidoctor-vscode-gitlab-firebase/[articles] 
https://blog.anoff.io/2019-02-17-hugo-render-asciidoc/[and] 
http://discuss.asciidoctor.org/Writing-BLOG-in-Asciidoctor-td7015.html[discussions] 
about blogging 
with Hugo and Asciidoctor so I'll add my 2 cents into the pile with 
the recent version of Hugo and Asciidoctor. 
Think of it like an update report. 

For future references, here are the following tools and their versions 
used for this post:

* Asciidoctor v2.0.10
* Hugo v0.57.2 

NOTE: This is not a full-on tutorial, more like a tour guide of my blogging setup with 
Hugo and Asciidoctor. 




== Prerequisites 

If you want to follow through the whole post, I assume you already 
satisfied the following conditions: 

* Installed https://gohugo.io/[Hugo] and https://asciidoctor.org/[Asciidoctor]
* Already know the basics of both tools 
* Already has a Hugo project with a theme installed 
* **OPTIONAL**: A https://travis-ci.org/[Travis CI] account (or similar CI/CD services) 
* **OPTIONAL**: A https://github.com/[GitHub] account (or similar remote Git repo) 

TIP: If you're not familiar with Hugo and Asciidoctor, they both have 
a quick start guide. Here's for https://gohugo.io/getting-started/quick-start/[Hugo] and 
https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/[Asciidoctor]. 




== Asciidoctor and Hugo 

In most static site generators including Hugo, https://daringfireball.net/projects/markdown/[Markdown] 
is the one and only first-class citizen when it comes to creating posts. 
However, in recent Hugo versions, there exists the 
https://blog.anoff.io/2019-02-17-hugo-render-asciidoc/[external helpers] 
feature which calls appropriate external programs to certain type of files 
(or file extension). 
Fortunately, Asciidoctor-based files are automatically compiled with 
Asciidoctor so we don't need to do anything. 
Just have it installed and you're raring to go. 



=== Creating content with Asciidoctor

Creating Asciidoctor-based content in a Hugo site is very easy. 
Just create an Asciidoctor file manually or you could go with Hugo's way 
which is the optimal way.

[source,shell]
----
hugo new posts/my-first-post.adoc 
----

And there should be a new Asciidoctor file at `content/posts/my-first-post.adoc`. 

Most likely, you would see that it's formatted like a Markdown file since 
most themes do not have focus for Asciidoctor. 

One of the features of Hugo is letting you create 
https://gohugo.io/content-management/archetypes/#readout[content templates] (or an 
archetype) for your usual content.
We create content with Asciidoctor so let's create a quick template for that. 

Create a file in `archetypes/default.adoc`. 
This will be the master template whenever Hugo detects the new content 
has a file extension of `.adoc`. 
Then, create a template for your Asciidoctor documents. 

To get an example, here's my template for my Asciidoctor documents. 

[source,asciidoctor]
....
--- <1>
title: "{{ replace .Name "-" " " | title }}" <2>
date: {{ .Date }} //
draft: true

categories:
    - "category1"
tags: 
    - "tag1"
    - "tag2"
---

= {{ replace .Name "-" " " | title }}
{{ .Site.Author.name }} {{ with .Site.Author.email }}<{{ . }}>{{ end }} <3>
{{ dateFormat "2006-01-02" .Date }} <4>

....
<1> The frontmatter. 
Unfortunately, we would still have to put this for Hugo to 
recognize this document as one of the content. 

<2> Converts the slug of the document to title case. 

<3> Putting the author in the Asciidoctor preamble along with the email (if there's any). 
Feel free to discard it. 

<4> The date in ISO format. 

NOTE: Speaking of frontmatters, native Asciidoctor frontmatter is not recognized. 

You can modify the template to your heart's content. 
For example, if you use https://www.mathjax.org/[MathJax] for writing mathematical formulas, 
you can https://asciidoctor.org/docs/user-manual/#activating-stem-support[add the stem attribute] 
(`:stem:`). 

Since Asciidoctor-based documents only recieve basic support, you still need 
to do some work yourself before you get satisfied with the settings. 
For example, enabling syntax highlighting and styling certain things like 
callouts, admonition blocks, and open blocks. 

Also, not everything is 100% working so you might encounter some problems 
which is discussed later in the post. 
Nonetheless, it works for the most part and you can still write expressively with the 
heavier feature set of Asciidoctor. 




== Syntax highlighting (without the shortcode)

NOTE: As of 2019-09-25, I don't use a syntax highlighter anymore for my site in the name of performance and consideration for low internet speeds. 
Clearly, I didn't think ahead about this. 

Syntax highlighting can be an important feature for technical blogs especially 
if you often have to show code in your posts. 

On Asciidoctor, you can 
https://asciidoctor.org/docs/user-manual/#enabling-source-highlighting[enable syntax highlighting] 
with the `:source-highlighter:` attribute. 
You can compile it on runtime with the executable but it's not possible with 
Hugo since the arguments passed to it is hardcoded. 
You can, however, enable it for every document you have but as you might 
imagine, it's not ideal and requires some manual labor. 

If you're only relying on the out-of-the-box features from Hugo (READ: if), 
you can get it with the 
https://gohugo.io/content-management/shortcodes/#highlight[`highlight` shortcode] 
which is going to bite back if you're going to migrate to another blogging 
platform or static site generator. 

Still, there are some ways with getting syntax highlighting for your Hugo site
without the Asciidoctor attribute or the Hugo shortcode. 
It'll just take some more effort to get through. 

One of the more reliable ways on enabling it is using syntax 
highlighters like https://github.com/highlightjs/highlight.js[highlight.js] 
or https://prismjs.com/[PrismJS]. 
I'll be discussing on setting it with PrismJS since it easier and 
that's what I'm mainly using on my blog. 

For future references, the version of PrismJS I'm using is at v1.17.1. 



=== Getting the files 

First, we are going to need the syntax highlighter scripts along 
with their stylesheets, of course. 
I recommend to save the files locally instead of linking them through 
a CDN since they're often prebuilt with limited languages and settings support. 

Getting the files for PrismJS is very easy. 

* Go to the https://prismjs.com/download.html[download page]. 
* Select the minified version.  
* Select all of the languages you think you need to support. 
* Include the https://prismjs.com/plugins/keep-markup/["Keep Markup"] plugin. 
* Download it. 

You'll need the "Keep Markup" plugin in case you use 
https://asciidoctor.org/docs/user-manual/#callouts[Asciidoctor callouts] since 
PrismJS replaces the HTML elements along with their classes. 

With the script downloaded, place them somewhere in your Hugo project. 
For this purpose, I'll assume the script is in the `static/js/lib/SYNTAXHIGHLIGHT.js`. 

Don't forget to choose a theme as well. 
I'll assume that the stylesheet is in `static/css/SYNTAXSTYLESHEET.css`. 



=== Integrating it with Hugo 

Now the hardest part, putting them into use with your Hugo project. 

Add the syntax highlighter before the end of the document 
body (`<body>`) tag and the stylesheet inside the `<head>`. 

The available location for it depends on the theme. 
I recommend to start looking to the layout folder with the default templates 
of the theme (`theme/$NAME_OF_THEME/layouts/_default`) 
then the partial folder (`theme/$NAME_OF_THEME/layouts/partials`). 

TIP: You might want to start at the base template 
(`theme/$NAME_OF_THEME/layouts/_default/baseof.html`). 

Copy the appropriate file from the theme folder to your own layout 
folder and link it similar to the following code listing. 

[source,html]
----
<!-- Inside of the head element -->
<link rel="stylesheet" href="{{ "css/SYNTAXSTYLESHEET.css" | absURL }}">

<!-- ... -->

<!-- Before the end of the body tag -->
<script src="{{ "js/lib/SYNTAXHIGHLIGHT.js" | absURL }}"></script>

----

The setup is done! 
That leaves one less problem for content migration in case 
you want to move out of Hugo. 
You'll thank yourself for doing so. 




== Problems with using the workflow 

While Hugo and Asciidoctor is great and all, there are a couple of 
problems with this setup. 

The most obvious is the HTML output of Asciidoctor with the default 
backend is not great and leaves a lot of things to be desired. 

.`<div>` then a `<p>` for a paragraph, really?
image::images/asciidoctor-sample-html.webp[`<div>` then a `<p>` for a paragraph, really?] 

It's not semantic and it is unconventional. 
Not only that it's a pain to style it with CSS but also breaks a lot 
of the accessibility features like screen readers since it 
relies on certain HTML tag structures. 

NOTE: You can get around this by using the 
https://github.com/jirutka/asciidoctor-html5s[Asciidoctor HTML5s backend] to 
produce the correct and semantic web-friendly output. 
Remember, the arguments passed from Hugo to Asciidoctor is hardcoded. 
You would have to trick Hugo somehow into passing your own arguments. 
Fortunately, you don't need to worry since
https://ratfactor.com/hugo-adoc-html5s/[there's already someone out there shared the details].

Another problem you could encounter (and maybe bash your head against) is 
the basic support for Asciidoctor itself if you don't want to rely much on 
creating hacks and workarounds. 

As previously mentioned, Hugo supports Asciidoctor through external helpers. 
External helpers are relatively new and more like an experimental feature. 
There is a proposal on improving it by 
https://github.com/gohugoio/hugo/issues/6089[adding user configurations] 
so at least there's hope for this particular feature to expand. 

There's also the fact that not all built-in feature of Hugo (such as 
https://gohugo.io/content-management/toc/[table of contents]) works within 
Asciidoctor (and possibly other non-Markdown formats) content. 
Fortunately, 
https://asciidoctor.org/docs/user-manual/[Asciidoctor is quite extensive by itself] 
and there's not a lot of Hugo features that doesn't work and you won't likely 
need them anyway. 

Also, native Asciidoctor front matter doesn't work as previously mentioned.  




== Deploying with Travis CI

https://rgielen.net/posts/2019/creating-a-dockerized-hugo-asciidoctor-toolchain/[Some] 
https://axdlog.com/2018/using-hugo-and-travis-ci-to-deploy-blog-to-github-pages-automatically/[posts] 
https://www.martinkaptein.com/blog/hugo-with-travis-ci-on-gh-pages/[are] 
https://jellis18.github.io/post/2017-12-03-continuous-integration-hugo/[floating] 
https://insileco.github.io/2018/03/30/hugo-github-travis-a-step-in-continuous-deployment/[around] 
on how to make a done-and-forget deployment toolchain with different tools.

Personally, I pass the full effort of deploying my blog to a CI/CD workflow. 
I use https://travis-ci.org/[Travis CI] for the job. 

NOTE: You can also take a view on the https://github.com/foo-dogsquared/blog[GitHub repo of my blog] for 
an idea how it works on a larger picture. 

Here's the configuration I've used to deploy my Hugo blog:

[source,yaml]
----
dist: bionic <1>
language: generic

before_install:
  - sudo apt-get update
  - sudo apt-get install ruby
  # Assuming that the GitHub API is at version 4.0 <2>
  - curl https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "hugo_extended.*deb" | grep "browser_download_url" | cut --delimiter=":" --delimiter="\"" --fields=4 | wget -qi -
  - sudo dpkg -i hugo*.deb
  - sudo gem install asciidoctor <3>
script:
  - hugo <4> 
deploy: <5>
  local_dir: "public/"
  provider: pages
  skip_cleanup: true
  github_token: $GITHUB_TOKEN
  target_branch: gh-pages
  on:
    branch: 
      - demo
      - master
---- 

Here's the breakdown of the configuration: 

<1> It will use a Linux-based machine with Ubuntu Bionic (18.04) as the operating system. 
<2> Downloads the latest Hugo binary from its repo through GitHub release and installs it.
<3> Installs the Asciidoctor toolchain. 
<4> Build the Hugo site. 
<5> Deploy the build folder to the `gh-pages` branch of my GitHub repo when the branch 
occurred at `demo` or `master`. 

NOTE: The configuration should work as long as the GitHub API version is at version 4. 
You may have to do a bit of API debugging and tweaking to get it right. 

Depending on the web hosting service provider, you may have to do additional work such as 
pre-compressing your files or configuring your server. 
Since the blog is hosted using https://pages.github.com/[GitHub Pages], 
I don't have to configure some stuff (unfortunately for me). 




== Conclusion 

That's all of the Hugo and Asciidoctor stuff you need to know for now. 

Just look for more examples and you'll get more idea. 
You can take https://github.com/foo-dogsquared/blog[the GitHub repo of my blog] 
for a starter point. 

Personally, blogging with Hugo and Asciidoctor sums up to be fun. 
So fun that 
https://github.com/foo-dogsquared/hugo-theme-terminal-plus-minus[I eventually created a theme that focuses on supporting Asciidoctor content along with other stuff].

Not perfect but it still offers a lot of satisfying and more expressive 
writing experience compared to writing with Markdown. 

With all of the imperfections this workflow has, there's some stuff to look forward in the 
future especially with Hugo's external helpers feature. 
Hopefully, more tools will take notice of Asciidoctor and how it could 
be great for writing technical and web-based content. 




== Further looking

=== Web

https://asciidoctor.org/docs/[_Asciidoctor documentation_]::
Getting started with Asciidoctor is quite easy with the official documentation. 
It should be able to help you a long way into getting comfortable with it. 
If you're getting the ropes of it, I recommend to check out the 
https://asciidoctor.org/docs/user-manual/[user manual] often. 

https://ratfactor.com/hugo-adoc-html5s/[_Better Hugo/AsciiDoc HTML_ by **David Gauer** (ratfactor.com)]:: 
It's a short and sweet post on how to make HTML output of Asciidoctor way better 
than before with the UNIX PATH trickery trick that I've mentioned in the article.

https://gohugo.io/documentation/[_Hugo documentation_]::
The documentation of Hugo is great. 
Has a lot of clear and concise information for newcomers and has an intuitive 
navigation of the content structure. 



=== Video 

https://www.youtube.com/playlist?list=PLLAZ4kZ9dFpOnyRlyS-liKL5ReHDcj4G3[_Hugo tutorial series_ by **Mike Dane**]:: 
A video series by https://www.youtube.com/channel/UCvmINlrza7JHB1zkIOuXEbw[Mike Dane]. 
It's also featured on the official Hugo documentation as a video resource. 
The video series is well-done and offers brief and concise explanation. 
