Skip to main content
Glama
runninghare

TypeScript Definition Finder

by runninghare
ts-def.ts5.74 kB
#!/usr/bin/env node import * as ts from 'typescript'; import { readFileSync, existsSync } from 'fs'; export function findDefinition(filePath: string, column: number, line_content?: string) { let line = 0; let results: Record<string, any>[] = []; try { const fileContent = readFileSync(filePath, 'utf8'); // If pattern is provided, find the line number if (line_content) { const lines = fileContent.split('\n'); for (let i = 0; i < lines.length; i++) { if (lines[i].includes(line_content)) { line = i + 1; // Convert to 1-based line number break; } } } // Create the language service host const servicesHost: ts.LanguageServiceHost = { getScriptFileNames: () => [filePath], getScriptVersion: () => '1', getScriptSnapshot: (fileName) => { if (fileName === filePath) { return ts.ScriptSnapshot.fromString(fileContent); } if (existsSync(fileName)) { return ts.ScriptSnapshot.fromString(readFileSync(fileName, 'utf8')); } return undefined; }, getCurrentDirectory: () => process.cwd(), getCompilationSettings: () => ({ target: ts.ScriptTarget.Latest, module: ts.ModuleKind.CommonJS, esModuleInterop: true, }), getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options), fileExists: ts.sys.fileExists, readFile: ts.sys.readFile, readDirectory: ts.sys.readDirectory, }; // Create the language service const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); // Get source file and find position const sourceFile = services.getProgram()?.getSourceFile(filePath); if (!sourceFile) { throw new Error('Could not get source file'); } const position = sourceFile.getPositionOfLineAndCharacter(line - 1, column - 1); // Try to get definition const definitions = services.getDefinitionAtPosition(filePath, position); if (!definitions || definitions.length === 0) { // Try to get type definition instead const typeDefinitions = services.getTypeDefinitionAtPosition(filePath, position); if (!typeDefinitions || typeDefinitions.length === 0) { // Get quick info as a fallback const quickInfo = services.getQuickInfoAtPosition(filePath, position); if (quickInfo) { console.log('Quick Info:'); console.log(`Kind: ${quickInfo.kind}`); console.log(`Documentation: ${ts.displayPartsToString(quickInfo.documentation)}`); console.log(`Type: ${ts.displayPartsToString(quickInfo.displayParts)}`); } else { console.log('No definition or information found at the specified position'); } return; } logDefinitions(typeDefinitions, 'Type Definition', services, results); return; } logDefinitions(definitions, 'Definition', services, results); return results; } catch (error) { console.error('Error finding definition:', error instanceof Error ? error.message : String(error)); } } function logDefinitions( definitions: readonly ts.DefinitionInfo[], definitionType: string, services: ts.LanguageService, resultStr: Record<string, any>[] ) { // resultStr += `\nFound ${definitions.length} ${definitionType}(s):\n`; definitions.forEach((def, index) => { // resultStr += `\n${definitionType} ${index + 1}:\n`; const result: Record<string, any> = { file: def.fileName, type: definitionType, } // resultStr += `File: ${def.fileName}\n`; // Read the file content const content = existsSync(def.fileName) ? readFileSync(def.fileName, 'utf8') : null; if (!content) { // resultStr += 'Could not read file contents\n'; return; } // Create a source file for position calculations const sourceFile = ts.createSourceFile( def.fileName, content, ts.ScriptTarget.Latest, true ); // Get start and end positions const start = sourceFile.getLineAndCharacterOfPosition(def.textSpan.start); const end = sourceFile.getLineAndCharacterOfPosition(def.textSpan.start + def.textSpan.length); result.location = `Line ${start.line + 1}, Column ${start.character + 1}`; // resultStr += `Location: Line ${start.line + 1}, Column ${start.character + 1}\n`; // Split content into lines and find the definition const lines = content.split('\n'); const startLine = start.line; let endLine = end.line; // Try to find the complete definition by looking at structure const baseIndent = getIndentation(lines[startLine]); for (let i = endLine + 1; i < lines.length; i++) { const line = lines[i]; if (line.trim() === '') continue; const indent = getIndentation(line); if (indent <= baseIndent) { break; } endLine = i; } // Add some context const contextBefore = 1; const contextAfter = 1; const displayStartLine = Math.max(0, startLine - contextBefore); const displayEndLine = Math.min(lines.length - 1, endLine + contextAfter); // resultStr += '\nDefinition:\n'; let codeSnippet = ''; for (let i = displayStartLine; i <= displayEndLine; i++) { const lineNum = (i + 1).toString().padStart(4); const marker = i === startLine ? ' >' : i > startLine && i <= endLine ? ' +' : ' '; codeSnippet += `${lineNum}${marker} ${lines[i]}\n`; } result.codeSnippet = codeSnippet; resultStr.push(result); }); } function getIndentation(line: string): number { const match = line.match(/^[\s\t]*/); return match ? match[0].length : 0; }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/runninghare/ts-def-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server