gulp.js livereload with express server? –
I am trying to setup my gulpfile.js
to use livereload on an express server without much luck. I see examples out there but they seem to be related to a static file server.
http://code.tutsplus.com/tutorials/gulp-as-a-development-web-server–cms-20903
http://rhumaric.com/2014/01/livereload-magic-gulp-style/
So I have an app.js
file which does the standard express server with jade files, etc. What I want to do is get it to work with livereload from a gulp.js boot.
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
There are lots of plugins like gulp-livereload
, connect-livereload
, gulp-connect
, gulp-watch
so how can I get this wired up?
Solution :
I’ve added code that
-
Detects changes in server files and reloads the server via nodemon
-
Waits for a couple seconds after process reload in order to give the server time to run its initialization code.
-
Triggers a change in a livereload server
note 1 : Your build should also include a livereload server and attach livereload scripts to html files before calling the ‘serve’ task
note 2: This is an asynchronous task that never ends, do not use it as a dependency of other tasks
gulp.task('serve', function (cb) {
nodemon({
script : <server start file>,
watch : <server files>
//...add nodeArgs: ['--debug=5858'] to debug
//..or nodeArgs: ['--debug-brk=5858'] to debug at server start
}).on('start', function () {
setTimeout(function () {
livereload.changed();
}, 2000); // wait for the server to finish loading before restarting the browsers
});
});
Here’s my solution:
//gulpfile.js:
var gulp = require('gulp');
var nodemon = require('gulp-nodemon');
var server = require('gulp-express');
var lr = require('tiny-lr')();
gulp.task('default', function () {
nodemon({
script: 'server.js'
})
.on('restart', function () {
console.log('restarted!')
});
lr.listen(35729);
gulp.watch('server/**/*', function(event) {
var fileName = require('path').relative('3000', event.path);
lr.changed({
body: {
files: [fileName]
}
});
});
});
You also need to include connect-livereload in your express server:
app.use(require('connect-livereload')());
Include it before bodyParser. I have read that this is not needed if you have the chrome live reload extension.
gulp-express is the right thing for you. It runs your express script as a server, with livereload!
I had the same issue and was not able to find anything related. My solution ends up with the following piece of code in Gulpfile
:
var fork = require('child_process').fork;
var tinyLr = require('tiny-lr');
var async = require('async');
var plugins = require('gulp-load-plugins')({ lazy: false });
var lr = null;
var lrPort = 35729;
var buildDir = 'build';
var serverAppFile = path.join(__dirname, 'build/server/app.js');
// This guy starts and stops nodejs process which runs our Express app
var app = {
start: function(callback) {
process.execArgv.push('--debug');
var instance = app.instance = fork(serverAppFile, {
silent: true
});
app.dataListener = function(data) {
var message = '' + data;
// Assume that server is started when it prints the following to stdout
if (message.indexOf('Express server listening on port') === 0) {
callback();
}
};
instance.stdout.on('data', app.dataListener);
instance.stdout.pipe(process.stdout);
instance.stderr.pipe(process.stderr);
},
stop: function(callback) {
app.instance.stdout.removeListener('data', app.dataListener);
plugins.util.log('Killing Express server with PID', app.instance.pid);
app.instance.kill('SIGTERM');
callback();
}
};
// build-assets task actually processes my client js, less, images, etc and puts them to build dir
// build-server task copies server files (app.js, controllers, views) to build dir
gulp.task('serve', ['build-assets', 'build-server'], function(callback) {
async.series([
app.start,
function(callback) {
lr = tinyLr();
lr.listen(lrPort, callback);
}
], callback);
});
gulp.task('watch', ['serve'], function() {
// Reload page if built files were updated
gulp.watch([
buildDir + '/**/*.handlebars',
buildDir + '/**/*.html',
buildDir + '/**/*.js',
buildDir + '/**/*.css'
], function(event) {
var fileName = path.relative(path.join(__dirname, buildDir), event.path);
plugins.util.log('Detected updated file ' + fileName + ', reloading server and page in browser');
async.series([
// Restart Express server
app.stop,
app.start,
// Send reload notification to browser
function(callback) {
lr.changed({
body: {
files: [fileName]
}
});
callback();
}
]);
});
// Perform build steps on source file modifications
gulp.watch(['app/**/*.js', '!app/**/*.spec.js'], ['build-app-js']);
gulp.watch(['app/**/*.html'], ['build-templates']);
gulp.watch(['app/**/*.less'], ['build-less']);
gulp.watch(['server/**/*.*'], ['build-server']);
});
You’ll need to install tiny-lr
, async
, gulp-util
and gulp-load-plugins
packages for this sample to work.
I guess that I’ll create a separate Gulp plugin for it =)
Take a look at gulp-nodemon which will restart your server when code changes.
Example:
gulp.task('develop', function () {
nodemon({ script: 'app.js', ext: 'html js' })
.on('restart', function () {
console.log('restarted!')
})
})
Live reload should work with any nodejs script. Here is a good gist.
To livereload both front and backend changes to the browser. There is also the option of not using Gulp. Then, the right combination of packages is ‘livereload’, ‘connect-livereload’, and a little help from ‘nodemon’. Here’s how they team up:
livereload
opens a high port and notifies the browser of changed public filesconnect-livereload
monkey patches every served HTML page with a snippet that connects to this high portnodemon
is then used to restart the server on changed backend files
Set up livereload in Express
You’ll want to set up the Express to both start livereload server watching the public directory and ping the browser during nodemon-induced restart:
const livereload = require("livereload");
const connectLivereload = require("connect-livereload");
// open livereload high port and start to watch public directory for changes
const liveReloadServer = livereload.createServer();
liveReloadServer.watch(path.join(__dirname, 'public'));
// ping browser on Express boot, once browser has reconnected and handshaken
liveReloadServer.server.once("connection", () => {
setTimeout(() => {
liveReloadServer.refresh("/");
}, 100);
});
const app = express();
// monkey patch every served HTML so they know of changes
app.use(connectLivereload());
Start Express with nodemon
Then you’d start the server with nodemon, for example, with a dedicated watch script npm run watch
.
The key point here is to ignore the public directory that’s already being watched by livereload. You can also configure files with non-default extensions, like pug and mustache, to be watched.
"scripts": {
"start": "node ./bin/www",
"watch": "nodemon --ext js,pug --ignore public"
},
You can read a longer explanation in “Refresh front and backend changes to browser with Express, LiveReload and Nodemon.”
You can see the setup I used at http://github.com/arvsr1988/gulp-expressjs-setup