Skip to main content
Glama

CTS MCP Server

by EricA1019
tree_sitter_bridge.ts5.56 kB
/** * Tree-sitter WASM Bridge for GDScript AST Parsing * * Provides production-grade AST parsing via tree-sitter-gdscript WASM runtime. * Replaces regex-based parser with accurate, fault-tolerant AST extraction. * * Performance targets: * - WASM initialization: <500ms * - Parsing throughput: >4000 LOC/second (250ms for 1000 LOC) * * @module tree_sitter_bridge */ import Parser from 'tree-sitter'; import { readFileSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; // Tree-sitter language grammars are typically loaded dynamically let GDScript: any; /** * WASM-backed GDScript parser using tree-sitter runtime. * * Initialization is async due to WASM loading. Call init() before parseFile(). * * @example * ```typescript * const bridge = new TreeSitterBridge(); * await bridge.init(); * const tree = await bridge.parseFile('path/to/script.gd'); * ``` */ export class TreeSitterBridge { private parser: Parser | null = null; private initialized: boolean = false; private initStartTime: number = 0; /** * Initialize tree-sitter WASM runtime and load GDScript grammar. * * @throws {Error} If WASM fails to load or grammar is unavailable * @returns {Promise<void>} */ async init(): Promise<void> { if (this.initialized) { return; } this.initStartTime = Date.now(); try { // Import tree-sitter-gdscript native bindings // For Node.js environments, we need to use dynamic require for native modules // We'll use a helper function to load the module GDScript = await this.loadGDScriptLanguage(); this.parser = new Parser(); this.parser.setLanguage(GDScript); this.initialized = true; const initDuration = Date.now() - this.initStartTime; if (initDuration > 500) { console.warn(`Tree-sitter WASM initialization took ${initDuration}ms (target: <500ms)`); } } catch (error) { throw new Error( `Failed to initialize tree-sitter WASM: ${error instanceof Error ? error.message : String(error)}. ` + `Ensure tree-sitter-gdscript is installed and compiled via: cd node_modules/tree-sitter-gdscript && node-gyp rebuild` ); } } /** * Load GDScript language grammar using dynamic import or require. * This helper handles the complexity of loading native modules in ESM context. */ private async loadGDScriptLanguage(): Promise<any> { try { // Try using Node's native require for CommonJS native modules // Using eval to avoid static analysis issues const req = (typeof require !== 'undefined') ? require : eval('require'); // Fallback for environments without global require // First try direct path to compiled binding (for newly compiled versions) try { const { resolve: resolvePath } = await import('path'); const bindingPath = resolvePath( process.cwd(), 'node_modules/tree-sitter-gdscript/build/Release/tree_sitter_gdscript_binding.node' ); return req(bindingPath); } catch (directError) { // Fall back to package name (will use prebuilt bindings if available) return req('tree-sitter-gdscript'); } } catch (error) { // If require doesn't work, try dynamic import as fallback const module = await import('tree-sitter-gdscript'); return module.default || module; } } /** * Parse GDScript file into AST tree structure. * * @param {string} filePath - Absolute or relative path to .gd file * @returns {Promise<Parser.Tree>} AST tree for traversal * @throws {Error} If parser not initialized or file read fails * * @example * ```typescript * const tree = await bridge.parseFile('EventBus.gd'); * const rootNode = tree.rootNode; * console.log(rootNode.type); // 'source_file' * ``` */ async parseFile(filePath: string): Promise<Parser.Tree> { if (!this.initialized || !this.parser) { throw new Error( 'TreeSitterBridge not initialized. Call init() before parseFile().' ); } const parseStartTime = Date.now(); try { const sourceCode = readFileSync(filePath, 'utf-8'); const tree = this.parser.parse(sourceCode); const parseDuration = Date.now() - parseStartTime; const lineCount = sourceCode.split('\n').length; // Performance validation: 1000 LOC should parse in <250ms if (lineCount >= 1000 && parseDuration > 250) { console.warn( `Performance target missed: ${lineCount} LOC parsed in ${parseDuration}ms (target: <250ms for 1000 LOC)` ); } return tree; } catch (error) { throw new Error( `Failed to parse file ${filePath}: ${error instanceof Error ? error.message : String(error)}` ); } } /** * Parse GDScript source code string into AST tree. * * @param {string} sourceCode - GDScript source code * @returns {Parser.Tree} AST tree for traversal * @throws {Error} If parser not initialized */ parseString(sourceCode: string): Parser.Tree { if (!this.initialized || !this.parser) { throw new Error( 'TreeSitterBridge not initialized. Call init() before parseString().' ); } return this.parser.parse(sourceCode); } /** * Check if bridge is initialized and ready for parsing. * * @returns {boolean} True if init() completed successfully */ isInitialized(): boolean { return this.initialized; } }

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/EricA1019/CTS_MCP'

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