Structuring Your Gulp Workflow
Gulp is a fantastic tool for automating your workflow. From compiling CSS and Javascript, to managing your entire build process, the value it adds to improving your workflow is priceless. Don’t let managing an enormous Gulpfile bog you down.
There are a lot of great articles out there on how to use Gulp to improve your workflow, but something that one seldom comes across is how to effectively manage your workflow.
In this article, I’ll build a small workflow that will watch .scss
files, compile Sass to CSS, and inject changes into your page using BrowserSync, while focusing on a clean and maintainable structure.
BrowserSync is similar to LiveReload, but on steroids. If you don’t yet know what BrowserSync can do to improve your workflow, take a gander at Tuts+’s Intro To BrowserSync.
This article assumes you are familiar with installing and requiring npm packages.
Gulp Folder Structure
Many Gulp workflows are managed via a single Gulpfile. Gulpfiles can quickly become unwieldy.
Large files are a nightmare when writing any sort of code. Splitting concerns to their own files makes navigating and maintaining projects easier.
The following is the go-to structure we’ve found excellent for managing Gulp tasks at Fixate, and will form the basis for our workflow here:
What we’ve got here can be outlined as follows:
our Gulpfile sits in the root of our project
a
gulp/
folder contains our gulp-specific filesinside this folder we keep a config that our tasks will share to keep our settings DRY
we also store our tasks in their very own
tasks/
folder, tooeach category of task gets its own file, properly separating concerns
With this structure in place, we’re ready to get down to building workflow zen.
The Gulpfile
This Gulpfile is responsible for creating a global instance of BrowserSync that we can access in tasks, requiring all the files in the gulp/tasks/
folder, and creating the default
gulp task, which will fire up our currently non-existent watch
task. If you’re not keen on implicitly loading all the tasks with require-dir
, you can always explicitly require them.
Note: I’m not a fan of anything implicit in code, nor global properties, but since this isn’t the actual production code, we let it slide in favour of being pragmatic.
And that’s it! Short and sweet, and we know exactly what our Gulpfile is responsible for.
The Config
Before we get into our tasks, it’s important that repeated configurations and options are managed in their own file to make tasks manageable, and eliminate hard-coding the same values in multiple places.
Most gulp tasks expect a src
– a file or list of files on which to operate, and a dest
– a location to which to output the result of the task.
Let’s add these to our gulpconfig.js
:
What we’re implying from this config is that we have src/scss/
directory where our .scss
files can be found, and a destination at built/css/
– likely where we want our css to be built and served from.
Any configs can be added to this object, and accessed by our tasks.
The Watch Task
In our Gulpfile we added the default
Gulp task. It has one dependency: run the watch
task.
Now that we have our config ready, let’s take a look at our watch
task:
We require gulp
, gulp-watch
, and our gulpconfig
.
We then create the actual watch
task, which first starts BrowserSync, and then watches for changes to .scss
files at the path we defined in our config. When a .scss
file is changed, a css:watch
task will be run, which we are yet to create.
Why css:watch
? We’re going to be splitting our CSS tasks into their own concerns, too.
Gulp is great for creating tasks that perform only one job, and we’re going to lead with that in mind.
The CSS Tasks
So far we’ve got our watch task that will watch for changes to CSS files, and then run a css:watch
task.
Now we can put together our CSS tasks to build our CSS for us.
We now have two tasks.
The first, css
task, is dedicated to compiling .scss
files from our src, automatically adding our prefixes using the excellent Autoprefixer, and writing files out to our destination.
The second, css:watch
task, is responsible for running our first task, and then following up by telling BrowserSync to reload any CSS files. The reason we can tell BrowserSync to reload is because we have access to the global object we defined in our Gulpfile.
Splitting our tasks like this ensures that our tasks are responsible for as little as possible, making them easy to reuse, and easy to understand.
We’re almost done! The only thing missing is our BrowserSync task.
BrowserSync
Our watch
task is elegantly linked to our CSS compilation and BrowserSync reloading.
As in the css:watch
task, we’re going to leverage the global instance of BrowserSync, instantiated in our Gulpfile.
Remember, too, that the browser-sync
task is a dependency on our watch
task – BrowserSync starts up before any watching even begins.
Let’s get our BrowserSync task up and running.
Running the browser-sync
task initialises our BrowserSync instance for the first time, allowing us to notify it when it should reload from other tasks. This is distinct from instantiating it – the BrowserSync instance doesn’t do anything until we explicitly initialise it with browserSync.init()
.
In BrowserSync’s options, we tell it to serve a site from our built/
folder, defined in our config. If we had an index.html
linking our compiled CSS, we would be all set to begin working efficiently on our new project.
Running Gulp
With everything set up, the last thing to do is to run gulp, and get to work!
$ gulp
In a few seconds we’ll have a server running at built/
, watching for changes to our .scss
files, and live injecting those changes into our site.
Wrapping Up
Creating a manageable automated workflow is an easy task with Gulp. Not only can we improve the maintainability by refactoring our folder structure, but we can also think about tasks in a focused way, making it easy to reason about how our tasks work together and on their own, and making adding new tasks effortless.
Contact Larry
Twitter: @LarryBotha
Github: github/larrybotha