In this post, I’ll go over how to set up Webpack for our WordPress plugin. Webpack, in simple terms, will read an input and parse together an output.
Webpack can be a monster. We’ll have a fairly simple Webpack config that’ll handle our sidebar JavaScript and parse our SASS files.
Webpack has a concept of an entry point (i.e., starting point). In the case of our plugin, the entry point will be /src/js/index.js
.
As a reminder, here’s our current plugin structure:
└── landing-page-gutenberg-template
├── landing-page-gutenberg-template.php
├── autoloader.php
├── languages
├── src
│ ├── js
│ │ └── index.js
│ ├── scss
│ │ ├── style.scss
│ │ └── common.scss
│ ├── languages
│ └── includes
│ └── Include files go here.
├── webpack.config.js
└── .babel
Code language: AsciiDoc (asciidoc)
Our entry point for our SASS files will be /src/scss/style.scss
.
Let’s go ahead and create the empty JavaScript file and the empty SASS files.
Our JavaScript file will just contain a simple alert. Our SASS file will just include an import statement to load in common.scss
.
Here is what I currently have as far as files go. All of the files are currently empty.
Here’s the contents of our JavaScript file:
alert("Hi there");
Code language: JavaScript (javascript)
And our common.scss
and style.scss
files are empty for now.
Here’s some basic Webpack config for parsing our JS file. When Webpack runs successfully, it’ll dump everything in a /dist/
folder in our plugin folder.
const BabelMinifyPlugin = require("babel-minify-webpack-plugin");
module.exports = {
mode: process.env.NODE_ENV,
entry: {
sidebar: ["./src/js/index.js"],
},
output: {
filename: "[name].js",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] },
},
],
},
externals: {
// Use external version of React
react: "React",
},
optimization: {
minimizer: [new BabelMinifyPlugin()],
},
};
Code language: JavaScript (javascript)
As mentioned previously, our JavaScript entry point is at /src/js/index.js. We tell Webpack to look at this file, and if it is determined to be a .js or .jsx file, it loads Babel, prevents React from being included in the bundle, and minifies the output so the file size isn’t astronomical.
We’ll be writing fairly modern JavaScript, which not every browser and client can recognize. Babel allows us to write code in a format we’re comfortable with and compile it down so other clients can recognize and run the code.
You can include Babel-specific functionality in Webpack, but sometimes it’s just cleaner placing them in the .babelrc
file at the root of our plugin.
For example, here’s what our .babelrc
file will look like completed:
{
"presets": ["@babel/env", "@babel/preset-react"],
"plugins": [
"@babel/plugin-proposal-class-properties",
[
"minify-mangle-names",
{ "exclude": { "__": true, "_n": true, "_x": true, "_nx": true } }
]
]
}
Code language: JSON / JSON with Comments (json)
The main crux of the above is that we have several presets and plugins we’ll have to install with Node.
You may be wondering why we have an exclude portion that references common WordPress translation functions like __
and _n
. Since our JavaScript will be minimized, we have to tell Babel that when it minifies our file, to skip these particular function names so they can be translated.
Looking up at the Webpack code example, there are several options we haven’t included. Furthermore, we need to configure our start scripts in our package.json
file.
The easiest way to set up Webpack with Node is to directly modify our package.json file to point to common Webpack modes.
For example, we want a command that’ll allow us to watch our files without having to run a build script every time. We’ll also want a development and production build. So we’ll need a total of three commands.
For brevity sake, here are the commands we want to set up:
Of note is that you can modify the shortcuts above if they are not to your liking, but for the sake of this series, these will be the commands we’ll be using.
"scripts": {
"start": "webpack --watch",
"dev": "webpack --mode development",
"build": "webpack --mode production"
},
Code language: JSON / JSON with Comments (json)
Now if you run npm run start
, you’ll instruct Webpack to watch for any changes and build the files needed. Command npm run dev
will instruct Webpack to build a development build of the files, which is useful for debugging. Command npm run build
is when we’re ready to show the world our completed project.
If we were to run npm run dev
in our plugin folder via command line, Webpack will scream at you saying there are dependencies missing. For example, here’s some output I received running Webpack as-is with no further dependencies installed.
Error: Cannot find module 'mini-css-extract-plugin'
Code language: Bash (bash)
That’s just one of many dependencies we’ll need, and you can run npm run dev indefinitely as you install your dependencies.
Here’s an example of installing the above missing dependency:
Code language: Bash (bash)npm install --save-dev mini-css-extract-plugin
If you were to run npm run dev
again, it’ll error out on the next dependency missing.
First, we’ll want to install all our Babel dependencies via CLI.
Code language: Bash (bash)npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader babel-minify-webpack-plugin babel-plugin-minify-mangle-names
At this point, running npm run dev
should result in sidebar.js
being placed in a dist
folder in our plugin.
If you look at sidebar.js in the dist folder, it’ll have some nice funky syntax like so:
(()=>{alert("Hi there")})();
Code language: JavaScript (javascript)
An additional Babel dependency to install allows us to use the awesome JavaScript arrow functions in our code:
Code language: Bash (bash)npm install --save-dev @babel/plugin-proposal-class-properties
To compile our SASS files to a CSS file, we have to configure Webpack to look at a new entry-point. This can theoretically be done in the same Webpack configuration, but I’ve found it’s a bit cleaner to have them as separate declarations.
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const BabelMinifyPlugin = require("babel-minify-webpack-plugin");
module.exports = [
{
mode: process.env.NODE_ENV,
entry: {
sidebar: ["./src/js/index.js"],
},
output: {
filename: "[name].js",
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: { presets: ["@babel/env"] },
},
],
},
externals: {
// Use external version of React
react: "React",
},
optimization: {
minimizer: [new BabelMinifyPlugin()],
},
},
{
mode: process.env.NODE_ENV,
entry: {
style: ["./src/scss/style.scss"],
},
module: {
rules: [
{
test: /\.scss$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
{
loader: "css-loader",
options: {
sourceMap: true,
},
},
"sass-loader",
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
],
},
];
Code language: JavaScript (javascript)
If we run npm run dev
again, Node will scream at us and telling us we are needing yet even more dependencies to process SCSS files.
Code language: Bash (bash)npm install --save-dev css-loader sass-loader node-sass
Finally, we should be able to run npm run dev
without any further issues (for now).
If all is well, you have fairly blank files in the /dist/
folder of your plugin’s root.
We now have a functioning Webpack build script which will build our JavaScript and CSS files for us. Up next is setting up our main plugin file so we can eventually enqueue the scripts so we can begin working on our Gutenberg sidebar.