- Updated on 11 July 2019:
– removed
gulp-util
(deprecated) – updated thegulp-deporder
package to^1.2.0
, now withoutgulp-util
dependency.
In this article I will show you how to use Gulp to setup a smooth WordPress themes development workflow, and create a boilerplate to be used as reference for your new themes development.
Gulp is a task runner tool to automate and enhance your development workflow, in general. It is used to implement tasks such as optimizing images, compressing scripts, compiling Sass to CSS, and watch files for changes.
TLTR;
You could skip all the details and go directly to the Summing up section at the end.
Workflow Requirements
- The
theme source files
can be managed in a single folder and repository without polluting the build or WordPress folders. - The final
built theme files
are created in a separate folder, and consist only in the files WordPress requires to run the theme on the webserver (placed typically in a/wp-content/themes/my-new-theme
folder). - Gulp, its plug-ins and other applications are not contained in the built theme folder. They cannot be accidentally copied to a production server which is unnecessary and could have security implications.
Automated tasks
This boilerplate will implement the following tasks:
- Copying all newer theme files.
- Optimizing images.
- Javascript files processing and concatenation.
- Sass to PostCSS to native CSS conversion.
- Watch files for changes.
PostCSS will automatically:
- Add asset references.
- Apply vendor prefixes.
- Group together all matching Media Queries.
- Minify the resulting CSS code.
Watch files for changes, purpose:
- Keep all the files updated and in sync with the
built theme files
in the destination folder. - Inject CSS changes without reload the whole page (using Browsersync).
- Automatically refresh page each time a .php/.js file, an image, or any file of the theme changes (again using BrowserSync).
Getting Started
Let’s go on step by step, installing Node.js and Gulp, and add the configuration to execute the tasks we want.
Install Node.js
Gulp requires you to download and install first Node.js, which will also install NPM, which is its package manager, and it is used to install modules.
To verify that you have installed Node.js and NPM correctly, open the terminal and check their current version entering the commands: node -v
and npm -v
.
Create a Node.js Project
- Open a terminal.
- Create a directory for your project with
npx mkdirp my-project
. Renamemy-project
as you wish, this will be yourproject source folder
root. - Navigate into it with
cd my-project
. - Initialize a project with
npm init
.
This will guide you through giving your project a name, version, description, etc., and finally will create a package.json
file, which is used to keep track of various information about the project such as its dependencies.
Install Gulp
- If you’ve previously installed gulp globally, run
npm rm --global gulp
before following these instructions. -
Install the gulp command line utility, run:
npm install --global gulp-cli
. - After having created a
package.json
file, navigate into the Node.js project folder, if not already there (e.g. withcd my-project
). - Install the gulp package in your devDependencies with
npm install --save-dev gulp@next
. - Verify your gulp versions with
gulp --version
. You should have aCLI
and aLocal
version as well.
A node_modules
folder will be created which contains the module code. That should be omitted from your source control system, therefore add node_modules to your .gitignore
file.
Note: If you are using npm
version >5 package-lock.json
is intended to be checked into source control.
Create a gulpfile.js
Tasks in Gulp are added and configured through a gulpfile.js
file in your project source folder
.
Each Gulp task is an asynchronous JavaScript function, and can be considered public or private.
- Public tasks are exported from your gulpfile, which allows them to be run by the gulp command.
- Private tasks are made to be used internally, usually used as part of
series()
orparallel()
composition.
A private task looks and acts like any other task, but an end-user can’t ever execute it independently. To register a task publicly, export it from your gulpfile.js
with export.taskname = taskfunction;
.
Let’s create a test configuration file:
-
Using your text editor, create a file named
gulpfile.js
in your project root with these contents:function defaultTask(cb) { // place code for your default task here cb(); } exports.default = defaultTask
- Run the gulp command in your project directory:
gulp
. - The default task will run and do nothing. It’s a good sign, you can go ahead.
- You can empty the
gulpfile.js
, this was just a test.
Project Files
I organize all my files under a project source folder
, containing the Node.js files (node_modules
folder, package.json
, and package-lock.json
), Gulp configuration files (gulpfile.js
), and the theme source files
which I keep under a ./src
folder.
The theme source files
are the source files containing your CSS, Javascript files, and images, to be processed, plus all the normal theme files which usually are just copied as they are. Gulp will process, manipulate, minify all, and copy all of the built theme files
to a specific destination folder
(e.g. /wp-content/themes/my-new-theme
).
I use Vagrant for my WordPress development, therefore, the destination folder
of the build is in my case, for a test WordPress website named wp-my-new-theme-test
, for a new theme named my-new-theme
:
vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/
I add a further four sub-folders to my project source folder
(the ./src
folder):
theme
– The WordPress theme files (all the PHPs, screenshot.png, and the language files), these are just copied as they are, and kept in sync with thedestination folder
.images
– Images used by your theme.scss
– Sass SCSS source files.js
– Any number of separate client-side JavaScript source files.
My working tree looks like this one:
my-new-theme (project source folder)
│
├── node_modules (add it to your .gitignore)
│
├── src (theme source files)
│ ├── images
│ ├── js
│ ├── scss
│ └── theme (the WordPress theme files)
│
├── gulpfile.js
├── package.json
├── package-lock.json (since npm >5 to be checked in)
└── ... other configuration files
Adding Tasks to Gulp
Some tasks in this tutorial introduce the need of packages dependency which need to be installed. These are the tasks Gulp will be in charge of:
- Synching Theme Files
- Optimizing Images
- Saas Compilation
- JavaScript Processing
- Run All Tasks at once
- Enable File Watching and Browsersync
- Add a Default Task
Installing and Including Dependencies
From the project source folder
, run the following npm command to install all the Gulp plug-ins as development dependencies:
npm install --save-dev autoprefixer browser-sync css-mqpacker cssnano gulp-concat gulp-deporder gulp-imagemin gulp-newer gulp-postcss gulp-sass gulp-strip-debug gulp-uglify gulp-noop postcss-assets
Note: gulp-util
has been deprecated. It was used by the gulp-deporder
package in the versions <1.2.0
. Please update your gulp-deporder
package to ^1.2.0
release.
Using your text editor, edit the file gulpfile.js
in your project source folder
, replace its content, if any, with these contents:
// Gulp.js configuration
'use strict';
const
// ** change these two to yours **
wordpress_project_name = 'your-site',
theme_name = 'your-site',
browserSyncProxy = 'http://local.your-site.test/',
// source and build folders, ** change this to yours **
dir = {
src : 'src/',
build : `../../vagrant-local/www/${wordpress_project_name}/public_html/wp-content/themes/${theme_name}/`
},
// Gulp and plugins
{ src, dest, series, parallel, watch } = require('gulp'),
noop = require("gulp-noop"),
newer = require('gulp-newer'),
imagemin = require('gulp-imagemin'),
sass = require('gulp-sass'),
postcss = require('gulp-postcss'),
deporder = require('gulp-deporder'),
concat = require('gulp-concat'),
stripdebug = require('gulp-strip-debug'),
uglify = require('gulp-uglify'),
browserSync = require('browser-sync').create(),
log = require('fancy-log')
;
// For BrowserSync
const reload = (cb) => { browserSync.reload(); cb(); };
Note: It’s your task to set correctly the values of wordpress_project_name
, theme_name
, dir.src
, dir.build
(basically source and build destination which probably will differ from mine, and you can take it as an example).
Synching Theme Files
The files in the theme
folder of the project source folder
should be simply copied to the destination folder
, no processing needed. These are all the theme PHP files, the screenshot.png, and all the language files in the folder language
.
Append the following configuration task to your file gulpfile.js
:
// theme files settings
const themefiles_settings = {
src : dir.src + 'theme/**/*',
build : dir.build
};
// copy theme files
const themefiles = function() {
return src(themefiles_settings.src)
.pipe(newer(themefiles_settings.build))
.pipe(dest(themefiles_settings.build));
};
exports.themefiles = themefiles;
// ---
Save, and create any file or folder in the source template folder. Then enter the following command:
gulp themefiles
All the files under theme
folder will be recursively copied to the destination folder
(specified in dir.dest
, in my case that would be vagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/
).
Optimizing Images
Image files can often be compressed further using tools such as imagemin. Add the following code to gulpfile.js
:
// image settings
const images_path = 'assets/images/';
const images_settings = {
src : dir.src + 'images/**/*',
build : dir.build + images_path
};
// image processing
const images = function() {
return src(images_settings.src)
.pipe(newer(images_settings.build))
.pipe(imagemin())
.pipe(dest(images_settings.build));
};
exports.images = images;
// ---
Save then run gulp images
. Compressed versions of any new or updated images in the source images
folder are copied to the destination folder
.
Saas Compilation
// CSS settings
var scss_settings = {
src : dir.src + 'scss/style.scss',
watch : dir.src + 'scss/**/*',
build : dir.build,
sassOpts: {
outputStyle : 'nested',
imagePath : images_settings.build,
precision : 3,
errLogToConsole : true
},
processors: [
require('postcss-assets')({
loadPaths: [images_path],
basePath: dir.build,
baseUrl: `/wp-content/themes/${theme_name}/`
}),
require('autoprefixer')({
browsers: ['last 2 versions', '> 2%']
}),
require('css-mqpacker'),
require('cssnano')
]
};
// SCSS processing
const scss = function() {
return src(scss_settings.src)
.pipe(sass(scss_settings.sassOpts))
.pipe(postcss(scss_settings.processors))
.pipe(dest(scss_settings.build))
.pipe(browserSync.active ? browserSync.reload({ stream: true }) : noop());
};
const css = series(images, scss);
exports.css = css;
// ---
Launch this new task with gulp css
to:
- run the Gulp
images
task first (images may be required in your CSS) - compile the Sass code in the source
scss/style.scss
file using the fastLibSass
compiler - use
PostCSS
to automatically add asset references, apply vendor prefixes, pack media queries together, and minify the resulting CSS code - output the stylesheet to the
destination folder
(specified indir.dest
, in my case that would bevagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/
). - force a Browsersync CSS reload (more about that later).
The source scss/style.scss
file must include the WordPress theme meta data at the top. It is important to use /*!
as the first line. This ensures the cssnano
minifier does not remove the comment and render your theme unusable.
/*!
Theme Name: Gutenberg
Theme URI: https://github.com/WordPress/gutenberg-theme/
Author: wordpressdotorg
Author URI: https://wordpress.org
Description: Showcasing themeing with Gutenberg.
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: LICENSE
Text Domain: gutenbergtheme
Tags: translation-ready
This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned with others.
gutenbergtheme is based on Underscores http://underscores.me/, (C) 2012-2016 Automattic, Inc.
Underscores is distributed under the terms of the GNU GPL v2 or later.
Normalizing styles have been helped along thanks to the fine work of
Nicolas Gallagher and Jonathan Neal http://necolas.github.io/normalize.css/
*/
// etc...
The postcss-assets
plugin allows you to refer to image assets using code such as:
.widget1 {
width: width('myimage.jpg');
height: height('myimage.jpg');
background-image: resolve('myimage.jpg');
}
You can also inline images with automatic Base64 encoding:
.widget2 {
background-image: inline('myimage.jpg');
}
JavaScript Processing
Add the following code to gulpfile.js:
// JavaScript settings
const js_settings = {
src : dir.src + 'js/**/*',
build : dir.build + 'js/',
filename : 'scripts.js'
};
// JavaScript processing
const js = function() {
return src(js_settings.src)
.pipe(deporder())
.pipe(concat(js_settings.filename))
.pipe(stripdebug())
.pipe(uglify())
.pipe(dest(js_settings.build))
.pipe(browserSync.active ? browserSync.reload({ stream: true }) : noop());
};
exports.js = js;
// ---
Run this new task with gulp js to:
- process all JavaScript files in the source
js
folder - order the files appropriately. Add comments at the top of your JavaScript files to declare dependencies, e.g.
// requires: lib1.js
or// requires: config.js lib1.js
(have a look atgulp-deporder
package’s help). - concatenate into a single file
- strip all debugging and console logging statements
- minify the code
- output the resulting code to the
destination folder
(specified indir.dest + '/js/scripts.js'
, in my case that would bevagrant-local/www/wp-my-new-theme-test/public_html/wp-content/themes/my-new-theme/js/scripts.js
). - force a Browsersync CSS reload (more about that later).
Run All Tasks
Rather than calling each task separately, we can add the following code to gulpfile.js
:
// run all tasks
const build = parallel(themefiles, css, js);
exports.build = build;
// ---
You can now use gulp build
to run the themefiles
, js
, css
and images
tasks in parallel. (Note images
is a dependency of the css
task so we need not call it directly.)
Enable File Watching and Browsersync
Your workflow can be radically improved by:
- Letting Gulp watch for file changes before launching the appropriate task.
- Automatically reloading CSS and JavaScript files when they change (without a page refresh).
- Automatically refreshing the page when a template file changes.
First, we need to define a browsersync
task in gulpfile.js
. This will create a proxy server to your web server running WordPress on localhost
(change this domain or use an IP address as necessary):
// Browsersync options
const syncOpts = {
proxy : browserSyncProxy,
open : false,
notify : false,
ghostMode : false,
watch : false, // do not refresh on changes automatically
injectChanges: true, // Inject CSS changes
ui: {
port: 8001
}
};
Now add a watch task to run Browsersync, watch for file changes and run the appropriate task:
// watch for file changes
const watch_all = function() {
browserSync.init(syncOpts);
// page changes
watch(themefiles_settings.src, series(themefiles, reload));
// image changes
watch(images_settings.src, series(images, reload));
// CSS changes
watch(scss_settings.watch, css);
// JavaScript main changes
watch(js_settings.src, js);
};
exports.watch_all = watch_all;
Add a Default Tasks
Finally, add a default Gulp task which runs an initial build and starts the watch task:
// default task
exports.default = series(build, watch_all);
// ---
Now run gulp from the command line. The console will display output which includes lines similar to:
Rather than loading your development site from http://localhost/, enter the address http://localhost:3000/ or the External URL if you are viewing from another device. Your WordPress site will load as before but Gulp will watch for changes and apply the updates immediately. You’ll need never switch to your browser and click refresh again!
Hit Ctrl/Cmd + C
when you want to stop Gulp processing.
TLTR - Summing up
To summarize in a few steps:
- Download and install first Node.js.
- Fork or clone the boilerplatw: wp-theme-gulp-basic
- Go into the project folder
cd wp-theme-gulp-basic
. - Initialize the project with
npm install
. - Create the
project source folder
withmkdir src src/images src/js src/scss src/theme
. - Copy your theme’s files into
./src/theme
. - Place your
style.scss
intosrc/scss
or create an empty one withtouch src/scss/style.scss
(this file is required). - Place into the
src/images
,src/js
, andsrc/scss
folders everything that must be processed by Gulp. - Run
gulp
(Ctrl/Cmd + C
when you want to stop watching).
References
The following articles were of big help in my investigation:
- Develop WordPress Themes Faster with Gulp
- How we use Sass and Gulp in our WordPress theme & plugin development workflow
- Introducing WPGulp: An Easy to Use WordPress Gulp Boilerplate
- My Advanced Gulp Workflow for WordPress Themes
If this article was useful to you, please share it using your favorite way for doing it! If you have suggestions of improvement or you’ve found something wrong, please let me know!