Gulp configuration for WordPress — Part II
Here we are for the second part of the tutorial to configure Gulp for the optimal development of WordPress.
If you missed the first part, where we have installed all the modules needed for the tasks we will need, you can find it here.
I remind you that we will write our configuration file gulpfile.babel.js
in JavaScript ES6, as I explained in the first part of this tutorial.
Importing Modules
Let’s start now, importing the downloaded modules into our file gulpfile.babel.js.
import { src, dest, watch, parallel, series } from 'gulp';
import yargs from 'yargs';
import sass from 'gulp-sass';
import cleanCss from 'gulp-clean-css';
import gulpif from 'gulp-if';
import postcss from 'gulp-postcss';
import sourcemaps from 'gulp-sourcemaps';
import autoprefixer from 'autoprefixer';
import imagemin from 'gulp-imagemin';
import webpack from 'webpack-stream';
import del from 'del';
import browserSync from "browser-sync";
We also create the constant that will help us determine if Gulp will have to work in development or production mode.
const PRODUCTION = yargs.argv.prod;
Task style
Let’s write our first task, which will be used to process our SCSS.
The task is set to detect any changes in the SCSS files in the src
folder, and to compile, add prefixes cross-browsers and minify (in case we want to create the build for production) the bundle.scss
file.
If you were wondering what exactly server.stream()
does, it is used to directly inject the CSS into our page without reloading. It is a feature of BrowserSync, we'll see later.
export const styles = () => {
return src('src/scss/bundle.scss')
.pipe(gulpif(!PRODUCTION, sourcemaps.init()))
.pipe(sass().on('error', sass.logError))
.pipe(gulpif(PRODUCTION, postcss([ autoprefixer ])))
.pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'})))
.pipe(gulpif(!PRODUCTION, sourcemaps.write()))
.pipe(dest('dist/css'))
.pipe(server.stream());
};
Task images
The second task that we will write is the one necessary to compress the images.
export const images = () => {
return src('src/images/**/*.{jpg,jpeg,png,svg,gif}')
.pipe(gulpif(PRODUCTION, imagemin()))
.pipe(dest('dist/images'));
};
Copy and clean functions
We also need to copy the CSS file, JS and the images processed in the dist
folder that we will feed to WordPress.
We will also benefit from a function that cleans the dist
folder every time we start our development work.
export const copy = () => {
return src(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'])
.pipe(dest('dist'));
};
export const clean = () => del(['dist'])
Task scripts
Images and SCSS are fine, now we move on to JavaScript.
export const scripts = () => {
return src('src/js/bundle.js')
.pipe(webpack({
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: []
}
}
}
]
},
mode: PRODUCTION ? 'production' : 'development',
devtool: !PRODUCTION ? 'inline-source-map' : false,
output: {
filename: 'bundle.js'
},
}))
.pipe(dest('dist/js'));
};
BrowserSync
We use BrowserSync to automatically reload the page in case we modify a PHP or JS file, while we use server.stream()
that we added to the task style to directly inject the CSS into the page, without the need to reload.
We point the BrowserSync server on our local server, indicating in proxy
our domain.
As I explained in this post, I suggest you to use a different domain from .local
, to avoid problems of slowdown in the update of the BrowserSync server that would frustrate all the work.
const server = browserSync.create();
export const serve = done => {
server.init({
proxy: "https://project.dev/" // your local website link
});
done();
};
export const reload = done => {
server.reload();
done();
};
Task watch
Obviously we want everything to be automated, and with the task watch, we can tell gulp to start tasks whenever we update a file.
export const watchForChanges = () => {
watch('src/scss/**/*.scss', series(styles));
watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', series(images, reload));
watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], series(copy, reload));
watch('src/js/**/*.js', series(scripts, reload));
watch("**/*.php", reload);
};
Final step
Finally, let’s add all our tasks in the build
and dev
tasks.
We will use build
to compile the files ready for deployment, while dev
will be the task we will use throughout the development phase.
As already explained, the distinction is useful to be able to work faster in development, avoiding unnecessary tasks to Gulp as the one in minifying files.
export const dev = series(clean, parallel(styles, images, copy, scripts), serve, watchForChanges);
export const build = series(clean, parallel(styles, images, copy, scripts));
export default dev;
Then add the tasks to the file package.json
so you have both tasks at hand
"scripts": {
"start": "gulp",
"build": "gulp build --prod"
},
Conclusions
Finally, here is the file gulpfile.babel.js
in its entirety:
import { src, dest, watch, parallel, series } from 'gulp';
import yargs from 'yargs';
import sass from 'gulp-sass';
import cleanCss from 'gulp-clean-css';
import gulpif from 'gulp-if';
import postcss from 'gulp-postcss';
import sourcemaps from 'gulp-sourcemaps';
import autoprefixer from 'autoprefixer';
import imagemin from 'gulp-imagemin';
import webpack from 'webpack-stream';
import del from 'del';
import browserSync from "browser-sync";
const PRODUCTION = yargs.argv.prod;
export const styles = () => {
return src('src/scss/bundle.scss')
.pipe(gulpif(!PRODUCTION, sourcemaps.init()))
.pipe(sass().on('error', sass.logError))
.pipe(gulpif(PRODUCTION, postcss([ autoprefixer ])))
.pipe(gulpif(PRODUCTION, cleanCss({compatibility:'ie8'})))
.pipe(gulpif(!PRODUCTION, sourcemaps.write()))
.pipe(dest('dist/css'))
.pipe(server.stream());
};
export const images = () => {
return src('src/images/**/*.{jpg,jpeg,png,svg,gif}')
.pipe(gulpif(PRODUCTION, imagemin()))
.pipe(dest('dist/images'));
};
export const copy = () => {
return src(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'])
.pipe(dest('dist'));
};
export const clean = () => del(['dist']);
export const scripts = () => {
return src('src/js/bundle.js')
.pipe(webpack({
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: []
}
}
}
]
},
mode: PRODUCTION ? 'production' : 'development',
devtool: !PRODUCTION ? 'inline-source-map' : false,
output: {
filename: 'bundle.js'
},
}))
.pipe(dest('dist/js'));
};
const server = browserSync.create();
export const serve = done => {
server.init({
proxy: "https://project.dev/" // your local website link
});
done();
};
export const reload = done => {
server.reload();
done();
};
export const watchForChanges = () => {
watch('src/scss/**/*.scss', series(styles));
watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', series(images, reload));
watch(['src/**/*','!src/{images,js,scss}','!src/{images,js,scss}/**/*'], series(copy, reload));
watch('src/js/**/*.js', series(scripts, reload));
watch("**/*.php", reload);
};
export const dev = series(clean, parallel(styles, images, copy, scripts), serve, watchForChanges);
export const build = series(clean, parallel(styles, images, copy, scripts));
export default dev;
All we have to do is import the bundle.js
and bundle.css
files into WordPress, adding the import function to functions.php
function _yourtheme_assets() {
wp_enqueue_style( '_yourtheme-stylesheet', get_template_directory_uri() . '/dist/css/bundle.css', array(), '1.0.0', 'all' );
wp_enqueue_script( '_yourtheme-scripts', get_template_directory_uri() . '/dist/js/bundle.js', array(), '1.0.0', true );
}
add_action('wp_enqueue_scripts', '_yourtheme_assets');
Remember to edit _yourtheme
with the name of your theme.
You just have to work on your new project, giving npm run start
from the terminal during the development phase, and npm run build
for the final build ready for deployment.
Originally published at https://dannyspina.com on September 4, 2019.