UNPKG

codn_ts

Version:

智能代码分析工具 - 支持语义搜索、调用链分析和代码结构可视化,对大模型/AI agent 友好

367 lines 12.8 kB
"use strict"; 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