fin he conseguido esto utilizando UglifyJS2 y Dot/GraphViz, en una especie de combinación de la respuesta anterior y las respuestas a la pregunta vinculada.
La parte que faltaba, para mí, era cómo filtrar el AST analizado. Resulta que UglifyJS tiene el objeto TreeWalker, que básicamente aplica una función a cada nodo del AST. Este es el código que tengo hasta ahora:
//to be run using nodejs
var UglifyJS = require('uglify-js')
var fs = require('fs');
var util = require('util');
var file = 'path/to/file...';
//read in the code
var code = fs.readFileSync(file, "utf8");
//parse it to AST
var toplevel = UglifyJS.parse(code);
//open the output DOT file
var out = fs.openSync('path/to/output/file...', 'w');
//output the start of a directed graph in DOT notation
fs.writeSync(out, 'digraph test{\n');
//use a tree walker to examine each node
var walker = new UglifyJS.TreeWalker(function(node){
//check for function calls
if (node instanceof UglifyJS.AST_Call) {
if(node.expression.name !== undefined)
{
//find where the calling function is defined
var p = walker.find_parent(UglifyJS.AST_Defun);
if(p !== undefined)
{
//filter out unneccessary stuff, eg calls to external libraries or constructors
if(node.expression.name == "$" || node.expression.name == "Number" || node.expression.name =="Date")
{
//NOTE: $ is from jquery, and causes problems if it's in the DOT file.
//It's also very frequent, so even replacing it with a safe string
//results in a very cluttered graph
}
else
{
fs.writeSync(out, p.name.name);
fs.writeSync(out, " -> ");
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
}
}
else
{
//it's a top level function
fs.writeSync(out, node.expression.name);
fs.writeSync(out, "\n");
}
}
}
if(node instanceof UglifyJS.AST_Defun)
{
//defined but not called
fs.writeSync(out, node.name.name);
fs.writeSync(out, "\n");
}
});
//analyse the AST
toplevel.walk(walker);
//finally, write out the closing bracket
fs.writeSync(out, '}');
lo ejecuto con node, y luego poner la salida a través
dot -Tpng -o graph_name.png dot_file_name.dot
Notas:
Se da una gráfica bastante básico - solo en blanco y negro y sin formato.
No atrapa ajax en absoluto, y presumiblemente no cosas como eval
o with
tampoco, como others have mentioned.
También, tal como está, incluye en el gráfico: funciones llamadas por otras funciones (y por lo tanto funciones que llaman a otras funciones), funciones que se llaman de forma independiente, Y funciones definidas pero no llamadas.
Como resultado de todo esto, puede perder cosas que son relevantes, o incluir cosas que no lo son.Sin embargo, es un comienzo, y parece lograr lo que buscaba, y lo que me llevó a esta pregunta en primer lugar.
+1 gran pregunta - – miku
¿Por qué no utilizas las herramientas de desarrollo integradas para el perfil javascript? – Tushar
Parece que el hilo original se ha ido y el enlace ahora está roto. :-( –