This is an old post. Information here may be out-dated, or the post may re¬≠Ô¨āect opin¬≠ions or be¬≠liefs I no longer share.

Pre-requisites

Introduction

The fever over task-run­ners like Grunt, Gulp and Broccoli has largely died in 2017 due to su­pe­rior al­ter­na­tives such as Webpack. However, if you’re like me, you’re still us­ing Gulp sim­ply be­cause it works for you and you’re not work­ing on big pro­jects or in big teams to ne­ces­si­tate the change to Webpack.

Since ini¬≠tially learn¬≠ing about LiveReload in 2015, I have moved to Firefox (and for the most part, ap¬≠pre¬≠ci¬≠ate it). This means I do not have ac¬≠cess to an of¬≠Ô¨Ā¬≠cial ad¬≠don as I did on Chrome. Not only this, I nei¬≠ther want to in¬≠stall an ad¬≠di¬≠tional ex¬≠ten¬≠sion for some¬≠thing I do not do 24x7 (hobby web de¬≠vel¬≠oper), nor bloat my Firefox in¬≠stal¬≠la¬≠tion, es¬≠pe¬≠cially if I can help it.

The prob­lem

If you read the ar­ti­cle on im­ple­ment­ing LiveReload, you must have come across the fol­low­ing sec­tion:

Next, we need to down¬≠load the Google Chrome ex¬≠ten¬≠sion LiveReload, go to the Chrome Store and down¬≠load it here. Make sure you can view it in your tool bar and that the cir¬≠cle is Ô¨Ālled in with black. This is im¬≠por¬≠tant or else it won‚Äôt work.

From dmitriz’s gulp-au­toma­tion pro­ject, which in­spired me to un­der­stand his code and im­ple­ment my own from scratch, you can see why he ar­gues for a dif­fer­ent ap­proach:

Many of them are based on in¬≠stalling a Chrome browser ex¬≠ten¬≠sion. However, in ad¬≠di¬≠tion to be¬≠ing lim¬≠ited to the Chrome browser, ex¬≠ten¬≠sions are po¬≠ten¬≠tially vul¬≠ner¬≠a¬≠ble. Extensions can also be guilty to slow down your browser, by adding un¬≠nec¬≠es¬≠sary and slow run¬≠ning scripts block¬≠ing your use¬≠ful con¬≠tent. As ex¬≠ten¬≠sion runs on every page in your browser, it does¬≠n‚Äôt seem to be a good Ô¨Āt for the pur¬≠pose of LiveReload.

Fixing our prob­lem

For the more rel­e­vant part, let’s start dis­cussing what we are go­ing to do to solve the prob­lem.

Run a server on Node us­ing Gulp

We are go­ing to use connect as a server.

npm install --save-dev connect

Open your gulpfile.js, re­quire connect and write a new task called server:

// Require the connect npm plugin
var connect = require("connect");
// Initialize connect
var connectApp = connect();

// connect server
gulp.task("server", function() {
return connectApp.listen(8080);
});

For now, the con¬≠nect server can¬≠not do any¬≠thing. It knows to lis¬≠ten to port 8080 but does not know what the root of the web¬≠site/‚Äčapp is. I‚Äôm serv¬≠ing a sta¬≠tic web¬≠site that out¬≠puts to build/ and so will con¬≠Ô¨Āg¬≠ure it as such us¬≠ing the serve-static mid¬≠dle¬≠ware for connect:

npm install --save-dev serve-static

Editing our gulpfile.js to re¬≠Ô¨āect the new changes:

// Require the connect npm plugin and any additional middleware
var connect = require("connect");
var connectServeStatic = require("serve-static");
// Initialize connect
var connectApp = connect();

// connect server
gulp.task("server", function() {
return connectApp.use(connectServeStatic("build/")).listen(8080);
});

Now our connect server knows to server sta¬≠tic Ô¨Āles from build/ when re¬≠quested over port 8080.

Set up base for LiveReload to use

We‚Äôll add connect-livereload to our pro¬≠ject. All it lit¬≠er¬≠ally does is in¬≠ject a piece of code to your Ô¨Āles while the connect server is run¬≠ning. This means that your built Ô¨Āles will not in¬≠clude this piece of code.

Since it is a mid­dle­ware for our connect server, we’ll use it so.

Install it as usual:

npm install --save-dev connect-livereload

