codn_ts
Version:
智能代码分析工具 - 支持语义搜索、调用链分析和代码结构可视化,对大模型/AI agent 友好
367 lines • 12.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateCallChain = generateCallChain;
exports.displayCallChain = displayCallChain;
exports.writeCallChainToFile = writeCallChainToFile;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
async function generateCallChain(references, options = {}) {
const { from = [], to = [], maxDepth = 10, includeExternal = false, showCycles = false, minCalls = 1, filter, exclude, } = options;
// 构建调用图
const callGraph = new Map();
const edgeData = new Map();
const nodeData = new Map();
// 处理引用关系
for (const ref of references) {
// 应用过滤条件
if (filter &&
!ref.caller.includes(filter) &&
!ref.callee.includes(filter)) {
continue;
}
if (exclude &&
(ref.caller.includes(exclude) || ref.callee.includes(exclude))) {
continue;
}
// 检查是否为外部调用
const isExternalCall = ref.callee.includes("node_modules") || ref.callee.includes("@");
if (!includeExternal && isExternalCall) {
continue;
}
// 构建调用图
if (!callGraph.has(ref.caller)) {
callGraph.set(ref.caller, new Set());
}
callGraph.get(ref.caller).add(ref.callee);
// 记录边数据
const edgeKey = `${ref.caller}->${ref.callee}`;
if (!edgeData.has(edgeKey)) {
edgeData.set(edgeKey, { calls: 0, locations: [] });
}
edgeData.get(edgeKey).calls++;
edgeData.get(edgeKey).locations.push(ref.location);
// 记录节点数据
if (!nodeData.has(ref.caller)) {
const [file, lineCol] = ref.caller.split(":");
const [line, column] = lineCol.split(",").map(Number);
nodeData.set(ref.caller, {
id: ref.caller,
name: path.basename(file) + ":" + lineCol,
file,
line,
column,
calls: 0,
depth: 0,
isExternal: false,
});
}
if (!nodeData.has(ref.callee)) {
const [file, lineCol] = ref.callee.split(":");
const [line, column] = lineCol.split(",").map(Number);
nodeData.set(ref.callee, {
id: ref.callee,
name: path.basename(file) + ":" + lineCol,
file,
line,
column,
calls: 0,
depth: 0,
isExternal: isExternalCall,
});
}
}
// 计算节点调用次数
for (const [edgeKey, data] of edgeData) {
if (data.calls >= minCalls) {
const [from, to] = edgeKey.split("->");
nodeData.get(from).calls += data.calls;
}
}
// 确定起始节点
let startNodes = new Set();
if (from.length > 0) {
for (const startFunc of from) {
for (const node of nodeData.keys()) {
if (node.includes(startFunc)) {
startNodes.add(node);
}
}
}
}
else {
// 如果没有指定起始节点,使用入度为0的节点
const inDegree = new Map();
for (const node of nodeData.keys()) {
inDegree.set(node, 0);
}
for (const [edgeKey, data] of edgeData) {
if (data.calls >= minCalls) {
const [, to] = edgeKey.split("->");
inDegree.set(to, (inDegree.get(to) || 0) + 1);
}
}
for (const [node, degree] of inDegree) {
if (degree === 0) {
startNodes.add(node);
}
}
}
// 执行BFS计算深度和构建结果
const visited = new Set();
const queue = [];
const resultNodes = [];
const resultEdges = [];
const cycles = [];
// 初始化队列
for (const startNode of startNodes) {
queue.push({ node: startNode, depth: 0 });
}
// BFS遍历
while (queue.length > 0) {
const { node, depth } = queue.shift();
if (visited.has(node) || depth > maxDepth) {
continue;
}
visited.add(node);
const nodeInfo = nodeData.get(node);
nodeInfo.depth = depth;
resultNodes.push(nodeInfo);
// 检查目标节点过滤
if (to.length > 0) {
const matchesTarget = to.some((target) => node.includes(target));
if (!matchesTarget) {
continue;
}
}
// 处理出边
const neighbors = callGraph.get(node) || new Set();
for (const neighbor of neighbors) {
const edgeKey = `${node}->${neighbor}`;
const edgeInfo = edgeData.get(edgeKey);
if (edgeInfo && edgeInfo.calls >= minCalls) {
resultEdges.push({
from: node,
to: neighbor,
calls: edgeInfo.calls,
locations: edgeInfo.locations,
});
if (!visited.has(neighbor) && depth < maxDepth) {
queue.push({ node: neighbor, depth: depth + 1 });
}
}
}
}
// 检测循环调用
if (showCycles) {
cycles.push(...detectCycles(callGraph, resultNodes.map((n) => n.id)));
}
// 计算统计信息
const statistics = {
totalNodes: resultNodes.length,
totalEdges: resultEdges.length,
maxDepth: Math.max(...resultNodes.map((n) => n.depth)),
cyclesCount: cycles.length,
externalCalls: resultNodes.filter((n) => n.isExternal).length,
};
return {
nodes: resultNodes,
edges: resultEdges,
cycles,
statistics,
};
}
function detectCycles(callGraph, nodes) {
const cycles = [];
const visited = new Set();
const recursionStack = new Set();
function dfs(node, path) {
if (recursionStack.has(node)) {
const cycleStart = path.indexOf(node);
if (cycleStart !== -1) {
cycles.push([...path.slice(cycleStart), node]);
}
return;
}
if (visited.has(node)) {
return;
}
visited.add(node);
recursionStack.add(node);
path.push(node);
const neighbors = callGraph.get(node) || new Set();
for (const neighbor of neighbors) {
if (nodes.includes(neighbor)) {
dfs(neighbor, path);
}
}
recursionStack.delete(node);
path.pop();
}
for (const node of nodes) {
if (!visited.has(node)) {
dfs(node, []);
}
}
return cycles;
}
function displayCallChain(result, format = "text") {
switch (format) {
case "text":
displayTextFormat(result);
break;
case "dot":
console.log(generateDotFormat(result));
break;
case "mermaid":
console.log(generateMermaidFormat(result));
break;
case "json-graph":
console.log(JSON.stringify(result, null, 2));
break;
default:
displayTextFormat(result);
}
}
function displayTextFormat(result) {
console.log("📊 调用链分析结果");
console.log("=".repeat(50));
// 统计信息
console.log(`总节点数: ${result.statistics.totalNodes}`);
console.log(`总边数: ${result.statistics.totalEdges}`);
console.log(`最大深度: ${result.statistics.maxDepth}`);
console.log(`循环调用数: ${result.statistics.cyclesCount}`);
console.log(`外部调用数: ${result.statistics.externalCalls}`);
console.log();
// 按深度分组显示节点
const nodesByDepth = new Map();
for (const node of result.nodes) {
if (!nodesByDepth.has(node.depth)) {
nodesByDepth.set(node.depth, []);
}
nodesByDepth.get(node.depth).push(node);
}
for (let depth = 0; depth <= result.statistics.maxDepth; depth++) {
const nodes = nodesByDepth.get(depth) || [];
if (nodes.length > 0) {
console.log(`深度 ${depth}:`);
for (const node of nodes) {
const external = node.isExternal ? " [外部]" : "";
console.log(` ${node.name} (调用次数: ${node.calls})${external}`);
}
console.log();
}
}
// 显示调用关系
if (result.edges.length > 0) {
console.log("调用关系:");
for (const edge of result.edges) {
console.log(` ${edge.from} -> ${edge.to} (${edge.calls} 次调用)`);
}
console.log();
}
// 显示循环调用
if (result.cycles.length > 0) {
console.log("⚠️ 检测到循环调用:");
for (const cycle of result.cycles) {
console.log(` ${cycle.join(" -> ")}`);
}
}
}
function generateDotFormat(result) {
let dot = "digraph CallChain {\n";
dot += " rankdir=TB;\n";
dot += " node [shape=box, style=filled, fillcolor=lightblue];\n";
dot += " edge [color=gray];\n\n";
// 添加节点
for (const node of result.nodes) {
const color = node.isExternal ? "lightcoral" : "lightblue";
dot += ` "${node.id}" [label="${node.name}\\n调用:${node.calls}", fillcolor="${color}"];\n`;
}
// 添加边
for (const edge of result.edges) {
dot += ` "${edge.from}" -> "${edge.to}" [label="${edge.calls}"];\n`;
}
dot += "}\n";
return dot;
}
function generateMermaidFormat(result) {
let mermaid = "```mermaid\ngraph TD\n";
// 添加节点
for (const node of result.nodes) {
const style = node.isExternal ? ":::external" : ":::internal";
mermaid += ` ${node.id.replace(/[^a-zA-Z0-9]/g, "_")}["${node.name}<br/>调用:${node.calls}"]${style}\n`;
}
// 添加边
for (const edge of result.edges) {
const fromId = edge.from.replace(/[^a-zA-Z0-9]/g, "_");
const toId = edge.to.replace(/[^a-zA-Z0-9]/g, "_");
mermaid += ` ${fromId} -->|${edge.calls}| ${toId}\n`;
}
// 添加样式
mermaid += "\n classDef internal fill:#e1f5fe\n";
mermaid += " classDef external fill:#ffcdd2\n";
mermaid += "```\n";
return mermaid;
}
async function writeCallChainToFile(result, filePath, format) {
let content = "";
switch (format) {
case "text":
// 重定向控制台输出到字符串
const originalLog = console.log;
const logs = [];
console.log = (...args) => {
logs.push(args.join(" "));
};
displayCallChain(result, "text");
console.log = originalLog;
content = logs.join("\n");
break;
case "dot":
content = generateDotFormat(result);
break;
case "mermaid":
content = generateMermaidFormat(result);
break;
case "json-graph":
content = JSON.stringify(result, null, 2);
break;
default:
content = JSON.stringify(result, null, 2);
}
await fs.writeFile(filePath, content, "utf-8");
}
//# sourceMappingURL=callChain.js.map