A webpack.config.js for WordPress Projects
Tags: webpack, wordpress, bundler, productivity
Published: 2023-07-31 13:41:29

That's another of those posts in which my **main goal is to document it** for my own future reference and with some hope can be useful for other developers. Configuring webpack manually can take a considerable amount of time, so I thought it's a good idea to keep things documented somewhere, and since I am 'documenting' why not share with others?

I would like to add that **you can extend the webpack as you want**, according to the needs of your project. That's a basic configuration that **covers basic needs to starting a WordPress theme for example**.

If you are reading and interested in **setting webpack in a WordPress project**, I am assuming you have some understanding about, obviously WordPress projects like custom themes or custom plugins.  And I  also believe that you know about setting up a WordPress development environment, either using a [Docker or XAMPP](https://dev.to/sarahcssiqueira/10-reasons-i-moved-from-xampp-to-docker-1j6l), so I will not write about those topics. I will not write about how to enqueue/register the assets generated by webpack either. If necessaire, you can read more about it in [this article](https://kinsta.com/blog/wp-enqueue-scripts/) or the official [WordPress documentation](https://developer.wordpress.org/reference/functions/wp_register_script/).

## About webpack

Some knowledge about webpack can be very handy, but I will do my best trying to explain one thing or another.

[Webpack](https://webpack.js.org/) is a **free and open-source module bundler** based on Node.js for JavaScript. It is very "famous" in the modern javascript frameworks world, and although it was made primarily for JavaScript, it also can transform front-end assets such as CSS, and images **if the corresponding loaders are included** (this is the reason I will be using so many dependencies below). 

In a **WordPress project** like [this example theme](https://github.com/sarahcssiqueira/base-wp-theme), webpack avoids multiple requests, taking all our javascript and css files, and bundling all of them together in a minified file. It also increases my productivity, as, well, **I don't need to manually compile and minify things**.

## Requirements

- [Node.js](https://nodejs.org/en) 14.0.0 or later. 
- [NPM]
- [webpack](https://webpack.js.org/) 4.8.3 and newer.

## Creating the package.json

Create the **package.json** by typing the command `npm init`. Then install the required dependencies:

```
npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react
@wordpress/dependency-extraction-webpack-plugin  
babel-loader clean-webpack-plugin copy-webpack-plugin 
cross-env css-loader css-minimizer-webpack-plugin 
file-loader mini-css-extract-plugin
sass-loader terser-webpack-plugin 
webpack webpack-cli -D
```

or after creating your package.json, edit it and paste the following in the file:

```
{
  "devDependencies": {
    "@babel/cli": "^7.22.9",
    "@babel/core": "^7.22.9",
    "@babel/preset-env": "^7.22.9",
    "@babel/preset-react": "^7.22.5",
    "@wordpress/dependency-extraction-webpack-plugin": "^4.21.0",
    "babel-loader": "^9.1.2",
    "clean-webpack-plugin": "^4.0.0",
    "copy-webpack-plugin": "^11.0.0",
    "cross-env": "^7.0.3",
    "css-loader": "^6.8.1",
    "css-minimizer-webpack-plugin": "^5.0.1",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^2.7.6",
    "sass-loader": "^13.3.2",
    "terser-webpack-plugin": "^5.3.9",
    "webpack": "^5.86.0",
    "webpack-cli": "^5.1.4"
  }
```

Then, run `npm install`. The dependencies will be installed. 

This project was updated in May 2023 and maybe necessaire to **replace some dependencies** in the future. So it's important you know more about each dependency to be able to solve any issue that may arise (I am also assuming you know the term [dependency hell](https://en.wikipedia.org/wiki/Dependency_hell)).

You can read about each dependency used in this article in the links below:

- [@babel/cli ](https://babeljs.io/docs/babel-cli)

- [@babel/core ](https://babeljs.io/docs/babel-core)

- [@babel/preset-env ](https://babeljs.io/docs/babel-preset-env)

- [@babel/preset-react](https://babeljs.io/docs/babel-preset-react) 

- [babel-loader ](https://github.com/babel/babel-loader)

- [clean-webpack-plugin ](https://www.npmjs.com/package/clean-webpack-plugin)

- [copy-webpack-plugin](https://webpack.js.org/plugins/copy-webpack-plugin/)

- [cross-env ](https://www.npmjs.com/package/cross-env)

- [css-loader](https://webpack.js.org/loaders/css-loader/)

- [css-minimizer-webpack-plugin](https://webpack.js.org/plugins/css-minimizer-webpack-plugin/) 

- [file-loader ](https://v4.webpack.js.org/loaders/file-loader/) Example of dependency that is deprecated for webpack v5, it's recommended migrate to [asset modules](https://webpack.js.org/guides/asset-modules/) dependency (which I still didn't).

- [mini-css-extract-plugin](https://www.npmjs.com/package/mini-css-extract-plugin)

- [sass-loader](https://www.npmjs.com/package/sass-loader) 

- [terser-webpack-plugin](https://webpack.js.org/plugins/terser-webpack-plugin/) 

- [webpack](https://www.npmjs.com/package/webpack)

- [webpack-cli](https://www.npmjs.com/package/webpack-cli)

## The webpack.config.js

Now let's set up **our webpack.config.js** file. See the complete [file on Gist](https://gist.github.com/sarahcssiqueira/67cdfa82fd3f310719e7f076010ae13b#file-webpack-config-js).

We will need export modules:

```
/**
 * Webpack configuration.
 */

module.exports = (env, argv) => ({
    entry: {},
    output: {},
});
```
### Entry and Output points

We need to define an entry point and an output. Let's place those out for the sake of readability:

```
const entry = {};
const output = {};

module.exports = (env, argv) => ({
    entry: entry,
    output: output,

});
```

Our entry/output paths: 

```
const entry = {
    main : require('path').resolve(__dirname, 'src/js' ) + '/index.js'
};

const output = {
    path: require('path').resolve(__dirname, 'build'),
    filename: 'js/[name].js'
};
```
Rewriting:

```
const path = require('path');
const JS_DIR = path.resolve(__dirname, 'src/js' );
const BUILD_DIR = path.resolve(__dirname, 'build');

const entry = {
    main : JS_DIR + '/index.js'
};
const output = {
    path: BUILD_DIR,
    filename: 'js/[name].js'
};
```
### devtool

Let's set our devtool in the module exports, this option controls if and how source maps are generated. For more information and options you can choose, please [refer to the documentation](https://webpack.js.org/configuration/devtool/). I choose the 'source-map' 

```
devtool: 'source-map',
```

### Rules

Yet in the module exports, let's add some rules:

```
    module: {
        rules: rules,
    }
```

Those are "my rules" for Javascript and CSS:

```
const rules = [
    {
        test: /\.js$/,
		include: [JS_DIR],
		exclude: /node_modules/,
		use: "babel-loader",
    },
    {
		test: /\.scss$/,
		exclude: /node_modules/,
		use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
	},
];
```

### Plugins 

#### CSS

Webpack doesn't understand CSS by default, so we will need some plugins, in this case, the [MiniCssExtractPlugin](https://webpack.js.org/plugins/mini-css-extract-plugin/) already installed above.

Require it at the top of our webpack.config adding:

`const MiniCssExtractPlugin = require( "mini-css-extract-plugin" );`

Add plugins in the module exports:

` plugins: plugins(argv) => [];`

And then the CSS plugin itself:

```
 new MiniCssExtractPlugin(
        {
            filename: "css/[name].css",
        }
    ),
    
```

#### Images

Webpack doesn't understand images either. The chosen plugin for handling it was the [file-loader](https://v4.webpack.js.org/loaders/file-loader/), yes, that one I am considering replacing as soon as I find time.

```
  {
		test: /\.(png|jpg|svg|jpeg|gif|ico)$/,
		use: {
			loader: "file-loader",
			options: {
				name: "[path][name].[ext]",
				publicPath: "production" === process.env.NODE_ENV ? "../" : "../../",
			},
		},
	},
```

#### Clean weppack plugin

A webpack plugin to remove/clean your build folder(s).

`const { CleanWebpackPlugin } = require( "clean-webpack-plugin" );`

Create a plugin (only for production mode):

```
new CleanWebpackPlugin(
	{
		cleanStaleWebpackAssets: "production" === argv.mode,
	}
),
```

#### Copy Files

```
new CopyPlugin({
    patterns: [{ from: IMG_DIR, to: BUILD_DIR + "/img" }],
  }),
```

### Optimization

Require the dependencies on top of the webpack file:

```
const CssMinimizerPlugin = require( "css-minimizer-webpack-plugin" );
const TerserJsPlugin = require( "terser-webpack-plugin" );
```

And inside the module.exports:

```
optimization: {
    minimizer: [
                new CssMinimizerPlugin(), 
                new TerserJsPlugin()
],
    minimize: true,
		},
```

## Externals for webpack

With external you can exclude some libraries from the bundler, for example, jquery:

```
externals: {
	jquery: "jQuery",
},
```

## Scripts

To help us work with all this, let's set some scripts in the  package.json:

```
scripts: {
"prod": "cross-env NODE_ENV=production webpack --mode production
        --progress",
"watch": "cross-env NODE_ENV=development webpack --watch 
         --mode development --progress"
}
```

The complete webpack.config.js and devDependencies are available [on this GitHub Gist](https://gist.github.com/sarahcssiqueira/67cdfa82fd3f310719e7f076010ae13b). If it was useful for you, please comment below and consider star the gist.