Better Hugo/AsciiDoc HTML

Generating Semantic HTML5 output from AsciiDoc and Hugo - this one has a happy ending

Update: Yeah, I had enought of Hugo after about a year. I replaced it with an 80 line Ruby script which runs faster and does exactly what I want it to. It uses AsciiDoctor as a library.

I’m also over my infatuation with AsciiDoc as a format. It’s fine, but it’s far from perfect. If I were to start over, I’d probably just make my own markup language. But I’m crazy. Don’t be like me.

Original article:


I like AsciiDoc so much that Hugo’s support of the AsciiDoc format was one of the main reasons I picked it to generate this (and other) sites. I’m also okay with AsciiDoctor, the popular Ruby processor for AsciiDoc.

However, I despise the default HTML output of AsciiDoctor. It works and I completely understand why it was written the way it was. But compared to clean, semantic HTML, it’s a noisy mess. I started to create a stylesheet for the default output and just couldn’t make myself do it.

If I’m going to use Hugo to generate this site, the HTML that is being generated is going to need to look at least a little bit like something I would have written myself.

Enter "HTML5s"

Thankfully, we can use Jakub Jirutka’s asciidoctor-html5s (Semantic HTML5) backend for AsciiDoctor. It outputs the sort of good, clean HTML I can live with. (Thanks Jakub!)

Using it at the command line is easy enough:

# gem install asciidoctor-html5s
$ asciidoctor -r asciidoctor-html5s -b html5s foo.adoc

But how do we get Hugo to use it?

It was pretty easy to find Additional Formats Through External Helpers in the Hugo documentation, which led me to this bit of the Hugo source: helpers/content.go:610

func getAsciidocContent(ctx *RenderingContext) []byte {
	...
	args := []string{"--no-header-footer", "--safe"}
	...
}

We can clearly see that the parameters to AsciiDoc/tor are hard-coded into Hugo. So unless I wanted to maintain my own fork of Hugo (nope nope nope!), I clearly wasn’t going to be able to make the change there.

UNIX trickery

The only clear solution, then, was to make my copy of AsciiDoctor always use the html5s backend.

First, let’s see where the asciidoctor executable lives:

$ which asciidoctor
/usr/bin/asciidoctor
$ file /usr/bin/asciidoctor
/usr/bin/asciidoctor: a /usr/bin/ruby script, ASCII text executable

Now let’s replace it with a Bash script:

$ sudo -i    # start being root
# mv /usr/bin/asciidoctor /usr/bin/asciidoctor_real
# cat > /usr/bin/asciidoctor
#!/bin/bash
/usr/bin/asciidoctor_real -r asciidoctor-html5s -b html5s "$@"

Set permission and check our work:

# chmod +x /usr/bin/asciidoctor
# exit    # stop being root
$ file /usr/bin/asciidoctor
/usr/bin/asciidoctor: Bourne-Again shell script, ASCII text executable

That’s three steps:

  • Rename the original Ruby script from asciidoctor to asciidoctor_real

  • Create a bash script called asciidoctor containing:

    #!/bin/bash
    /usr/bin/asciidoctor_real -r asciidoctor-html5s -b html5s "$@"
  • Add the executable permission to the new script with chmod +x

It works!

Now when Hugo generates HTML pages from my .adoc files, it is using my new script which passes the custom html5s backend parameters to the real AsciiDoctor program.

The only downside is that I’ll have to make sure to re-do this change on any new systems or after upgrading AsciiDoctor. At least now I have this article to remind me.