Skip to main content
Glama
index.ts8.1 kB
/** * Extractor module - Extract types, methods, and functions from TypeScript packages */ import { join } from 'path'; import type { ExtractionOptions, ExtractionResult, ExtractionError, ExtractedType, ExtractedMethod, ExtractedFunction, } from './types.js'; import { resolvePackage, findDtsFiles, getPackageJson, findMainEsmJs, buildEsmExportSurface, } from './package-resolver.js'; import { extractTypesFromFile } from './type-extractor.js'; import { extractMethodsFromFile } from './method-extractor.js'; import { extractFunctionsFromFile } from './function-extractor.js'; // Re-export types export * from './types.js'; // Re-export utilities export { resolvePackage, findDtsFiles } from './package-resolver.js'; export { extractTypesFromFile } from './type-extractor.js'; export { extractMethodsFromFile } from './method-extractor.js'; export { extractFunctionsFromFile } from './function-extractor.js'; export * from './ast-parser.js'; /** * Extract types, methods, and functions from an npm package */ export function extractFromPackage(options: ExtractionOptions): ExtractionResult { const { packageName, dtsFiles: providedDtsFiles, typeFilter, methodFilter, classFilter, basePath = process.cwd(), } = options; const types: ExtractedType[] = []; const methods: ExtractedMethod[] = []; const functions: ExtractedFunction[] = []; const errors: ExtractionError[] = []; // Resolve package if no files provided let dtsFiles = providedDtsFiles; let exportAliases: Map<string, string> | undefined; let publicExports: Set<string> | undefined; let jsSurface: | ReturnType<typeof buildEsmExportSurface> | undefined; if (!dtsFiles || dtsFiles.length === 0) { const packageInfo = resolvePackage(packageName, basePath); if (!packageInfo) { errors.push({ file: packageName, message: `Could not resolve package: ${packageName}`, }); return { packageName, types, methods, functions, errors }; } dtsFiles = packageInfo.allDtsFiles; exportAliases = packageInfo.exportAliases; publicExports = packageInfo.publicExports; // JS fallback: if no .d.ts files exist, attempt to index from ESM JavaScript source. if (dtsFiles.length === 0) { const packageJsonPath = join(packageInfo.packagePath, 'package.json'); const packageJson = getPackageJson(packageJsonPath); if (!packageJson) { errors.push({ file: packageName, message: `No .d.ts files found and could not read package.json for package: ${packageName}`, }); return { packageName, types, methods, functions, errors }; } const entryFile = findMainEsmJs(packageInfo.packagePath, packageJson); if (!entryFile) { errors.push({ file: packageName, message: `No .d.ts files found and no ESM JavaScript entrypoint found for package: ${packageName}`, }); return { packageName, types, methods, functions, errors }; } jsSurface = buildEsmExportSurface(entryFile); if (jsSurface.publicExports.size === 0) { errors.push({ file: packageName, message: `No .d.ts files found and no ESM exports discovered for package: ${packageName}`, }); return { packageName, types, methods, functions, errors }; } } } // Create filter functions const typeFilterFn = typeof typeFilter === 'function' ? typeFilter : typeFilter instanceof RegExp ? (name: string) => typeFilter.test(name) : undefined; const methodFilterFn = typeof methodFilter === 'function' ? methodFilter : methodFilter instanceof RegExp ? (name: string) => methodFilter.test(name) : undefined; const classFilterFn = typeof classFilter === 'function' ? classFilter : classFilter instanceof RegExp ? (name: string) => classFilter.test(name) : undefined; // === JS fallback extraction (ESM-only) === if (jsSurface) { for (const filePath of jsSurface.filesToParse) { const allowlist = jsSurface.exportAllowlistByFile.get(filePath); if (!allowlist || allowlist.size === 0) { continue; } const aliases = jsSurface.aliasMapByFile.get(filePath); try { // Extract types (classes) and filter to public surface const fileTypes = extractTypesFromFile(filePath, packageName, { allowlist, aliases, }); const filteredTypes = typeFilterFn ? fileTypes.filter((t) => typeFilterFn(t.name)) : fileTypes; types.push(...filteredTypes); // Extract methods, then filter by exported classes (file-level allowlist) and apply aliases. const rawMethods = extractMethodsFromFile(filePath, packageName); const fileMethods = rawMethods .filter((m) => allowlist.has(m.className)) .map((m) => { const internalClassName = m.className; const publicClassName = aliases?.get(internalClassName) ?? internalClassName; const description = m.description === `${m.name} method of ${internalClassName}` ? `${m.name} method of ${publicClassName}` : m.description; return { ...m, className: publicClassName, description, }; }) .filter((m) => (classFilterFn ? classFilterFn(m.className) : true)); const filteredMethods = methodFilterFn ? fileMethods.filter((m) => methodFilterFn(m.name)) : fileMethods; methods.push(...filteredMethods); // Extract functions and filter to public surface + apply aliases const fileFunctions = extractFunctionsFromFile(filePath, packageName, { allowlist, aliases, }); const filteredFunctions = methodFilterFn ? fileFunctions.filter((f) => methodFilterFn(f.name)) : fileFunctions; functions.push(...filteredFunctions); } catch (error) { errors.push({ file: filePath, message: error instanceof Error ? error.message : String(error), }); } } return { packageName, types, methods, functions, errors }; } // === TypeScript declaration (.d.ts) extraction === for (const filePath of dtsFiles!) { try { // Extract types const fileTypes = extractTypesFromFile(filePath, packageName); const filteredTypes = typeFilterFn ? fileTypes.filter((t) => typeFilterFn(t.name)) : fileTypes; types.push(...filteredTypes); // Extract methods (pass classFilter, exportAliases, and publicExports for proper filtering) const fileMethods = extractMethodsFromFile( filePath, packageName, classFilterFn, exportAliases, publicExports ); const filteredMethods = methodFilterFn ? fileMethods.filter((m) => methodFilterFn(m.name)) : fileMethods; methods.push(...filteredMethods); // Extract functions const fileFunctions = extractFunctionsFromFile(filePath, packageName); const filteredFunctions = methodFilterFn ? fileFunctions.filter((f) => methodFilterFn(f.name)) : fileFunctions; functions.push(...filteredFunctions); } catch (error) { errors.push({ file: filePath, message: error instanceof Error ? error.message : String(error), }); } } return { packageName, types, methods, functions, errors, }; } /** * Extract from specific .d.ts files */ export function extractFromFiles( files: string[], libraryName: string, options?: { typeFilter?: RegExp | ((name: string) => boolean); methodFilter?: RegExp | ((name: string) => boolean); } ): ExtractionResult { return extractFromPackage({ packageName: libraryName, dtsFiles: files, typeFilter: options?.typeFilter, methodFilter: options?.methodFilter, }); }

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