Webpack with hot module replacement (HMR) in WordPress theme development

I tried for what seemed a very long time to get HMR set up and working with in my wordpress theme but really struggled to workout how to set it up correctly.

Below I’ve detailed what I have done to get HMR working on my development virtual machine. And also getting a Vue.js app working with HMR and vue-cli 3.

I was trying to create a Vue app for my theme, and most of the online info about setting it up are about the old vue-cli (version 2) and not much about the new way (vue-cli 3) of doing it.

Until I found this post https://medium.com/@romualdasromasdakeviius/using-vue-cli-to-build-wordpress-plugin-including-hmr-1a71dfdf05c2 and managed to get it working pretty quickly with vue-cli 3.

Getting Started

First, lets create an a new default theme, create a package.json and define the devDependencies. I use yarn to manage my npm packages.

$> mkdir my-webpack-theme && cd my-webpack-theme
$> tee style.css <<EOF
/**
 * Theme Name: My Webpack Theme
 */
EOF > /dev/null
$> yarn init
$> yarn add webpack webpack-dev-server webpack-merge --dev

Now we have Webpack installed, it’s time to create a config file to tell webpack what to build.

With Webpack 4 a lot of the config is default, and hidden, although we do need an entry point though, and an output block to tell webpack where to write the files.

I like to use webpack-merge to separate my dev and prod configs.

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

module.exports = {
  context: path.resolve(__dirname, 'src/js'),
  entry: {
    main: './main.js',
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist/js/'),
    publicPath: '/wp-content/themes/my-webpack-theme/dist/js/'
  }
}

webpack.common.js sets up the base config, that defines my entry and output.

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production'
});

webpack.prod.js doesn’t do much, it just sets the mode to production

const merge = require('webpack-merge');
const webpack = require('webpack');

const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  output: {
    publicPath: 'http://localhost:8080/'
  },
  devServer: {
    hot: true,
    publicPath: 'http://localhost:8080/',
    allowedHosts: ["development.vm"],
    headers: {
      "Access-Control-Allow-Origin": "*"
    },
    disableHostCheck: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
})

Now the webpack.dev.js is where we set up the <a href="https://webpack.js.org/configuration/dev-server/" target="_blank" rel="noreferrer noopener" aria-label="webpack-dev-server (opens in a new tab)">webpack-dev-server</a>.

Loading webpack entry points automatically

I develop on an Ubuntu virtual machine, so I needed a way of setting up my WordPress theme to load the javascript files from the webpack-dev-server when I had it running, but load the built files from the dist directory for production.

Once, I set this up, I found that I was getting a polling error to sockjs-node in the console.

console error

It turns our that the dev server doesn’t get set up with the correct port, it’s still trying to access localhost, port 80 by default, and the dev server is listening on 8080.

I managed to find a solution on the following ticket: https://github.com/vuejs/vue-cli/issues/1472, This mentions PR https://github.com/vuejs/vue-cli/pull/1526 and that vue-cli uses devServer.public to define the socks url.

I modified my vue.config.js devServer to have the public property, and the console errors went away.


Leave a Reply

Your email address will not be published. Required fields are marked *