/
node-dev
executable file
·128 lines (115 loc) · 3.25 KB
/
node-dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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');
}