Pretty HTML with Jekyll
by Lukas Kukacka
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 theprettify_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!
Like the article, got a comment or found an issue? Get in touch via Twitter .
Subscribe via RSS