Require the mod­ule in your gulpfile.js and mod­ify the server task to make use of it.

// Require the connect npm plugin and any additional middleware
var connect = require("connect");
var connectServeStatic = require("serve-static");
var connectLiveReload = require("connect-livereload");
// Initialize connect
var connectApp = connect();

// connect server
gulp.task("server", ["html", "scss", "js"], function() {
return connectApp
.use(connectLiveReload({ port: 35729 }))
.use(connectServeStatic("build/"))
.listen(8080);
});

If you re­fresh your page now, you will see the fol­low­ing code in­jected be­fore the close of your body tag:

<script
src="//localhost:35729/livereload.js?snipver=1"
async=""
defer=""
>
</script>

connect-livereload does not pro­vide the ac­tual script, it merely in­jects the code to make it avail­able for gulp-livereload to use, as we’ll see in the next sec­tion.

Start lis­ten­ing for changes

Since you’re here, you must be ac­cus­tomed to gulp.watch.

Currently, this is what our watch task looks like:

gulp.task("watch", ["server"], function() {
gulp.watch("source/**/*.html", ["html"]);
gulp.watch("source/assets/**/*.scss", ["scss"]),
gulp.watch("source/assets/**/*.js", ["js"]);
});

We’ll in­stall gulp-livereload and mod­ify our gulpfile.js ac­cord­ingly:

npm install --save-dev gulp-livereload
var gulpLiveReload = require("gulp-livereload");

gulp.task("watch", ["server"], function() {
// start the LiveReload server
gulpLiveReload.listen();
gulp.watch("source/**/*.html", ["html"]);
gulp.watch("source/assets/**/*.scss", ["scss"]),
gulp.watch("source/assets/**/*.js", ["js"]);
});

Call LiveReload within a task re-run to re­quest a page re­fresh

Finally, for the magic, we’ll add a new pipe to our html, scss, and js tasks to LiveReload when these tasks are called to be run again by gulp.watch. Here is the scss task as a sam­ple:

gulp.task("scss", function() {
return sass("source/assets/**/*.scss")
.pipe(gulp.dest("build/assets/"))
.pipe(gulpLiveReload());
});

One key thing to note is that our server task only runs af¬≠ter html, scss and js tasks have been run once; our watch task also runs only af¬≠ter server task is run. This means that the Ô¨Ārst at¬≠tempt by gulp-livereload to re¬≠load the page will be fu¬≠tile be¬≠cause the LiveReload server is not yet lis¬≠ten¬≠ing for changes. This ap¬≠proach pre¬≠vents un¬≠nec¬≠es¬≠sary re¬≠loads on the Ô¨Ārst run of html, scss and js tasks.

Shortcomings

Each task re­quires an ad­di­tional pipe with a call to gulp-livereload for my im­ple­men­ta­tion to work. dmitriz’s im­ple­men­ta­tion works dif­fer­ently and does not ne­ces­si­tate such a be­hav­iour.

If there are any more short­com­ings in your opin­ion, please feel free to open an is­sue on the GitHub pro­ject, or sub­mit a PR.

An al­ter­nate ap­proach

Another ap­proach is to use gulp.watch and livereload.changed() like so:

gulp.task("watch", ["server"], function() {
// start the LiveReload server
gulpLiveReload.listen();
var watch1 = gulp.watch("source/**/*.html", ["html"]);
watch1.on("changed", function(event) {
gulpLiveReload.changed(event.path);
});
// gulp.watch('source/assets/**/*.scss', ['scss']),
// gulp.watch('source/assets/**/*.js', ['js']);
});

This will, how¬≠ever, re¬≠load the page be¬≠fore the html task Ô¨Ān¬≠ishes run¬≠ning. To counter this, you would have to in¬≠stall run-sequence and con¬≠Ô¨Āg¬≠ure the or¬≠der of tasks. That‚Äôs yet an¬≠other mod¬≠ule we would have to add to our pro¬≠ject. Although it would bring the en¬≠tire re¬≠load logic within a sin¬≠gle watch task, it is ex¬≠ten¬≠sive and un¬≠nec¬≠es¬≠sary in my opin¬≠ion. The run-sequence pro¬≠ject page says it is a tem¬≠po¬≠rary Ô¨Āx and such a Ô¨Āx should not be re¬≠quired with the re¬≠lease of Gulp 4.0. Fingers crossed.