UNPKG

symref

Version:

Static code checker for AI code agents (Windsurf, Cline, etc.)

135 lines 5.53 kB
import { SyntaxKind } from 'ts-morph'; import * as path from 'path'; import { NodeUtils } from '../utils'; /** * シンボルの定義と参照を検索するクラス */ export class SymbolFinder { /** * コンストラクタ * @param project ts-morphのプロジェクトインスタンス */ constructor(project) { this.project = project; this.nodeUtils = new NodeUtils(); } /** * シンボルの定義ノードを見つける * @param symbolName シンボル名 * @returns 定義ノード(見つからない場合はundefined) */ findDefinitionNode(symbolName) { let definitionNode; // 定義を探す for (const sourceFile of this.project.getSourceFiles()) { // .d.tsファイルはスキップ if (sourceFile.getFilePath().endsWith('.d.ts')) continue; const nodes = sourceFile.getDescendantsOfKind(SyntaxKind.Identifier) .filter(node => node.getText() === symbolName); for (const node of nodes) { const parent = node.getParent(); if (!parent) continue; // エクスポートされた変数宣言(Reactコンポーネントなど)をチェック if (parent.isKind(SyntaxKind.VariableDeclaration)) { const varStmt = parent.getParent()?.getParent(); if (varStmt && varStmt.isKind(SyntaxKind.VariableStatement)) { const modifiers = varStmt.getModifiers(); if (modifiers?.some(m => m.isKind(SyntaxKind.ExportKeyword))) { definitionNode = node; break; } } } // 通常の定義 else if (parent.isKind(SyntaxKind.ClassDeclaration) || parent.isKind(SyntaxKind.InterfaceDeclaration) || parent.isKind(SyntaxKind.FunctionDeclaration) || parent.isKind(SyntaxKind.MethodDeclaration) || parent.isKind(SyntaxKind.PropertyDeclaration) || parent.isKind(SyntaxKind.EnumDeclaration)) { definitionNode = node; break; } } if (definitionNode) break; } return definitionNode; } /** * 定義情報を抽出する * @param definitionNode 定義ノード * @returns 定義情報 */ extractDefinitionInfo(definitionNode) { const defPos = definitionNode.getSourceFile().getLineAndColumnAtPos(definitionNode.getStart()); const defFilePath = path.relative(process.cwd(), definitionNode.getSourceFile().getFilePath()); const defContext = this.nodeUtils.getNodeContext(definitionNode); return { filePath: defFilePath, line: defPos.line, column: defPos.column, context: defContext }; } /** * シンボルの参照を収集する * @param symbolName シンボル名 * @param definitionNode 定義ノード * @param includeInternalReferences 内部参照を含めるかどうか * @returns 参照情報の配列 */ collectReferences(symbolName, definitionNode, includeInternalReferences = false) { const references = []; const definitionFilePath = definitionNode.getSourceFile().getFilePath(); // すべてのソースファイルを検索 for (const sourceFile of this.project.getSourceFiles()) { const currentFilePath = sourceFile.getFilePath(); // 内部参照を含めない場合は、定義ファイルをスキップ if (!includeInternalReferences && currentFilePath === definitionFilePath) { continue; } // .d.tsファイルはスキップ if (currentFilePath.endsWith('.d.ts')) continue; // シンボル名に一致する識別子を検索 const identifiers = sourceFile.getDescendantsOfKind(SyntaxKind.Identifier) .filter(node => node.getText() === symbolName); // 各識別子が有効な参照かどうかをチェック for (const node of identifiers) { if (this.nodeUtils.isValidReference(node, definitionNode)) { const referenceInfo = this.extractReferenceInfo(node, currentFilePath); if (referenceInfo) { references.push(referenceInfo); } } } } return references; } /** * 参照情報を抽出する * @param node 参照ノード * @param currentFile 現在のファイルパス * @returns 参照情報 */ extractReferenceInfo(node, currentFile) { try { const pos = node.getSourceFile().getLineAndColumnAtPos(node.getStart()); const relativeFilePath = path.relative(process.cwd(), currentFile); const context = this.nodeUtils.getNodeContext(node); return { filePath: relativeFilePath, line: pos.line, column: pos.column, context }; } catch (error) { return null; } } } //# sourceMappingURL=SymbolFinder.js.map