Jekyll is a great way to build static website. But it is not easy to produce a nice HTML code with it.

This blog is built with Jekyll. I like to work with, but one thing which bothered me is the HTML code it generated. It is not pretty at all. Indentation is all wrong, many empty lines and so on.

It is not really fault of Jekyll; it can generate beautifully formatted HTML. However because all code is defined with Liquid templating language, it all boils down to website templates. If the template is good, HTML will be pretty. But that is the catch. It would be extremely time consuming and in some cases probably impossible to make templates perfect on all places.

Is there a simpler solution? I got one…

Before we continue, let me answer the question many people might ask: “Why bother at all?”. I just like all my code tidy, no matter if it is generated. And it was fun to do… 😎

Take 1: Existing Jekyll plugin

There is a Jekyll::Tidy plugin. It is very easy to integrate and use.

Unfortunately, it does not work perfectly for me. It runs fine, but does not generate really pretty HTML in my case.

In my case the </html> ends up indented with 3 tabs. So the plugin seems to get confused somewhere along parsing my HTML, maybe it has issues with some inline JavaScript.

Take 2: Using Ruby gems explicitly

My next attempt was to use existing Ruby gems.

I tried using HTML Beautifier and Nokogiri wrapped in a simple script. No luck here either.

  • Using HTML Beautifier the result had the same issue as with Jekyll::Tidy plugin. Not a big surprise since Jekyll::Tidy is internally using HTML Beautifier
  • Using Nokogiri the output was even worse. There was a lot weird tags generated. I guess that is mainly because my HTMLs are not pure XMLs, there is nested JavaScript and so on.

The final solution: Node.js

I resorted to pretty npm package as it is the only one I found to produce correct pretty HTML.

There is a clear drawback here: Depending on just another technology. On top of Ruby/Gems/Jekyll, we also need Node.js. I guess it might be a problem if you are using Github pages (didn’t try myself). It can be used with Docker based CI’s without issues though (there are Ruby-Node images).

Install node

Install Node.js. Via brew:

$ brew install node

prettify_html.js script

To simplify everything, I have wrapped pretty package usage to simple Node.js script. It prettifies HTML file in place.

The script accepts path to a HTML file in a first argument, reads the file, prettifies using pretty and writes back to the same file.

#!/usr/bin/env node

// validate number of arguments
if (process.argv.length < 3) {
    console.error("Missing argument #1: path to HTML file");
    process.exit(1);
}
var filePath = process.argv[2]
// Formatting configuration
var formattingOptions = {
    indent_char: '\t',
    indent_size: 1,
    unformatted: ['code', 'pre', 'em', 'strong'],
    ocd: true,
};
// Read contents of original file
var fs = require("fs");
fs.readFile(filePath, "utf8", function (err, fileContent) {
    if (err) {
        console.error("ERROR: " + err);
        process.exit(1);
    }
    // Prettify HTML
    var pretty = require('pretty');
    var prettifiedHtml = pretty(fileContent, formattingOptions)
    // Write pretty HTML back to file
    fs.writeFile(filePath, prettifiedHtml, (err) => {
        if (err) {
            console.error("ERROR: " + err);
            process.exit(1);
        }
    });
});

Putting everything together

With few Shell scripts all of this could be absolutely transparent to you.

Setup for Node.js

Create a package.json file in the root of your Jekyll code (same folder as _config.yml)

{
  "name": "YOUR_PROJECT_NAME",
  "dependencies": {
    "pretty": "2.0.0"
  },
  "repository": {
    "type": "git",
    "url": "git+ssh://[email protected]/you/yourrepo.git"
  },
  "author": "Your Name",
  "license": "CUSTOM"
}

Install dependencies script

Create an install_dependencies.sh script

#!/usr/bin/env bash
# Fail on any error
set -e
echo "# Installing gem dependencies"
bundle check --path vendor/bundle || bundle install --path vendor/bundle
echo "# Installing npm dependencies"
npm install

Build script

Create a build.sh script

#!/usr/bin/env bash
# Fail on any error
set -e
echo "# Building with Jekyll"
bundle exec jekyll build
echo "# Prettifying HTML files"
for html_file_path in $(find ./_site -name '*.html' | sort); do
    echo -n "${html_file_path} ..."
    ./prettify_html.js "${html_file_path}"
    echo " Done"
done

The script:

  • builds website using the usual jekyll build
  • finds all .html files inside _site folder and uses the prettify_html.js on each of them

Exclude newly added files from build

Don’t forget to exclude newly added files in _config.yml so it does not get published in _site folder on build.

exclude:
  - "*.sh"
  - "prettify_html.js"

This will ignore all Shell scripts and the newly created Node.js file.

Put it all together

Don’t forget to make scripts executable:

$ chmod u+x ./install_dependencies.sh
$ chmod u+x ./build.sh

Now use the newly created scripts to build your website. Make sure all gem and npm dependencies are installed:

$ ./install_dependencies.sh
# Installing gem dependencies
The Gemfiles dependencies are satisfied
# Installing npm dependencies
Done installing dependencies

Build the website:

$ ./build.sh
# Building with Jekyll
Configuration file: /Users/user/website/_config.yml
            Source: /Users/user/website
       Destination: /Users/user/website/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
                    done in 0.826 seconds.
 Auto-regeneration: disabled. Use --watch to enable.
# Prettifying HTML files
./_site/404.html ... Done
./_site/about/index.html ... Done
...

and enjoy pretty HTML code for your whole website in _site folder!