During recent updates to our Theme boilerplate we began work behind the scenes reimagining our internal dev workflow for both WordPress plugins and custom theme development.

Webpack vs NPM Scripts

We looked at both Webpack and NPM scripts as alternatives to our old Grunt setup. I spent a fair bit of time with both, but eventually settled on Webpack.



Webpack and WordPress

One of the challenges with Webpack was getting to grips with the idea of js entrypoints. Once I’d got my head around the ins and outs (literally) of Webpack, I began pulling in the equivalent Webpack packages for each one we had in our original grunt setup.

Getting your JavaScript into WordPress/Webpack

Webpack is first passed a regex test, which picks up any matching files, and then applies the “loaders” defined for that test. Here we’re just picking out any files that have the .js extension.

We’re also explicitly excluding any js imported from the node_modules or bower_components folders.

test: /\.js$/, exclude: /(node_modules|bower_components)/, use: [ { loader: 'babel-loader', }, ],

Babel

We’re using the babel-loader on any js picked up in our test. Babel is transpiling the js so it will work with older browsers.

We’re also using this opportunity to include the lodash utility library.

module.exports = function(api) { api.cache(true); const presets = [['@babel/preset-env', { modules: false }]]; const plugins = ['lodash']; return { presets, plugins, }; };

Terser

Outside our test, we use Terser to compress our resulting js files, ready for production.

This uses Webpack’s built in optimization parameter.

optimization: { minimizer: [new TerserPlugin()], },

CSS & SCSS Stylesheets

Perhaps the most complex of our pipelines, the stylesheet has 4 steps.

Below we’re looking for any files with the .css or .scss extension, then passing them over to the loaders.

test: /\.s?css$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { sourceMap: true, }, }, { loader: 'postcss-loader', options: { sourceMap: true, }, }, { loader: 'sass-loader', options: { sourceMap: true, importer: globImporter(), }, }, ],

Sass processing

The tasks above run in reverse order, so first we use sass-loader to load SCSS and compile to CSS. We’re also using the globImporter here which allows Webpack to understand glob expressions, such as **/* .

The result of the above is a CSS file ready for the next step.

Looking for a Future-Proof WordPress Dev Team? Speak to the Make Do Experts!

PostCSS

Next we’re processing the CSS with postcss-loader, which is the webpack loader for PostCSS.

In a separate PostCSS config file, we defined what we wanted PostCSS to do with our CSS.

module.exports = { plugins: [ require('autoprefixer')({ browsers: ['> 5%', 'last 2 versions'], }), require('pixrem')({ atrules: true, }), require('cssnano'), ], };

Above, we’re auto-prefixing our CSS declarations with vendor prefixes, to work with the last two versions of every browser and any browser version with more than 5% global usage.

We then use pixrem to provide a pixel fallback each time we use the rem unit. Finally, we use cssnano to compress down our resulting CSS into the smallest file possible, ready for going live!

CSS file export

Following PostCSS, we use css-loader to retrieve the resulting CSS from above, then finally pass it to mini-css-extract-plugin which exports the CSS to its own file.

Theme & Plugin Project Assets

In this context, assets cover Images, SVGs and Fonts. For each of these we used the file-loader, which lets us extract the files out into from the js they were originally imported into, then output them again as their own file after processing.

Image Files

For images, we first test for any file ending in .jpg , .png or .gif . Each file is then output to the output directory with it’s original name and extension.

test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { outputPath: 'themes/my-project/assets/images', name: '[name].[ext]', }, }, ],

In addition to moving the images, we also minify them using imagemin-webpack-plugin.

new ImageminPlugin({ test: /\.(jpe?g|png|gif)$/i, cacheFolder: './imgcache', }),

In order to save a fair bit of time, we also set up an image cache folder locally, to avoid re-processing images that have already been compressed.

SVG Files

With SVGs, we use the svgo-loader. This allows us to make use of svgo, a separate node package that optimises svgs. Inside the loader, we’re passing a few options to help reduce the resulting .svg file size.

test: /\.(svg)$/, use: [ { loader: 'file-loader', options: { outputPath: 'themes/my-project/assets/svgs', name: '[name].[ext]', }, }, { loader: 'svgo-loader', options: { plugins: [ { removeTitle: true }, { convertColors: { shorthex: false } }, { convertPathData: false }, ], }, }, ],

Font File Loading

As with images, we’re just using the file-loader to extract and export our font files to the output directory.

test: /\.(woff(2)?|ttf|eot)$/, use: [ { loader: 'file-loader', options: { outputPath: 'themes/my-project/assets/fonts', name: '[name].[ext]', }, }, ],

Need an Experienced, Expert WordPress Partner? Let’s Work Together? Get Started

The Future of WordPress Development is JavaScript

We hope you find this guide useful and that it gives you some insight into the future of WordPress Development.

Tools like Webpack and REACT are key to developing custom Gutenberg blocks and our specialist WordPress team of engineers has been working super hard to adopt new development workflows which mean we end up writing more and more JavaScript every day.

Over to you Matt:

PS – this announcement was made back in December 2015 so it really is about time you got your WP-JS on 🙂