Leaving Jekyll behind

Update 23/01/2023: After about 6 years of leaving this project as-is (I haven't been very active updating the site 😂), I finally wanted to get the finger out and make the site automatically build and release whenever I pushed a commit, so I put on my big-boy pants and spent the weekend updating all the dependencies, removing those that were no longer necessary, and generally turning it into a project that can be run via CI/CD. See the section at the end for details.

A while ago, I posted on how I was moving away from Drupal towards Jekyll, thus changing from a dynamic site to a static one. Well, over Christmas, I upgraded my 8 year old laptop for a spanking new Dell XPS 13 (which is great btw) and in the process of reinstalling all my programs, getting my different dev environments set up, and generally keeping my computer as fluff-free as possible, I took a good look at what was actually necessary.

Jekyll uses both Ruby and Python. I had originally installed Ruby a long time ago to test some Ruby on Rails stuff, back when it was the hot stuff, so it influenced my decision to install Jekyll in the first place. For my new laptop, I didn't want to install both languages just to generate my site, and firing up my old laptop just to do a build seemed like a pain (once you go SSD, you never go back), so something new was in order.

Enter heckle

I do do Node.js development though, and in searching around for a Node.js Jekyll replacement, I came across heckle, by marijnh. It seemed pretty straight-forward and did everything I was looking for, with the exception that it uses Mold as a template language instead of Liquid. Hmmm...

Enter liquid-node

Hunting around some more, I came across liquid-node (changed to liquid), by sirlantis. Liquid-node is a port of the Liquid template engine to Node.js, using Promises for all the asyncy stuff. Two points stood out on the features panel: "Based on original Ruby code" and "High test coverage". Seems like it would do the job just nicely.


I started a project (TypeScript), based off of heckle, using liquid-node, and named it JekyllJS, the idea being that you could take your existing Jekyll site, run it through JekyllJS, and it'd produce the same content.

All in all, development went surpisingly smoothly. I found another Node.js module, js-yaml to handle reading the _config.yml and each post's frontMatter, and with heckle's code as a reference, I was able to get something up and running pretty quickly.

Until I hit an Unknown tag: "{% highlight %}" Exception anyway. I guess Liquid has been updated since the Node.js port happened.

Highlighting code

I have a lot of code samples across my site, and you can set up Jekyll to use Pygments or Rouge to handle this for you. I didn't find a Node.js equivalent, so I settled for highlight.js, which happily allows itself to be installed as a Node.js module to allow for server-side pre-rendering (as opposed to each client highlighting the code on each page load).

Liquid-node allows you to write your own custom tags, so I wrote one for the {% highlight %} tag that takes the chunk of text, and pipes it through highlight.js. The results are obviously different from what Pygments or Rouge generate, but I can live with the difference - as an added bonus, the CSS is smaller.

Other differences

I also had to hack a patch for the {% include %} tag, as the liquid-node version doesn't allow for tokens in order to use dynamic includes, which I use for adding comments and files to posts. The patch doesn't handle assigning variables, but there's an issue open for it, so maybe it'll happen in the future. It's included at the end of the post (as liquid-node is written in coffeescript and I don't know that). Just replace the include.js file in the node_modules/liquid-node/lib/liquid/tags/ folder after all the dependencies are installed.

It looks like this has been fixed, though as I changed how I was doing things, it was no longer an issue though. I did have to rewrite {% include_if_exists %} tag though, which I used to automatically include files and comments for posts when the relevant include exists. In any case, as liquid is no longer actively maintained, you're probably better off moving to liquidjs, but I didn't have the heart to changing rendering engines after getting everything working again 😅. The update to the {% include %} tag does mean that you need to drop the filetype from your includes. So {% include footer.html %} becomes {% include footer %}

When I added Markdown support using the marked node.js library I found that it was escaping all quotes for all reasons, including normal text ("don't", "can't", "I'm", etc). I don't particularly like this behaviour (it's still valid HTML though), so I added a fix around it. Just replace the marked.js file - included at the end of the post - in the node_modules/marked/lib/ folder if you want to do the same.

Another difference is the way that dates are parsed. Normally in your frontMatter, you'd specify a date like so:

date: 2016-01-23 19:50:00 +0100

and Jekyll would treat this date as-is, i.e. +1hr after GMT. JavaScript however, takes into account Romance Daylight Time, which handles the extra hour for daylights time. This means that if you're in the EU and you use CET during the winter, you'll use CEST during the summer, or during the last Sunday of March to the last Sunday of October, and +0100 will act as +0200.

var d1 = new Date( "2016-03-01 00:00:00 +0100" ) // Tue Mar 01 2016 00:00:00 GMT+0100 (Romance Standard Time)
var d2 = new Date( "2016-04-01 00:00:00 +0100" ) // Fri Apr 01 2016 01:00:00 GMT+0200 (Romance Daylight Time)

Depending on when you've published your post (i.e. the date), this behaviour can change the rendered day, or even month, which may or may not be a problem for you. I've not found a way around this behaviour, but if it's affecting you, you can try changing the date set in your frontMatter.

As for categories, I don't use them in my site, so I've no idea how they're supposed to render, so I didn't include them in JekyllJS.

The final thing I came across was when using {% if %} checks. I was checking to see if a variable was set in one of my templates using {% if someVar == null %}. For liquid-node, I had to change this to {% if someVar == undefined %} or simply {% if someVar %}.

The JekyllJS config

JekyllJS comes with a simple config file, config/default.json, meaning you can change a few things without having to re-compile the project. It looks like this:

		"description":"default description"

which translates as

Create this config file in the root your site project (e.g. as _siteGenConfig.json). When executing JekyllJS, the code will take that folder as the site path to start discovering and parsing files.

Running JekyllJS

I compared the generated site using an SVN diff, and aside from the Date and difference in highlighted code, it matches out. If you try it and see something, let me know.

As for running it, you can generate your site using

node app.js | "./node_modules/.bin/bunyan" -o short

from a command line. The bit at the end is just running the output through Bunyan, which is the logging lib I use in the project.

If you want to test your site, JekyllJS will also serve it for you via

node app.js serve | "./node_modules/.bin/bunyan" -o short

which will start a local webserver under the port set in the config. It'll also handle 404 errors with the specified URL.

I've completely changed how to run JekyllJS and generate your site in lieu of it supporting CI/CD environments. Previously, you executed code from the JekyllJS folder, which didn't make any sense. Now, you create your config file in the root path of your project, then create a new npm project and install it as a dependency:

npm install --save git@bitbucket.org:divillysausages/jekylljs.git

Once that's done, you can create scripts that will generate and build your site, and optionally serve it locally, using npx:

"scripts": {
	"build": "npx jekylljs ./_siteGenConfig.json",
	"serve": "npx jekylljs ./_siteGenConfig.json serve"

When running this in a CI/CD environment, simply deploy the _site folder to S3.

Get JekyllJS

You can find the code over on Bitbucket, and you can install the dependencies using npm install. Also on site is the liquid code that I use to generate my tags, archive, and feed.xml if it's something interesting to you.




thanks for sharing this. if it was on github I think you’d get a lot more interest amd possibly some comtributions, its a really great thing youve put together, could you put it on github.

Damian Connolly

Hi Diceman,
The code's available on Bitbucket if you want to download it.

If you're looking for something specific on Github, check out GitHub-Pages-Preview, which is a fork of the project.

Submit a comment

* indicates required