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.
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?
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?
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 🙂