Skip to content

Instantly share code, notes, and snippets.

@webdesserts
Last active April 3, 2023 08:16
Show Gist options
  • Save webdesserts/5632955 to your computer and use it in GitHub Desktop.
Save webdesserts/5632955 to your computer and use it in GitHub Desktop.
Automatically reload your node.js app on file change with Gulp (https://github.com/wearefractal/gulp).
// NOTE: I previously suggested doing this through Grunt, but had plenty of problems with
// my set up. Grunt did some weird things with scope, and I ended up using nodemon. This
// setup is now using Gulp. It works exactly how I expect it to and is WAY more concise.
var gulp = require('gulp'),
spawn = require('child_process').spawn,
node;
/**
* $ gulp server
* description: launch the server. If there's a server already running, kill it.
*/
gulp.task('server', function() {
if (node) node.kill()
node = spawn('node', ['index.js'], {stdio: 'inherit'})
node.on('close', function (code) {
if (code === 8) {
gulp.log('Error detected, waiting for changes...');
}
});
})
/**
* $ gulp
* description: start the development environment
*/
gulp.task('default', function() {
gulp.run('server')
gulp.watch(['./index.js', './lib/**/*.js'], function() {
gulp.run('server')
})
// Need to watch for sass changes too? Just add another watch call!
// no more messing around with grunt-concurrent or the like. Gulp is
// async by default.
})
// clean up if an error goes unhandled.
process.on('exit', function() {
if (node) node.kill()
})
@ncodes
Copy link

ncodes commented Dec 30, 2016

Awesome! Thanks

@behrank
Copy link

behrank commented Jan 31, 2017

nice work!

@crowmagnumb
Copy link

crowmagnumb commented Feb 10, 2017

Yes, thank you! I just made server() a function so that you don't have to rely on gulp.run or any alternative...

const gulp = require('gulp');
const spawn = require('child_process').spawn;
let node;

function server() {
    if (node) {
        node.kill();
    }

    node = spawn('node', ['src/index.js'], {stdio: 'inherit'});
    node.on('close', function (code) {
        if (code === 8) {
            gulp.log('Error detected, waiting for changes...');
        }
    });
}

gulp.task('server', function() {
    server();
});

gulp.task('watch', ['server'], function() {
    gulp.watch(['./src/**/*.js'], function() {
        server();
    });
});

gulp.task('default', ['watch']);

// clean up if an error goes unhandled.
process.on('exit', function() {
    if (node) {
        node.kill();
    }
});

@safizn
Copy link

safizn commented Mar 1, 2017

For using BrowserSync (Browser reload) with server reload.

import childProcess from 'child_process'
var browserSync = require('browser-sync').create();
let node;
const INTERVAL = 10000;
const usePolling = true;

function serverLivereload() {
    if(node) node.kill()

    node = childProcess.fork('app.entrypoint.js', { cwd: '/app', stdio:'inherit' })
    node.on('message', (m) => {
        console.log('Server ready & listening.', m);
        browserSync.reload()
    });
    node.on('close', (code) => {
        if(code === 8) {
            gulp.log('Error detected, waiting for changes.')
        }
    })
}

gulp.task('watch:livereload', ()=> {
    browserSync.init({
        proxy: {
            target: 'localhost'
        },
        logLevel: 'debug',
        logConnections: true,
        ui: {
            port: 9901,
            weinre: {
                port: 9902
            }
        },
        port: 9903,
        open: false // open browser false.
    });
    serverLivereload()

	gulp.watch(
		[ 
            '/app/**/*.js', 
            '/app/**/*.css', 
            '/app/**/*.html', 
            '!/app/**/node_modules{,/**/*}' // equals to '!/app/{node_modules,node_modules/**/*}'
        ], 
	{ interval: INTERVAL, usePolling: usePolling },  // Fixed Windows issue, requiring legacy watch 'polling'
	async (done) => {
            serverLivereload()
            done()
        }        
	);
});

Then on forked child process: /app/app.entrypoint.js

import http from 'http'
import Koa from 'koa'
const serverKoa = module.exports = new Koa()
serverKoa.use(<middlewares>)
...
    http.createServer(serverKoa.callback()).listen(APP.port, ()=> {
        console.log(`listening on port ${APP.port}`)
        process.send({ message: 'Server ready & listening'});
    })

Run with gulp watch:livereload

@tiagosiebler
Copy link

This makes everything SO much easier, awesome & thank you for sharing!

@tiagosiebler
Copy link

tiagosiebler commented Nov 16, 2017

By the way, if this launches too quickly and gets stuck. Instead of node.on('close') use node.on('sigterm') to handle the relaunch properly:

gulp.task('server', function() {
	runLiveServer();
	node.on('SIGTERM', function(code) {
		if (code === 8 || code === 12) {
			console.log('Error detected, attempting reboot...');
			setTimeout(runLiveServer, 500);
		} else {
			console.log('Relaunced with code: ', code);
		}
	});
})

@felixcatto
Copy link

Awesome! Big thanks

@0xjorgev
Copy link

Awesome! this saves tons of time!

@longtc
Copy link

longtc commented Jun 2, 2018

Never mind, I got it to work.
Make sure the task signal [Async Completion](https://github.com/gulpjs/gulp/blob/9f4a2e96506dec1d85804de8884678e72ffc5aa0/docs/getting-started/4-async-completion.md). Or, just define your task as an [`async` function](https://github.com/gulpjs/gulp/blob/9f4a2e96506dec1d85804de8884678e72ffc5aa0/docs/getting-started/4-async-completion.md#using-asyncawait):

```javascript
async function startServer() {
  if (node) node.kill();
  node = await spawn("node", ["./src/server.js"], { stdio: "inherit" });

  node.on("close", function (code) {
    if(code === 8) {
      console.log("Error detected, waiting for changes...");
    }
  });
}

gulp.task("default", function () {

  // Start the server, if a change is detected restart it
  gulp.watch(
    ["src/**/*", "src/server.js"],
    {
      queue: false,
      ignoreInitial: false // Execute task on startup 
    },
    startServer);
});
```

@B3none
Copy link

B3none commented Aug 4, 2018

👍

@lgcavalheiro
Copy link

Hey man thanks for sharing your gulpfile, you got me out of one hell of a pickle haha!
Here's how mine endend up looking, works great now :D

const gulp = require('gulp');
const watch = gulp.watch;
const series = gulp.series;
const { exec, spawn } = require('child_process');
var serverProc = undefined;

const watcher = watch(['./frontend/**/*', './backend/**/*', '!./backend/**/*.json']);

watcher.on('change', function (path, stats) {
    console.log(`File ${path} was changed - Relaunching...`);
    serverProc.kill('SIGINT');
    exports.default();
});

watcher.on('error', function (e) {
    console.error(e.stack);
});

function purge() {
    return exec('shx rm -rf ./public/*');
}

function bundle() {
    return exec('parcel build ./frontend/html/*.html --out-dir public --no-source-maps');
}

function serve() {
    if (serverProc) serverProc.kill('SIGINT');
    serverProc = spawn('node', ['backend/server.js'], { stdio: 'inherit' });
    serverProc.on('close', function (code) {
        if (code === 8) {
            gulp.log('Error detected, waiting for changes...');
        }
    });
}

exports.default = series(purge, bundle, serve);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment