esprima 分析 js 代码
var fs = require("fs"),
esprima = require("esprima"),
estraverse = require("estraverse"),
escodegen = require("escodegen"),
escope = require("escope");
var funcStats = {},
API = ["tag", "tag2", "mount", "update", "Tag", "observable", "Router"];
function addStats(name) {
if (!funcStats[name]) {
funcStats[name] = { calls: [], declarations: [], func: "", calling: [] };
}
}
var a = {};
function analyze(code) {
var ast = esprima.parse(code);
// var scopeMan = escope.analyze(ast);
estraverse.traverse(ast, {
enter: function(node, parent) {
if (node.type === "FunctionDeclaration") {
var name = node.id.name;
addStats(name);
funcStats[name].declarations.push(node);
funcStats[name].func = escodegen.generate(node);
recurBody(node);
function recurBody(node) {
if (node.body) {
node.body.body &&
node.body.body.forEach(function(n) {
recurBody(n);
});
if (node.type === "WhileStatement") {
node.test && recurBody(node.test);
}
} else {
switch (node.type) {
case "CallExpression":
var _name = "",
calling = funcStats[name].calling,
count = 0;
if (node.callee.type === "Identifier") {
_name = node.callee.name;
} else if (node.callee.type === "MemberExpression") {
_name = node.callee.property.name;
}
calling.forEach(function(call) {
if (call === _name) {
count++;
}
});
if (count === 0) {
calling.push(_name);
}
node.arguments.forEach(function(n) {
recurBody(n);
});
break;
case "IfStatement":
node.test && recurBody(node.test);
node.consequent && recurBody(node.consequent);
node.alternate && recurBody(node.alternate);
break;
case "LogicalExpression":
node.left && recurBody(node.left);
node.right && recurBody(node.right);
break;
case "UnaryExpression":
node.argument && recurBody(node.argument);
break;
case "AssignmentExpression":
node.right && recurBody(node.right);
break;
case "VariableDeclaration":
node.declarations.forEach(function(n) {
recurBody(n);
});
break;
case "VariableDeclarator":
node.init && recurBody(node.init);
break;
case "ExpressionStatement":
node.expression && recurBody(node.expression);
break;
case "ReturnStatement":
node.argument && recurBody(node.argument);
break;
}
}
}
} else if (
node.type === "CallExpression" &&
node.callee.type === "Identifier"
) {
addStats(node.callee.name);
funcStats[node.callee.name].calls.push(parent);
} else if (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression"
) {
addStats(node.callee.property.name);
funcStats[node.callee.property.name].calls.push(parent);
}
},
});
var data = {},
statistic = {
count: 0,
nodes: [],
links: [],
},
list = [];
Object.keys(funcStats).forEach(function(key) {
statistic.count++;
data[key] = {
calls: funcStats[key].calls.length,
declarations: funcStats[key].declarations.length,
func: funcStats[key].func,
calling: [],
weight: 0,
category: 0, //0=>normal 1=>API
};
API.forEach(function(a) {
if (a === key) {
data[key].category = 1;
}
});
funcStats[key].calling.forEach(function(c) {
funcStats[c] && data[key].calling.push(c);
});
data[key].weight = data[key].calling.length;
statistic.nodes.push({
category: data[key].category,
value: data[key].weight,
name: key,
});
});
statistic.nodes.forEach(function(node, source) {
data[node.name].calling.forEach(function(call) {
statistic.nodes.forEach(function(n, t) {
if (n.name === call) {
statistic.links.push({
source: source,
target: t + 1,
weight: 1,
});
}
});
});
});
Object.keys(funcStats).forEach(function(key) {
var d = data[key],
w = d.weight;
d.name = key;
if (list.length) {
var inserted = 0;
list.forEach(function(item, index) {
if (inserted) return;
if (item.weight < w) {
list = list.slice(0, index).concat([d], list.slice(index));
inserted++;
}
});
} else {
list.push(d);
}
});
fs.writeFileSync("ast.json", JSON.stringify(list));
}
if (process.argv.length < 3) {
throw new Error("Usage: index.js file.js");
}
var filename = process.argv[2];
console.log("[READ]\t" + filename);
var code = fs.readFileSync(filename);
analyze(code);
console.log("[DONE]");