Skip to main content
Glama
method-extractor.ts6.75 kB
/** * Extract methods from TypeScript class declarations */ import * as ts from 'typescript'; import { readFileSync, existsSync } from 'fs'; import type { ExtractedMethod } from './types.js'; import { createSourceFile, getJSDocDescription, extractParameterInfo, isAsyncMethod, isStaticMethod, isPrivateMember, } from './ast-parser.js'; /** * Extract all methods from a TypeScript file * * @param filePath - Path to the .d.ts file * @param libraryName - Name of the library * @param classFilter - Optional filter function for class names * @param exportAliases - Optional map of internal class names to exported aliases * @param publicExports - Optional set of publicly exported names (only index these) */ export function extractMethodsFromFile( filePath: string, libraryName: string, classFilter?: (className: string) => boolean, exportAliases?: Map<string, string>, publicExports?: Set<string> ): ExtractedMethod[] { if (!existsSync(filePath)) { return []; } const sourceCode = readFileSync(filePath, 'utf-8'); const sourceFile = createSourceFile(filePath, sourceCode); const methods: ExtractedMethod[] = []; function visit(node: ts.Node) { if (ts.isClassDeclaration(node) && node.name) { const internalClassName = node.name.text; // Use exported alias if available (e.g., ObjectCoreV1Api -> CoreV1Api) const className = exportAliases?.get(internalClassName) ?? internalClassName; // If we have public exports info, only index classes that are publicly exported // A class is public if: // 1. Its internal name has an alias in the export map (aliased export) // 2. OR its name is directly in the public exports set if (publicExports && publicExports.size > 0) { const isAliasedExport = exportAliases?.has(internalClassName); const isDirectExport = publicExports.has(internalClassName); if (!isAliasedExport && !isDirectExport) { // This class is not publicly exported, skip it return; } } // Apply class filter if provided (filter on the public name) if (classFilter && !classFilter(className)) { return; } const classMethods = extractMethodsFromClass( node, sourceFile, libraryName, filePath, className // Pass the resolved className (with alias applied) ); methods.push(...classMethods); } ts.forEachChild(node, visit); } visit(sourceFile); return methods; } /** * Extract methods from a class declaration * * @param classNode - The class declaration AST node * @param sourceFile - The source file containing the class * @param libraryName - Name of the library * @param filePath - Path to the source file * @param resolvedClassName - Optional pre-resolved class name (with export alias applied) */ export function extractMethodsFromClass( classNode: ts.ClassDeclaration, sourceFile: ts.SourceFile, libraryName: string, filePath: string, resolvedClassName?: string ): ExtractedMethod[] { if (!classNode.name) return []; // Use resolved name if provided, otherwise use the internal name const className = resolvedClassName ?? classNode.name.text; const methods: ExtractedMethod[] = []; const defaultReturnType = filePath.endsWith('.js') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs') ? 'any' : 'void'; for (const member of classNode.members) { // Handle method declarations if (ts.isMethodDeclaration(member) && member.name) { const methodName = member.name.getText(sourceFile); // Skip private methods, constructors, and internal methods if ( methodName === 'constructor' || isPrivateMember(member, sourceFile, methodName) ) { continue; } const description = getJSDocDescription(member) || `${methodName} method of ${className}`; const parameters = extractParameterInfo(member.parameters, sourceFile); const returnType = member.type?.getText(sourceFile) || defaultReturnType; const isStatic = isStaticMethod(member); const isAsync = isAsyncMethod(member, sourceFile); methods.push({ name: methodName, className, description, parameters, returnType, isStatic, isAsync, sourceFile: filePath, library: libraryName, }); } // Handle method signatures (for interfaces/abstract classes) if (ts.isMethodSignature(member) && member.name) { const methodName = member.name.getText(sourceFile); // Skip private methods if (isPrivateMember(member, sourceFile, methodName)) { continue; } const description = getJSDocDescription(member) || `${methodName} method of ${className}`; const parameters = extractParameterInfo(member.parameters, sourceFile); const returnType = member.type?.getText(sourceFile) || defaultReturnType; const isAsync = isAsyncMethod(member, sourceFile); methods.push({ name: methodName, className, description, parameters, returnType, isStatic: false, isAsync, sourceFile: filePath, library: libraryName, }); } } return methods; } /** * Extract methods from an interface declaration (for type libraries) */ export function extractMethodsFromInterface( interfaceNode: ts.InterfaceDeclaration, sourceFile: ts.SourceFile, libraryName: string, filePath: string ): ExtractedMethod[] { if (!interfaceNode.name) return []; const interfaceName = interfaceNode.name.text; const methods: ExtractedMethod[] = []; const defaultReturnType = filePath.endsWith('.js') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs') ? 'any' : 'void'; for (const member of interfaceNode.members) { if (ts.isMethodSignature(member) && member.name) { const methodName = member.name.getText(sourceFile); // Skip private methods if (methodName.startsWith('_')) { continue; } const description = getJSDocDescription(member) || `${methodName} method of ${interfaceName}`; const parameters = extractParameterInfo(member.parameters, sourceFile); const returnType = member.type?.getText(sourceFile) || defaultReturnType; const isAsync = isAsyncMethod(member, sourceFile); methods.push({ name: methodName, className: interfaceName, description, parameters, returnType, isStatic: false, isAsync, sourceFile: filePath, library: libraryName, }); } } return methods; }

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/harche/ProDisco'

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