Jekyll with ES6 using webpack

30 Oct 2018
Last updated: 22 Nov 2018

This blog uses Jekyll to layout and manage the content of the pages and this blog - it’s a fantastic static site generator. We have the same level of passion for Webpack also given it’s power, customisability and performance tools - we can write in modern ES6, and have it transpile into ‘old’ style JS for legacy browsers, without losing any functionality.

Jekyll however is quite limiting when it comes to compiling JavaScript, especially if you want to use npm package management and modern ES6. I wanted ES6 to import some polyfills for IntersectionObserver API on the web performance page.

Setup jekyll

gem install jekyll
jekyll new blog

By default you store Jekyll files in the root directory of the project, posts in _posts - and it all gets outputted into _site.

.
├── 404.html
├── Gemfile
├── Gemfile.lock
├── _config.yml
├── _posts
│   └── 2018-10-30-welcome-to-jekyll.markdown
├── about.md
└── index.md

However, because webpack will be in our project’s root directory, we need to move the Jekyll files into a new src directory, and output in one called public so that Jekyll won’t start regarding webpack files as content, or vice versa.

Keep the Gemfiles and _config.yml in the root directory, and make a .ruby-version and .nvmrc to set the correct Ruby and Node versions for your installation.

Add this to the config.yml file… this instructs Jekyll to look in the right places for our code

destination: public
source: src
exclude: ['node_modules']

Setting up npm

Webpack, babel and all the libraries that you’ll use will be managed through npm, so initialize it in the root directory.

npm init

It’ll walk you through some handy default values, guessing off of the directory name and any git repository it finds.

Setting up webpack

Now we have npm install webpack as a dependency

npm install webpack webpack-cli --save-dev

We also need to install Babel, for transpiling our modern ES6 into good (well, not great) old fashioned JavaScript.

npm install @babel/core @babel/preset-env babel-loader --save-dev

Make a Hello World homepage JS

We’re going to make a script that just includes itself on the homepage, and nowhere else. So let’s make a directory called webpack, and in it make a homepage.js with a console log (or whatever takes your fancy).

./webpack/homepage.js

console.log('hello world!')

Add the webpack.config.js

You instruct Webpack through it’s config file - there’s loads of great tutorials about it, so I won’t really go into huge detail here.

Make a new webpack.config.js file in the root directory with these contents.

const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    homepage: "./webpack/homepage.js"
  },
  output: {
    path: path.resolve(__dirname, "./src/assets/javascripts/")
  },
  mode: "production",
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules)/,
        loader: "babel-loader",
        query: {
          presets: ["@babel/preset-env"]
        }
      }
    ]
  }
};

Note that the output of the webpack compilation is into the Jekyll controlled ./src directory

Make Jekyll & git ignore some things

node_modules is a massive directory full of lots of code that we don’t need to keep lugging around wherever we take this project. Let npm handle this for you for each installation you do.

So let’s get git to ignore that, and a few other common things we don’t need to version control.

In your .gitignore

public
node_modules
.sass-cache
src/assets/*.js

We’re choosing not to version control the compiled JS in src/assets because it’s just compiled minified code we already have, and will quickly become large. If you include it, then changes to you non-compiled code might be ignored by the build process, so you could get old code after a deploy.

Compiling and running Jekyll

To compile your JS code, and keep a watch out for any changes, run:

webpack -w

And then run a Jekyll server

jekyll s

Including JavaScript on Jekyll pages

We’re quite keen on web performance here at Recollate - and a big performance gain you can get from JavaScript, is through using Webpack’s chunking. Basically, you split your code into sections and pages, so each will only load the code it needs… but Webpack is very clever, and aggregates the common code across all of the chunks, providing the user with only the new code they need, stopping any duplication in the JS.

We’ll make a new template variable in our default template, that looks for a per-page JavaScript bundle.

Add this just before the body end tag…

  ...
  {% if page.js_bundle %}
    <script type="text/javascript" src="/assets/javascripts/{{ page.js_bundle }}.js" async></script>
  {% endif %}
  </body>
</html>

And then on your index.md, add some front matter to get us loading the JavaScript

---
layout: page
title: Homepage
js_bundle: webperf
---

...

More bundles

To add another compiled bundle, add a new ‘entry’ line to webpack.config.js

  ...
  entry: {
    homepage: "./webpack/homepage.js",
    webperf: "./webpack/webperf.js"
  },
  ...

GitHub repo

See the code above, all setup and working on our GitHub repo.