Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BREAKING CHANGE: node-dev now only tracks files that were loaded via …
…require(). Therefore applications have to load the new node-dev module.
- Loading branch information
Showing
5 changed files
with
197 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#! /usr/bin/env node | ||
/* | ||
* Node.js supervisor that spawns a node child-process and restarts | ||
* it when the worker commits suicide. In order to work, your main | ||
* script must require('node-dev') before loading any other modules. | ||
* | ||
* Author: Felix Gnass [fgnass at neteye dot de] | ||
* License: MIT | ||
* See http://github.com/fgnass/node-dev | ||
*/ | ||
var sys = require('sys'), | ||
fs = require('fs'), | ||
path = require('path'), | ||
child_process = require('child_process'), | ||
util = require('util'), | ||
server = null, | ||
error = '', | ||
files, | ||
args = [].concat(process.argv), | ||
cmd = args.shift(); | ||
|
||
args.shift(); | ||
|
||
process.stdin.on('data', function (chunk) { | ||
if (server) { | ||
server.stdin.write('data: ' + chunk); | ||
} | ||
}); | ||
|
||
process.on('SIGINT', function() { | ||
server.kill('SIGHUP'); | ||
process.exit(0); | ||
}); | ||
|
||
/** | ||
* Logs a message to the console. The level is displayed in ANSI colors, | ||
* either bright red in case of an error or green otherwise. | ||
*/ | ||
function log(msg, level) { | ||
var csi = level == 'error' ? '1;31' : '32'; | ||
sys.log('[\x1B[' + csi + 'm' + level.toUpperCase() + '\x1B[0m] ' + msg); | ||
} | ||
|
||
/** | ||
* Displays a message as Growl notification. | ||
* Requires http://growl.info/extras.php#growlnotify | ||
*/ | ||
function notify(msg, title, level) { | ||
level = level || 'info'; | ||
log(title || msg, level); | ||
child_process.spawn('growlnotify', [ | ||
'-m', msg, | ||
'--image', __dirname + '/../icons/node_' + level + '.png', | ||
title || 'node.js']); | ||
} | ||
|
||
function start(title, msg) { | ||
/** Spawn a node child-process */ | ||
server = child_process.spawn(cmd, args, {customFds: [-1, 1, -1]}); | ||
notify(msg || 'Started', title); | ||
server.on('exit', function (code) { | ||
if (code == 101) { | ||
/** Worker committed suicide */ | ||
start('Restarting', 'Restarting'); | ||
} | ||
else if (files) { | ||
/** Worker exited due to an error */ | ||
files.forEach(function(file) { | ||
fs.watchFile(file, function(cur, prev) { | ||
if (+cur.mtime !== +prev.mtime) { | ||
/** Stop watching the files */ | ||
files.forEach(function(file) { | ||
fs.unwatchFile(file); | ||
}); | ||
files = null; | ||
/** Resume */ | ||
start("Resuming"); | ||
} | ||
}); | ||
}); | ||
} | ||
}); | ||
|
||
/** Scan stderr for stack-traces */ | ||
server.stderr.on('data', function(data) { | ||
var s = data.toString(), stack, src, m, file, line, col; | ||
|
||
error += s; | ||
|
||
stack = s.match(/^(.+): (.*)\n\s+at.+\((.*?):(\d+):(\d+)/m); | ||
if (stack) { | ||
|
||
// file:line | ||
// source-code | ||
// ^^^^^ | ||
// ErrorType: Message | ||
src = error.match(/^\s*(.+):(\d+)\n(.*)\n(\s*)\^/); | ||
|
||
if (src && !src[3].match(/throw/)) { | ||
file = src[1]; | ||
line = src[2]; | ||
col = src[4].length; | ||
} | ||
else { | ||
/** No source-code or error was rethrown */ | ||
file = stack[3]; | ||
line = stack[4]; | ||
col = stack[5]; | ||
} | ||
notify(stack[2] + '\n @' + file + ',' + line + ':' + col, stack[1], 'error'); | ||
|
||
/** Extract all file paths from the stack-trace */ | ||
files = []; | ||
(error.match(/\(\/.*?:\d+:\d+\)/mg) || []).forEach(function(line) { | ||
files.push(line.match(/(\/.*?):/)[1]); | ||
}); | ||
error = ''; | ||
} | ||
sys.print(data); | ||
}); | ||
} | ||
|
||
if (args.length > 0) { | ||
start(); | ||
} | ||
else { | ||
sys.print('Usage: node-dev [options] script.js [arguments]\n'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* Hooks into `require()` to watch for modifications of the required | ||
* file. If a modification is detected, the process exits with code | ||
* `101`. | ||
* If the process wasn't spawned by the `node-dev` binary the module | ||
* does nothing, hence it's safe require it even in production. | ||
*/ | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
var fs = require('fs'); | ||
|
||
/** | ||
* Watches the given module's filename. | ||
*/ | ||
function watch(module) { | ||
if (!module.loaded) { | ||
fs.watchFile(module.filename, {interval : 500}, function(cur, prev) { | ||
if (cur && +cur.mtime !== +prev.mtime) { | ||
process.exit(101); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Test if the process was started as `node-dev`. Will only work in | ||
* bash-like shells that set `$_`. | ||
*/ | ||
if (/node-dev$/.test(process.env._)) { | ||
|
||
/** | ||
* Watch this module's ancestors. | ||
*/ | ||
var m = module; | ||
while ((m = m.parent)) { | ||
watch(m); | ||
} | ||
|
||
/** | ||
* Hook into `require`. | ||
*/ | ||
var requireJs = require.extensions['.js']; | ||
require.extensions['.js'] = function(module, filename) { | ||
watch(module); | ||
requireJs(module, filename); | ||
}; | ||
} |
Oops, something went wrong.