$> node tree.js ./app.js | dot -Tsvg > events.svg && xdg-open events.svg
$> curl http://localhost:8080/app.js
... LOTS OF JS CODE ...
$> curl http://localhost:8080/favicon.ico
Not Found
var http = require('http'); | |
var send = require('send'); | |
var urlParse = require('url').parse; | |
var count = 2; | |
var server = http.createServer(function (req, res) { | |
if (!--count) server.close(); // Only allow two connection and then exit. | |
send(req, urlParse(req.url).pathname) | |
.root(__dirname) | |
.pipe(res); | |
}); | |
server.listen(8080, function () { | |
console.error("Server listening at http://localhost:8080/"); | |
}); |
// polyfill process.addAsyncListener for older nodes. | |
if (!process.addAsyncListener) require('async-listener'); | |
process.addAsyncListener(setup, { | |
before: onBefore, | |
after: onAfter, | |
error: onError | |
}); | |
var current = null; | |
var stack = []; | |
var leaves = []; | |
var nextId = 1; | |
function guessName() { | |
var stack = (new Error()).stack.split("\n"); | |
var seen = false; | |
var line; | |
for (var i = 1; i < stack.length; i++) { | |
line = stack[i]; | |
var isLib = /node_modules\/async-listener/.test(line); | |
if (!seen) { | |
if (isLib) seen = true; | |
continue; | |
} | |
if (isLib) continue; | |
break; | |
} | |
var match = line.match(/at ([^ ]*).*\(([^)]*)\)/); | |
return { | |
name: match && match[1], | |
file: match && match[2] | |
}; | |
} | |
function setup() { | |
var info = guessName(); | |
info.parent = current; | |
info.parentIndex = current ? current.children.length - 1 : 0; | |
info.setup = Date.now(); | |
info.id = nextId++; | |
info.children = []; | |
if (current) { | |
var index = leaves.indexOf(current); | |
if (index >= 0) leaves.splice(index, 1); | |
} | |
leaves.push(info); | |
return info; | |
} | |
function onBefore(context, storage) { | |
if (current) stack.push(current); | |
storage.children.push({ before: Date.now() }); | |
current = storage; | |
} | |
function onAfter(context, storage) { | |
storage.children[storage.children.length - 1].after = Date.now(); | |
current = stack.pop(); | |
} | |
function onError(storage, error) { | |
if (!storage) return false; | |
storage.error = error; | |
return true; | |
} | |
process.on('exit', function () { | |
var inspect = require('util').inspect; | |
var nodes = leaves; | |
leaves = []; | |
var seen = []; | |
console.error(inspect(nodes, {colors:true,depth:100})); | |
console.log('digraph events {'); | |
console.log('graph [rankdir = "LR"];'); | |
nodes.forEach(function (node) { | |
while (node) { | |
if (seen.indexOf(node) >= 0) return; | |
seen.push(node); | |
var ch = node.children.map(function (child, i) { | |
return '<c' + i + '>' + (child.before - node.setup) + 'ms - ' + (child.after - child.before) + 'ms'; | |
}).join("|"); | |
console.log('n' + node.id + ' [ shape=record label="<name>' + ("" + node.name).replace('<', "(").replace(">", ")") + '|' + node.file + '|' + ch + '" ];'); | |
var parent = node.parent; | |
if (parent) { | |
console.log('n' + parent.id + ':c' + node.parentIndex + ' -> n' + node.id + ':name'); | |
} | |
node = parent; | |
} | |
}); | |
console.log('}'); | |
}); | |
require(process.argv[2]); |