I’ve been looking for quite some time how to set up an ideal environment to develop my themes in WordPress using the power of Webpack. After trying out different setups I think I finally found one which matches my way of working.
The basic structure
My biggest issue with previous setups was the fact that my source files were in my theme directory. It felt like I was uploading a theme with unnecessary files in it since we just use the files generated by Webpack.
My new setup solves this issue. In my root directory of the project I have my configuration files (docker-compose.yml, webpack.config.js, …). Next to my configuration files I have a few directorys.
My public directory contains PHP files necessary to run my WordPress and make my theme capable of showing content. The source directory (src) contains my sass and JavaScript files which will be compiled to CSS and vanilla JavaScript. The compiled files will be placed in the public directory and added to git-ignore.
Setting up WordPress with Docker
My setup in Docker is out of the box WordPress provided on Docker Hub together with the MySQL 5.2 container as suggested. You can find all information necessary on the WordPress Docker Hub page, but here’s my Docker Compose file just for your information. As you’ll pobably notice, the theme directory on the WordPress server is mapped to our local public directory.
version: "3.1"
services:
wordpress:
image: wordpress
restart: unless-stopped
ports:
- 8080:80
depends_on:
- db
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
WORDPRESS_DB_NAME: mywp
volumes:
- wordpress:/var/www/html
- ./public:/var/www/html/wp-content/themes/[theme_name]
networks:
- wpNetwork
db:
image: mysql:5.7
restart: unless-stopped
environment:
MYSQL_DATABASE: mywp
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
networks:
- wpNetwork
networks:
wpNetwork:
volumes:
wordpress:
db:
Compiling JavaScript
First thing to do when you want to compile ES6 to ES5 JavaScript is adding the Babel dependencies. Once you’ve done with that, you can easily configure Babel using the .babelrc file.
{
"presets": ["@babel/preset-env"]
}
Second is to make sure our application is build for all browsers we wish to support. Therefor we can use the .browserslistrc file, which is basically a simple text file. You can specify different targets for different environments.
[production]
>0.2%
not dead
not op_mini all
ie 11
[development]
last 1 chrome version
last 1 firefox version
last 1 safari version
Now the only thing left to do is installing and configuring Webpack. This will bundle all of our code an place it in the public directory together with our PHP files.
const path = require('path');
const pkg = require('./package.json');
const webpackConfig = {
mode: "development",
entry: {
app: path.join(__dirname, 'src', 'index.js')
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
// we'll add some CSS stuff in here
]
},
plugins: [
// we'll add some pulgins later
]
};
module.exports = webpackConfig;
Adding some style to it
Next to JavaScript our Sass needs to be compiled into CSS as well. Since WordPress is expecting a style.css
file in the root of our theme, that where we’ll place it. To get a seperate CSS file out of Webpack we’ll use the MiniCssExtractPlugin. Let’s add some rules and configure our plugin to handle our CSS, and assets if necessary.
...
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const webpackConfig = {
mode: "development",
entry: {
app: path.join(__dirname, 'src', 'index.js')
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
module: {
rules: [
// Here was JS configuration
{
test: /\.s?css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
use: {
loader: 'file-loader',
options: {
name: `wp-content/themes/${pkg.name}/img/[name].ext`
}
}
}
]
},
plugins: [
// Another plugin will be added here
new MiniCssExtractPlugin({
disable: false,
filename: 'style.css',
allChunks: true
})
]
};
module.exports = webpackConfig;
Making live a bit easier
So finally, we don’t want to switch constantly between our Webpack development server and our docker container. To make our life as developers a bit easier, we can add the browsersync plugin to our Webpack configuration. This will handle our traffic and deliver us a single endpoint to check our website.
...
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const webpackConfig = {
mode: "development",
entry: {
app: path.join(__dirname, 'src', 'index.js')
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, 'public'),
publicPath: '/'
},
module: {
rules: [
// JS & CSS rules
]
},
plugins: [
new BrowserSyncPlugin({
proxy: {
target: 'http://localhost:8080'
},
files:['**/*.php'],
cors: true,
reloadDelay: 0,
open: false
}),
// MiniExtractCSSPlugin
]
};
module.exports = webpackConfig;
The only thing left to do is add some scripts to our package.json
so we can run our Webpack when necessary
{
...
"scripts": {
"start": "webpack --watch",
"build": "webpack --mode production"
},
...
}