This article is Part 1 in a 3-Part series.

I’ve made another Jekyll plugin jekyll-esm which works with the new standards compliant es6 module system if you are interested in using JS with no asset bundler.

I’d been following guides online and experimenting with integrating Webpack with Jekyll and I ultimately realised that there were certain aspects of the two tools that mean they need to be treated very specifically when used in combination.

Build order matters

In an earlier more naive Jekyll/Webpack hybrid Jamstack implementation I’d been building Webpack first and then piping the output to Jekyll as the second step. While this works to a point, it soon breaks down because some JS tools (Such as PurgeCSS) depend on your HTML in order to properly function. As a result the jekyll-webpack gem was born.

Compile HTML first, assets second

With this plugin you only need the one command: jekyll build or jekyll serve and Webpacked assets are handled automatically as a step in the build pipeline. This neatly solves the problem of running webpack after your html has been rendered.

Installing jekyll-webpack

In your jekyll project add gem 'jekyll-webpack', group: [:jekyll_plugins] to your Gemfile and then bundle install.

Now add it as a plugin in _config.yml:-

# _config.yml

plugins:
  - jekyll/webpack

Preferably add this as the last plugin to ensure it happens last.

Webpack can get slow! Debounce!

If you’re using a tool such as tailwindcss, the bundle can get large, you don’t always want webpack to run every time jekyll does if you’re using jekyll serve for example. As long as you’ve got your assets in a sub directory (such as ./src) you can tell Jekyll Webpack to watch for changes and only run when you change a file in that folder. To do this, add a section to _config.yml and restart jekyll serve:-

# _config.yml

webpack:
  debounce:
    watch: src

Now, Jekyll Webpack will cache the last changed version of your bundle and will only rebuild it if a file in ./src is changed (while serve is running).

Building it for production

If you want webpack to build a production bundle you can pass the environment via NODE_ENV. So to build your production bundle:-

NODE_ENV=production bundle exec jekyll build

Webpack can do a lot!

And so now you can have a webpack config in your Jekyll project such as:-

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require("path");
const glob = require("glob-all");
const PurgecssPlugin = require("purgecss-webpack-plugin");

module.exports = {
  entry: "./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader", {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              sourceMap: true,
              plugins: [
                require('tailwindcss'),
                require('autoprefixer'),
              ],
            },
          }]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin(),
    new PurgecssPlugin({
      paths: glob.sync([
        path.join(__dirname, "**/*.html"),
        path.join(__dirname, "dist/**/*.js")
      ]),
      extractors: [
        {
          extractor: content => content.match(/[A-z0-9-:\/]+/g) || [],
          extensions: ["html", "js"]
        }
      ]
    })
  ]
};

Note that PurgeCSS is bundled in to Webpack now. There’s no need to have it as a seperate build step handled by a Jekyll plugin or such. Personally I prefer this style of asset configuration as it keeps it all in one place.

Installation and Usage instructions are available in the README