Skip to content

Instantly share code, notes, and snippets.

@marcello3d
Created March 28, 2011 02:59
Show Gist options
  • Save marcello3d/889934 to your computer and use it in GitHub Desktop.
Save marcello3d/889934 to your computer and use it in GitHub Desktop.
little node.js reloader
/*
* This file is part of the Spludo Framework.
* Copyright (c) 2009-2010 DracoBlue, http://dracoblue.net/
*
* Licensed under the terms of MIT License. For the full copyright and license
* information, please see the LICENSE file in the root folder.
*/
/*
* based on https://github.com/DracoBlue/spludo/blob/master/build/run_dev_server.js
* Rewritten by marcello@cellosoft.com
*/
var child_process = require('child_process'),
fs = require("fs"),
util = require("util"),
path = require("path"),
log = require('log4js')().getLogger('launcher')
if (process.ARGV.length < 3) {
log.error("usage: %s %s <server.js>", process.ARGV[0], process.ARGV[1])
process.exit(1)
}
var child = null,
watchedFiles = [],
restarting = false,
shuttingDown = false,
selfPath = path.resolve(__filename),
pidPath = selfPath + '.pid',
mainScript = process.ARGV[2],
mainPath = path.dirname(path.resolve(mainScript))+'/',
currentDirectory = path.resolve(process.cwd())+'/',
changedFiles = [],
addedFiles = [],
removedFiles = [],
lastFailure = 0
fs.watchFile(selfPath, {interval:500}, function(current, previous) {
if (current.mtime.valueOf() != previous.mtime.valueOf() ||
current.ctime.valueOf() != previous.ctime.valueOf()) {
log.warn("!!! " + selfPath + " has been modified (i.e. this hot swap code) Shutting down...")
shutdown()
}
})
function restart() {
if (!restarting) {
restarting = true
changedFiles.forEach(function(file) {
log.info('/// ' + file)
})
addedFiles.forEach(function(file) {
log.info('+++ ' + file)
})
removedFiles.forEach(function(file) {
log.info('--- ' + file)
})
if (child) {
stop()
} else {
start()
}
}
}
function start() {
try {
var pidData = fs.readFileSync(pidPath);
var oldPid = parseInt(pidData)
if (oldPid) {
log.info('Killing old server ('+oldPid+')')
process.kill(oldPid)
}
} catch (e) {}
restarting = false
watchFiles()
child = child_process.spawn(process.ARGV[0], ['--debug', mainScript])
log.info("<"+ mainScript + "> Started... (pid = "+child.pid+")")
fs.writeFileSync(pidPath, String(child.pid))
child.stdout.on('data', function (data) {
process.stdout.write(data)
})
child.stderr.on('data', function (data) {
util.print(data)
})
child.on('exit', function (code, signal) {
child = null
try {
fs.unlinkSync(pidPath)
} catch (e) {}
if (shuttingDown) {
process.exit(0)
}
log.warn("<" + mainScript + "> Exited with code " + code + (signal ? " ("+signal+")" : ""))
if (!restarting && (new Date - lastFailure) < 2000) {
log.warn("Two exits in a row, waiting for file change...")
return
}
lastFailure = new Date
start()
})
}
function stop() {
child && child.kill()
}
function findFiles(callback) {
child_process.exec('find '+mainPath, function(error, stdout, stderr) {
callback(null, stdout.trim().split("\n").filter(function(file) {
return !/\.(pid|sock)$/.test(file) && file != selfPath
}).map(function(file) {
return path.resolve(file)
}))
})
}
function watchFiles() {
findFiles(function(err, files) {
// Unwatch any file in watchedFiles that's not in files
watchedFiles = watchedFiles.filter(function(watchedFile) {
if (files.some(function(file) { return file == watchedFile })) {
return true
}
log.debug("unwatching "+watchedFile)
fs.unwatchFile(watchedFile)
return false
})
changedFiles = []
addedFiles = []
removedFiles = []
files.forEach(function(fullFile) {
// Don't watch a file we're already watching
if (watchedFiles.some(function(watchedFile) { return fullFile == watchedFile })) {
return
}
watchedFiles.push(fullFile)
var stats = fs.statSync(fullFile)
var isDirectory = stats.isDirectory()
var file = fullFile.replace(currentDirectory, "")
log.debug("watching "+file)
fs.watchFile(file, {interval:500}, function(current, previous) {
if (shuttingDown) return
if (current.mtime.valueOf() != previous.mtime.valueOf() ||
current.ctime.valueOf() != previous.ctime.valueOf()) {
if (isDirectory) {
findFiles(function(err,newFiles) {
if (newFiles.length != watchedFiles.length) {
var originals = {}
files.forEach(function(file) { originals[file] = true })
newFiles.forEach(function(file) {
if (originals[file]) {
delete originals[file]
} else {
addedFiles.push(file)
}
})
for (var file in originals) {
removedFiles.push(file)
}
process.nextTick(restart)
}
})
} else {
changedFiles.push(file)
process.nextTick(restart)
}
}
})
})
})
}
function shutdown() {
shuttingDown = true
stop()
watchedFiles.forEach(function(file) { fs.unwatchFile(file) })
}
process.on('exit', shutdown)
start()
@DTrejo
Copy link

DTrejo commented Mar 28, 2011

I LIKE.

I should eventually steal your features and add them to github.com/dtrejo/run.js ;)

Cheers!
-David

@marcello3d
Copy link
Author

by all means

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