UNPKG

codn_ts

Version:

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

278 lines 12.7 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.analyzeProjectReferences = analyzeProjectReferences; exports.analyzeSymbolReferences = analyzeSymbolReferences; const lsp_core_1 = require("./lsp_core"); const symbol_utils_1 = require("./symbol_utils"); const os_utils_1 = require("./os_utils"); const file_utils_1 = require("./file_utils"); const path = __importStar(require("path")); const fs = __importStar(require("fs")); const glob_1 = require("glob"); const logger = { trace: (...args) => console.error("TRACE", ...args), error: (...args) => console.error("ERROR", ...args), }; async function analyzeProjectReferences(projectRoot) { const rootUri = (0, file_utils_1.toFileUri)(projectRoot); const client = new lsp_core_1.LSPClient(rootUri, { ...lsp_core_1.DEFAULT_CONFIG, timeout: 60000, logLevel: "debug", }); const languages = (0, os_utils_1.detectDominantLanguages)(projectRoot, { ignoredDirs: new Set(["node_modules", "dist"]), topN: 1, }); const lang = languages[0]; const file_ext = (0, os_utils_1.getLanguageFileExtensions)(lang); try { logger.trace("Initializing LSP client..."); await client.start(lang); const files = await (0, glob_1.glob)(`**/*.{${file_ext}}`, { cwd: projectRoot, ignore: "node_modules/**", absolute: true, }); const allReferences = []; for (const filePath of files) { const fileUri = (0, file_utils_1.toFileUri)(filePath); const relativePath = path.relative(projectRoot, filePath); try { const content = fs.readFileSync(filePath, "utf-8"); await client.sendDidOpen(fileUri, content, "typescript"); const symbols = await client.sendDocumentSymbol(fileUri); if (!symbols) continue; for (const symbol of symbols) { if (isAnalyzableSymbol(symbol.kind)) { const str_code = await client.readFile(fileUri); const lines = str_code.split("\n"); const line = lines .slice(symbol.location.range.start.line, symbol.location.range.end.line + 1) .join("\n"); const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri); if (pos.character < 0) continue; const refs = await client.sendReferences(fileUri, pos.line, pos.character); if (refs?.result?.length) { for (const ref of refs.result) { allReferences.push(createReferenceTriple(projectRoot, ref, relativePath, symbol, pos)); } } } } await client.sendDidClose(fileUri); } catch (err) { logger.error(`Error analyzing ${relativePath}:`, err); } } return allReferences; } finally { await client.shutdown(); } } async function analyzeSymbolReferences(projectRoot, symbolName, options = {}) { const { includeExternal = false, maxDepth = 3, caseSensitive = false, } = options; const rootUri = (0, file_utils_1.toFileUri)(projectRoot); const client = new lsp_core_1.LSPClient(rootUri, { ...lsp_core_1.DEFAULT_CONFIG, timeout: 60000, logLevel: "debug", }); const languages = (0, os_utils_1.detectDominantLanguages)(projectRoot, { ignoredDirs: new Set(["node_modules", "dist"]), topN: 1, }); const lang = languages[0]; const file_ext = (0, os_utils_1.getLanguageFileExtensions)(lang); try { logger.trace("Initializing LSP client..."); await client.start(lang); const files = await (0, glob_1.glob)(`**/*.{${file_ext}}`, { cwd: projectRoot, ignore: "node_modules/**", absolute: true, }); const symbolReferences = []; const allReferences = []; // 第一步:收集所有引用关系 for (const filePath of files) { const fileUri = (0, file_utils_1.toFileUri)(filePath); const relativePath = path.relative(projectRoot, filePath); try { const content = fs.readFileSync(filePath, "utf-8"); await client.sendDidOpen(fileUri, content, "typescript"); const symbols = await client.sendDocumentSymbol(fileUri); if (!symbols) continue; for (const symbol of symbols) { if (isAnalyzableSymbol(symbol.kind)) { const str_code = await client.readFile(fileUri); const lines = str_code.split("\n"); const line = lines .slice(symbol.location.range.start.line, symbol.location.range.end.line + 1) .join("\n"); const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri); if (pos.character < 0) continue; const refs = await client.sendReferences(fileUri, pos.line, pos.character); if (refs?.result?.length) { for (const ref of refs.result) { allReferences.push(createReferenceTriple(projectRoot, ref, relativePath, symbol, pos)); } } } } await client.sendDidClose(fileUri); } catch (err) { logger.error(`Error analyzing ${relativePath}:`, err); } } // 第二步:查找匹配的符号 for (const filePath of files) { const fileUri = (0, file_utils_1.toFileUri)(filePath); const relativePath = path.relative(projectRoot, filePath); try { const content = fs.readFileSync(filePath, "utf-8"); await client.sendDidOpen(fileUri, content, "typescript"); const symbols = await client.sendDocumentSymbol(fileUri); if (!symbols) continue; for (const symbol of symbols) { if (isAnalyzableSymbol(symbol.kind)) { const symbolMatch = caseSensitive ? symbol.name === symbolName : symbol.name.toLowerCase() === symbolName.toLowerCase(); if (symbolMatch) { const str_code = await client.readFile(fileUri); const lines = str_code.split("\n"); const line = lines .slice(symbol.location.range.start.line, symbol.location.range.end.line + 1) .join("\n"); const pos = (0, symbol_utils_1.checkRealFuncChar)(line, symbol.location.range.start.line, symbol.location.range.start.character, symbol.name, symbol.kind, fileUri); if (pos.character < 0) continue; // 查找调用方(谁调用了这个符号) const callers = allReferences.filter((ref) => ref.callee === `${relativePath}:${symbol.name}`); // 查找被调用方(这个符号调用了谁) const callees = allReferences.filter((ref) => ref.caller === `${relativePath}:${symbol.name}`); // 过滤外部调用 const filteredCallers = includeExternal ? callers : callers.filter((ref) => !ref.caller.includes("node_modules")); const filteredCallees = includeExternal ? callees : callees.filter((ref) => !ref.callee.includes("node_modules")); symbolReferences.push({ symbol: symbol.name, file: relativePath, line: pos.line + 1, column: pos.character + 1, kind: getSymbolKindName(symbol.kind), callers: filteredCallers, callees: filteredCallees, definition: { file: relativePath, line: pos.line + 1, column: pos.character + 1, content: line.trim(), }, }); } } } await client.sendDidClose(fileUri); } catch (err) { logger.error(`Error analyzing ${relativePath}:`, err); } } return symbolReferences; } finally { await client.shutdown(); } } // Helper functions function isAnalyzableSymbol(kind) { const analyzableKinds = new Set([ lsp_core_1.SymbolKind.Function, lsp_core_1.SymbolKind.Method, lsp_core_1.SymbolKind.Class, lsp_core_1.SymbolKind.Interface, lsp_core_1.SymbolKind.Property, ]); return analyzableKinds.has(kind); } function createReferenceTriple(projectRoot, ref, relativePath, symbol, pos) { const absolutePath = (0, file_utils_1.fileUriToPath)(ref.uri); const callerFile = path.relative(projectRoot, absolutePath); const callerPos = `${ref.range.start.line + 1}:${ref.range.start.character + 1}`; return { caller: `${callerFile}:${callerPos}`, callee: `${relativePath}:${symbol.name}`, location: `${relativePath}:${pos.line + 1}:${pos.character + 1}`, }; } function getSymbolKindName(kind) { switch (kind) { case lsp_core_1.SymbolKind.Function: return "function"; case lsp_core_1.SymbolKind.Method: return "method"; case lsp_core_1.SymbolKind.Class: return "class"; case lsp_core_1.SymbolKind.Interface: return "interface"; case lsp_core_1.SymbolKind.Property: return "property"; case lsp_core_1.SymbolKind.Variable: return "variable"; case lsp_core_1.SymbolKind.Constant: return "constant"; case lsp_core_1.SymbolKind.Constructor: return "constructor"; case lsp_core_1.SymbolKind.Field: return "field"; default: return "unknown"; } } //# sourceMappingURL=reference_analyzer.js.map