Skip to main content
Glama
local-repo-reader.ts5.24 kB
import { readdir, readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { existsSync } from 'node:fs'; import type { GitFile } from './git-downloader.js'; export class LocalRepoReader { private verbose: boolean; private repoPath: string; constructor(verbose = false) { this.verbose = verbose; this.repoPath = join(process.cwd(), 'expo-repo'); } /** * Read MDX files from local expo repository */ async readMDXFiles(version: string): Promise<Map<string, GitFile>> { if (!existsSync(this.repoPath)) { throw new Error(`Expo repository not found at ${this.repoPath}. Please run 'git clone https://github.com/expo/expo.git expo-repo' first.`); } // Ensure we're on the correct branch await this.checkoutVersion(version); const docsPath = join(this.repoPath, 'docs', 'pages'); if (!existsSync(docsPath)) { throw new Error(`docs/pages directory not found in repository for version ${version}`); } const mdxFiles = await this.findMdxFiles(docsPath); console.log(`✅ Found ${mdxFiles.size} MDX files in local repository`); return mdxFiles; } /** * Switch to the correct git branch/version */ private async checkoutVersion(version: string): Promise<void> { const { execFile } = await import('node:child_process'); const { promisify } = await import('node:util'); const execFileAsync = promisify(execFile); try { if (this.verbose) { console.log(`🔄 Switching to version ${version}...`); } // First, fetch latest changes await execFileAsync('git', ['fetch'], { cwd: this.repoPath }); // Determine the correct branch const branch = this.getVersionBranch(version); if (this.verbose) { console.log(`📦 Checking out branch: ${branch}`); } // Checkout the branch await execFileAsync('git', ['checkout', branch], { cwd: this.repoPath }); // Pull latest changes await execFileAsync('git', ['pull'], { cwd: this.repoPath }); } catch (error) { if (version === 'latest') { console.warn('⚠️ Failed to update repository, using existing state'); } else { throw new Error(`Failed to checkout version ${version}: ${error instanceof Error ? error.message : String(error)}`); } } } /** * Get the appropriate branch for a version */ private getVersionBranch(version: string): string { if (version === 'latest') { return 'main'; } // For versioned releases, try sdk-XX format if (version.startsWith('v')) { const versionNumber = version.slice(1); return `sdk-${versionNumber}`; } return version; } /** * Recursively find all .mdx files in a directory */ private async findMdxFiles(dir: string, basePath: string = ''): Promise<Map<string, GitFile>> { const mdxFiles = new Map<string, GitFile>(); try { const entries = await readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = join(dir, entry.name); const relativePath = basePath ? join(basePath, entry.name) : entry.name; if (entry.isDirectory()) { // Skip certain directories that are not relevant if (['node_modules', '.git', '.next', 'out', 'dist'].includes(entry.name)) { continue; } const subFiles = await this.findMdxFiles(fullPath, relativePath); for (const [path, file] of subFiles) { mdxFiles.set(path, file); } } else if (entry.isFile() && entry.name.endsWith('.mdx')) { try { const content = await readFile(fullPath, 'utf-8'); mdxFiles.set(relativePath, { path: fullPath, content, relativePath, }); if (this.verbose) { console.log(`📄 Loaded: ${relativePath}`); } } catch (error) { console.warn(`⚠️ Failed to read ${fullPath}:`, error); } } } } catch (error) { console.warn(`⚠️ Failed to read directory ${dir}:`, error); } return mdxFiles; } /** * Get repository status */ async getRepoStatus(): Promise<{ branch: string; lastCommit: string; isClean: boolean }> { const { execFile } = await import('node:child_process'); const { promisify } = await import('node:util'); const execFileAsync = promisify(execFile); try { const [branchResult, commitResult, statusResult] = await Promise.all([ execFileAsync('git', ['branch', '--show-current'], { cwd: this.repoPath }), execFileAsync('git', ['log', '-1', '--pretty=format:%h %s'], { cwd: this.repoPath }), execFileAsync('git', ['status', '--porcelain'], { cwd: this.repoPath }), ]); return { branch: branchResult.stdout.trim(), lastCommit: commitResult.stdout.trim(), isClean: statusResult.stdout.trim() === '', }; } catch (error) { throw new Error(`Failed to get repository status: ${error instanceof Error ? error.message : String(error)}`); } } }

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/jaksm/expo-docs-mcp'

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