vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
703 lines (702 loc) • 29.9 kB
JavaScript
import ParserFromPackage from 'web-tree-sitter';
import path from 'path';
import { fileURLToPath } from 'url';
import crypto from 'crypto';
import os from 'os';
import logger from '../../logger.js';
import { FileCache } from './cache/fileCache.js';
import { readFileSecure } from './fsUtils.js';
import { ensureDirectoryExists, validateDirectoryIsWritable, getCacheDirectory } from './directoryUtils.js';
import { GrammarManager } from './cache/grammarManager.js';
import { TieredCache } from './cache/tieredCache.js';
import { MemoryManager } from './cache/memoryManager.js';
import { MemoryLeakDetector } from './cache/memoryLeakDetector.js';
import { ProcessLifecycleManager } from './cache/processLifecycleManager.js';
import { ResourceTracker } from './cache/resourceTracker.js';
import { getProjectRoot, resolveProjectPath } from './utils/pathUtils.enhanced.js';
import { FileContentManager } from './cache/fileContentManager.js';
import { MetadataCache } from './cache/metadataCache.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export let grammarManager = null;
export let memoryManager = null;
export let astMemoryCache = null;
export let sourceCodeMemoryCache = null;
let parseCache = null;
let sourceCodeCache = null;
let parseTreeTieredCache = null;
let sourceCodeTieredCache = null;
let sourceCodeMetadataCache = null;
let astMetadataCache = null;
export let memoryLeakDetector = null;
export let processLifecycleManager = null;
let fileContentManager = null;
const GRAMMARS_BASE_DIR = resolveProjectPath('src/tools/code-map-generator/grammars');
logger.info(`Grammar files directory: ${GRAMMARS_BASE_DIR}`);
logger.info(`Project root directory: ${getProjectRoot()}`);
const currentWorkingDirectory = process.cwd();
logger.info(`Current working directory: ${currentWorkingDirectory}`);
logger.info(`Module directory (__dirname): ${__dirname}`);
if (currentWorkingDirectory === '/' || currentWorkingDirectory === '') {
logger.error('CRITICAL SECURITY ISSUE: Working directory has been changed to root (/). This could compromise security boundaries.');
logger.error('RECOMMENDATION: All file operations should use absolute paths from getProjectRoot() instead of process.cwd()');
logger.warn('Continuing with absolute path resolution to maintain security...');
}
export const languageConfigurations = {
'.js': { name: 'JavaScript', wasmPath: 'tree-sitter-javascript.wasm' },
'.jsx': { name: 'JavaScript JSX', wasmPath: 'tree-sitter-javascript.wasm' },
'.ts': { name: 'TypeScript', wasmPath: 'tree-sitter-typescript.wasm' },
'.tsx': { name: 'TSX', wasmPath: 'tree-sitter-tsx.wasm' },
'.html': { name: 'HTML', wasmPath: 'tree-sitter-html.wasm' },
'.css': { name: 'CSS', wasmPath: 'tree-sitter-css.wasm' },
'.vue': { name: 'Vue', wasmPath: 'tree-sitter-vue.wasm' },
'.py': { name: 'Python', wasmPath: 'tree-sitter-python.wasm' },
'.java': { name: 'Java', wasmPath: 'tree-sitter-java.wasm' },
'.cs': { name: 'C#', wasmPath: 'tree-sitter-c_sharp.wasm' },
'.go': { name: 'Go', wasmPath: 'tree-sitter-go.wasm' },
'.rb': { name: 'Ruby', wasmPath: 'tree-sitter-ruby.wasm' },
'.rs': { name: 'Rust', wasmPath: 'tree-sitter-rust.wasm' },
'.php': { name: 'PHP', wasmPath: 'tree-sitter-php.wasm' },
'.kt': { name: 'Kotlin', wasmPath: 'tree-sitter-kotlin.wasm' },
'.swift': { name: 'Swift', wasmPath: 'tree-sitter-swift.wasm' },
'.scala': { name: 'Scala', wasmPath: 'tree-sitter-scala.wasm' },
'.ex': { name: 'Elixir', wasmPath: 'tree-sitter-elixir.wasm' },
'.exs': { name: 'Elixir Script', wasmPath: 'tree-sitter-elixir.wasm' },
'.lua': { name: 'Lua', wasmPath: 'tree-sitter-lua.wasm' },
'.c': { name: 'C', wasmPath: 'tree-sitter-c.wasm' },
'.h': { name: 'C Header', wasmPath: 'tree-sitter-c.wasm' },
'.cpp': { name: 'C++', wasmPath: 'tree-sitter-cpp.wasm' },
'.hpp': { name: 'C++ Header', wasmPath: 'tree-sitter-cpp.wasm' },
'.cc': { name: 'C++', wasmPath: 'tree-sitter-cpp.wasm' },
'.m': { name: 'Objective-C', wasmPath: 'tree-sitter-objc.wasm' },
'.mm': { name: 'Objective-C++', wasmPath: 'tree-sitter-objc.wasm' },
'.zig': { name: 'Zig', wasmPath: 'tree-sitter-zig.wasm' },
'.sh': { name: 'Bash', wasmPath: 'tree-sitter-bash.wasm' },
'.bash': { name: 'Bash', wasmPath: 'tree-sitter-bash.wasm' },
'.ml': { name: 'OCaml', wasmPath: 'tree-sitter-ocaml.wasm' },
'.mli': { name: 'OCaml Interface', wasmPath: 'tree-sitter-ocaml.wasm' },
'.elm': { name: 'Elm', wasmPath: 'tree-sitter-elm.wasm' },
'.re': { name: 'ReScript', wasmPath: 'tree-sitter-rescript.wasm' },
'.res': { name: 'ReScript', wasmPath: 'tree-sitter-rescript.wasm' },
'.el': { name: 'Emacs Lisp', wasmPath: 'tree-sitter-elisp.wasm' },
'.json': { name: 'JSON', wasmPath: 'tree-sitter-json.wasm' },
'.yaml': { name: 'YAML', wasmPath: 'tree-sitter-yaml.wasm' },
'.yml': { name: 'YAML', wasmPath: 'tree-sitter-yaml.wasm' },
'.toml': { name: 'TOML', wasmPath: 'tree-sitter-toml.wasm' },
'.sol': { name: 'Solidity', wasmPath: 'tree-sitter-solidity.wasm' },
'.ql': { name: 'CodeQL', wasmPath: 'tree-sitter-ql.wasm' },
'.tla': { name: 'TLA+', wasmPath: 'tree-sitter-tlaplus.wasm' },
'.rdl': { name: 'SystemRDL', wasmPath: 'tree-sitter-systemrdl.wasm' },
'.erb': { name: 'Embedded Ruby', wasmPath: 'tree-sitter-embedded_template.wasm' },
'.ejs': { name: 'EJS', wasmPath: 'tree-sitter-embedded_template.wasm' },
};
export async function initializeParser() {
if (grammarManager && grammarManager.isInitialized()) {
logger.debug('Tree-sitter parser already initialized.');
return;
}
try {
memoryManager = new MemoryManager({
maxMemoryPercentage: 0.4,
monitorInterval: 30000,
autoManage: true
});
astMemoryCache = memoryManager.createASTCache();
sourceCodeMemoryCache = memoryManager.createSourceCodeCache();
const tempGrammarManager = new GrammarManager(languageConfigurations, {
maxGrammars: 20,
preloadCommonGrammars: true,
preloadExtensions: ['.js', '.ts', '.py', '.html', '.css'],
grammarsBaseDir: GRAMMARS_BASE_DIR
});
await tempGrammarManager.initialize();
grammarManager = tempGrammarManager;
memoryManager.registerGrammarManager(grammarManager);
if (grammarManager.isInitialized() && grammarManager.getOptions().preloadCommonGrammars) {
await grammarManager.preloadGrammars();
}
const resourceTracker = new ResourceTracker();
memoryLeakDetector = new MemoryLeakDetector({
autoDetect: true,
checkInterval: 60 * 1000,
snapshotInterval: 10 * 60 * 1000,
maxSnapshots: 5
});
await memoryLeakDetector.init();
processLifecycleManager = new ProcessLifecycleManager({
autoMonitor: true,
healthCheckInterval: 30 * 1000,
gcInterval: 5 * 60 * 1000
});
await processLifecycleManager.init(memoryManager, resourceTracker);
logger.info('Tree-sitter parser, memory management, and process lifecycle management initialized successfully.');
}
catch (error) {
logger.error({ err: error }, 'Failed to initialize Tree-sitter parser and memory management.');
throw error;
}
}
export async function initializeCaches(config) {
if (config.cache?.enabled === false) {
logger.info('File-based caching is disabled in configuration.');
return;
}
try {
const cacheDir = getCacheDirectory(config);
logger.debug(`Using cache directory: ${cacheDir}`);
await ensureDirectoryExists(cacheDir);
const parseTreeCacheDir = path.join(cacheDir, 'parse-trees');
const sourceCodeCacheDir = path.join(cacheDir, 'source-code');
await ensureDirectoryExists(parseTreeCacheDir);
await ensureDirectoryExists(sourceCodeCacheDir);
await validateDirectoryIsWritable(parseTreeCacheDir);
await validateDirectoryIsWritable(sourceCodeCacheDir);
parseCache = new FileCache({
name: 'parse-trees',
cacheDir: parseTreeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge,
serialize: ((tree) => {
return JSON.stringify({ rootNodeType: tree.rootNode?.type || 'unknown' });
}),
deserialize: ((serialized) => {
return JSON.parse(serialized);
})
});
sourceCodeCache = new FileCache({
name: 'source-code',
cacheDir: sourceCodeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge
});
if (config.cache?.useMemoryCache) {
parseTreeTieredCache = new TieredCache({
name: 'parse-trees-tiered',
cacheDir: parseTreeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge,
useMemoryCache: true,
memoryMaxEntries: config.cache?.memoryMaxEntries,
memoryMaxAge: config.cache?.memoryMaxAge,
memoryThreshold: config.cache?.memoryThreshold,
serialize: ((tree) => {
return JSON.stringify({ rootNodeType: tree.rootNode?.type || 'unknown' });
}),
deserialize: ((serialized) => {
return JSON.parse(serialized);
})
});
sourceCodeTieredCache = new TieredCache({
name: 'source-code-tiered',
cacheDir: sourceCodeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge,
useMemoryCache: true,
memoryMaxEntries: config.cache?.memoryMaxEntries,
memoryMaxAge: config.cache?.memoryMaxAge,
memoryThreshold: config.cache?.memoryThreshold
});
await parseTreeTieredCache.init();
await sourceCodeTieredCache.init();
logger.info('Tiered caches initialized successfully.');
}
await parseCache.init();
await sourceCodeCache.init();
sourceCodeMetadataCache = new MetadataCache({
name: 'source-code-metadata',
cacheDir: sourceCodeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge,
useMemoryCache: true,
memoryMaxEntries: 5000,
memoryThreshold: 0.5
});
astMetadataCache = new MetadataCache({
name: 'ast-metadata',
cacheDir: parseTreeCacheDir,
maxEntries: config.cache?.maxEntries,
maxAge: config.cache?.maxAge,
useMemoryCache: true,
memoryMaxEntries: 3000,
memoryThreshold: 0.5
});
await sourceCodeMetadataCache.init();
await astMetadataCache.init();
fileContentManager = new FileContentManager({
maxCachedFiles: config.cache?.maxCachedFiles !== undefined ? config.cache.maxCachedFiles : 0,
maxAge: 5 * 60 * 1000,
cacheDir: cacheDir,
useFileCache: config.cache?.enabled === undefined || config.cache?.enabled === true
});
await fileContentManager.init();
logger.info('File-based caches, metadata caches, and file content manager initialized successfully.');
}
catch (error) {
logger.error({ err: error }, 'Failed to initialize caches.');
parseCache = null;
sourceCodeCache = null;
fileContentManager = null;
sourceCodeMetadataCache = null;
astMetadataCache = null;
}
}
export async function clearCaches() {
try {
if (parseCache) {
await parseCache.clear();
}
if (sourceCodeCache) {
await sourceCodeCache.clear();
}
if (parseTreeTieredCache) {
await parseTreeTieredCache.clear();
}
if (sourceCodeTieredCache) {
await sourceCodeTieredCache.clear();
}
if (fileContentManager) {
fileContentManager.clearCache();
}
if (astMemoryCache) {
astMemoryCache.clear();
}
if (sourceCodeMemoryCache) {
sourceCodeMemoryCache.clear();
}
if (sourceCodeMetadataCache) {
await sourceCodeMetadataCache.clear();
}
if (astMetadataCache) {
await astMetadataCache.clear();
}
if (memoryManager) {
memoryManager.runGarbageCollection();
}
logger.info('All caches cleared successfully.');
}
catch (error) {
logger.error({ err: error }, 'Failed to clear caches.');
}
}
export async function loadLanguageGrammar(extension, langConfig) {
if (!grammarManager) {
throw new Error('Parser not initialized. Call initializeParser() first.');
}
try {
await grammarManager.loadGrammar(extension);
return true;
}
catch (error) {
logger.error({
err: error,
grammarName: langConfig.name,
extension
}, `Failed to load Tree-sitter grammar for ${langConfig.name}. Check if the grammar file exists.`);
return false;
}
}
export async function getParserForFileExtension(fileExtension) {
if (!grammarManager) {
logger.warn('Attempted to get parser before Tree-sitter initialization. Call initializeParser() first.');
await initializeParser();
if (!grammarManager)
return null;
}
const langConfig = languageConfigurations[fileExtension];
if (!langConfig) {
logger.warn(`No language configuration found for extension: ${fileExtension}. Cannot parse.`);
return null;
}
try {
return await grammarManager.getParserForExtensionWithMemoryAwareness(fileExtension);
}
catch (error) {
logger.error({ err: error, fileExtension }, `Error getting parser for extension ${fileExtension}.`);
return null;
}
}
export async function getCachedValue(key, fileCache, memoryCache) {
if (fileCache) {
try {
const value = await fileCache.get(key);
if (value !== undefined) {
logger.debug(`File cache hit for key: ${key}`);
if (memoryCache) {
memoryCache.set(key, value);
}
return value;
}
}
catch (error) {
logger.warn({ err: error, key }, `Error getting value from file cache, falling back to memory cache`);
}
}
if (memoryCache) {
const value = memoryCache.get(key);
if (value !== undefined) {
logger.debug(`Memory cache hit for key: ${key}`);
return value;
}
}
return undefined;
}
export async function setCachedValue(key, value, fileCache, memoryCache) {
if (memoryCache) {
memoryCache.set(key, value);
}
if (fileCache) {
try {
await fileCache.set(key, value);
}
catch (error) {
logger.warn({ err: error, key }, `Error setting value in file cache`);
}
}
}
export async function parseCode(sourceCode, fileExtension, filePath, config) {
let cacheKey = '';
if (filePath) {
let fileHash;
if (fileContentManager) {
try {
const metadata = await fileContentManager.getMetadata(filePath);
if (metadata) {
fileHash = metadata.hash;
cacheKey = crypto.createHash('md5').update(`${filePath}:${fileExtension}:${fileHash}`).digest('hex');
}
}
catch (error) {
logger.debug(`Error getting file metadata: ${error}`);
}
}
if (!cacheKey) {
cacheKey = crypto.createHash('md5').update(`${filePath}:${fileExtension}`).digest('hex');
}
const useFileCaching = config?.cache?.enabled !== false && parseCache !== null;
const useTieredCaching = config?.cache?.enabled !== false && config?.cache?.useMemoryCache === true && parseTreeTieredCache !== null;
const useMemoryCaching = shouldUseMemoryCache(config);
if (astMetadataCache && filePath) {
try {
const astMetadata = await astMetadataCache.get(cacheKey);
if (astMetadata) {
logger.debug(`Found AST metadata for ${filePath} in metadata cache`);
let sourceHash = '';
if (sourceCodeMetadataCache) {
const sourceMetadata = await sourceCodeMetadataCache.get(cacheKey);
if (sourceMetadata) {
sourceHash = sourceMetadata.hash;
}
}
if (!sourceHash) {
sourceHash = crypto.createHash('md5').update(sourceCode).digest('hex');
}
if (sourceHash === astMetadata.sourceHash) {
logger.debug(`Source code unchanged for ${filePath}, using cached AST metadata`);
logger.debug(`Using AST metadata to optimize parsing for ${filePath}`);
}
}
}
catch (error) {
logger.warn({ err: error, filePath }, 'Error checking AST metadata cache');
}
}
if (useTieredCaching) {
const cachedTree = await parseTreeTieredCache.get(cacheKey);
if (cachedTree) {
logger.debug(`Using tiered cached parse tree for ${filePath}`);
return cachedTree;
}
}
else if ((useMemoryCaching && astMemoryCache) || (useFileCaching && parseCache)) {
const cachedTree = await getCachedValue(cacheKey, useFileCaching ? parseCache : null, useMemoryCaching ? astMemoryCache : null);
if (cachedTree) {
logger.debug(`Using cached parse tree for ${filePath}`);
return cachedTree;
}
}
}
const parser = await getParserForFileExtension(fileExtension);
if (!parser) {
return null;
}
try {
const useIncrementalParsing = grammarManager?.getOptions().enableIncrementalParsing === true;
const incrementalThreshold = grammarManager?.getOptions().incrementalParsingThreshold || 1024 * 1024;
let tree;
if (useIncrementalParsing && sourceCode.length > incrementalThreshold) {
tree = await parseCodeIncrementally(parser, sourceCode);
logger.debug({
fileExtension,
fileSize: formatBytes(sourceCode.length),
threshold: formatBytes(incrementalThreshold)
}, `Used incremental parsing for large file with extension ${fileExtension}`);
}
else {
if (!parser || typeof parser.parse !== 'function') {
logger.error({ fileExtension }, `Parser is in invalid state for extension ${fileExtension}`);
return null;
}
if (!parser.getLanguage || !parser.getLanguage()) {
logger.error({ fileExtension }, `Parser language not set for extension ${fileExtension}`);
return null;
}
tree = parser.parse(sourceCode);
logger.debug(`Successfully parsed code for extension ${fileExtension}. Root node: ${tree.rootNode.type}`);
}
if (cacheKey) {
const useFileCaching = config?.cache?.enabled !== false && parseCache !== null;
const useTieredCaching = config?.cache?.enabled !== false && config?.cache?.useMemoryCache === true && parseTreeTieredCache !== null;
const useMemoryCaching = shouldUseMemoryCache(config);
if (astMetadataCache && filePath) {
try {
let sourceHash = '';
if (sourceCodeMetadataCache) {
const sourceMetadata = await sourceCodeMetadataCache.get(cacheKey);
if (sourceMetadata) {
sourceHash = sourceMetadata.hash;
}
}
if (!sourceHash) {
sourceHash = crypto.createHash('md5').update(sourceCode).digest('hex');
}
const astMetadata = MetadataCache.createASTMetadata(filePath, sourceHash, tree.rootNode);
await astMetadataCache.set(cacheKey, astMetadata);
logger.debug(`Cached AST metadata for ${filePath}`);
}
catch (error) {
logger.warn({ err: error, filePath }, 'Error caching AST metadata');
}
}
if (useTieredCaching) {
await parseTreeTieredCache.set(cacheKey, tree);
logger.debug(`Cached parse tree in tiered cache for ${filePath}`);
}
else if ((useMemoryCaching && astMemoryCache) || (useFileCaching && parseCache)) {
await setCachedValue(cacheKey, tree, useFileCaching ? parseCache : null, useMemoryCaching ? astMemoryCache : null);
}
}
return tree;
}
catch (error) {
const errorInfo = { err: error, fileExtension, parserState: 'null' };
if (parser) {
const language = parser.getLanguage && parser.getLanguage();
errorInfo.parserState = {
hasParseMethod: typeof parser.parse === 'function',
hasLanguage: !!language,
languageName: language ? language?.name || 'unknown' : 'unknown'
};
}
logger.error(errorInfo, `Error parsing code for extension ${fileExtension}.`);
return null;
}
}
export async function readAndParseFile(filePath, fileExtension, config) {
try {
let sourceCode;
let metadata;
if (sourceCodeMetadataCache) {
const cacheKey = crypto.createHash('md5').update(filePath).digest('hex');
metadata = await sourceCodeMetadataCache.get(cacheKey);
if (metadata) {
logger.debug(`Found metadata for ${filePath} in metadata cache`);
if (metadata.content) {
logger.debug(`Using content from metadata cache for ${filePath}`);
sourceCode = metadata.content;
}
else {
if (fileContentManager) {
sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory);
}
else {
sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory);
}
metadata.content = sourceCode;
await sourceCodeMetadataCache.set(cacheKey, metadata);
}
}
else {
if (fileContentManager) {
sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory);
}
else {
sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory);
}
metadata = await MetadataCache.createSourceCodeMetadata(filePath, sourceCode);
await sourceCodeMetadataCache.set(cacheKey, metadata);
}
}
else {
if (fileContentManager) {
sourceCode = await fileContentManager.getContent(filePath, config.allowedMappingDirectory);
}
else {
sourceCode = await readFileSecure(filePath, config.allowedMappingDirectory);
}
}
const tree = await parseCode(sourceCode, fileExtension, filePath, config);
return { tree, sourceCode };
}
catch (error) {
logger.error({ err: error, filePath }, `Error reading or parsing file: ${filePath}`);
return { tree: null, sourceCode: '' };
}
}
export function shouldUseMemoryCache(config) {
if (config?.cache?.maxCachedFiles === 0) {
logger.debug('In-memory caching is disabled via configuration (maxCachedFiles=0).');
return false;
}
if (!memoryManager) {
return true;
}
const stats = memoryManager.getMemoryStats();
if (stats.formatted.memoryStatus === 'critical') {
logger.warn('Memory usage is critical. Disabling memory caching.');
return false;
}
if (stats.formatted.memoryStatus === 'high') {
if (config?.cache?.enabled !== false && parseCache !== null && sourceCodeCache !== null) {
logger.warn('Memory usage is high. Using file-based caching only.');
return false;
}
logger.warn('Memory usage is high, but file-based caching is not available. Using memory caching with caution.');
return true;
}
return true;
}
export async function getSourceCodeFromCache(filePath, allowedDir) {
if (fileContentManager) {
try {
if (allowedDir) {
return await fileContentManager.getContent(filePath, allowedDir);
}
logger.warn({ filePath }, 'getSourceCodeFromCache called without allowedDir - security boundary cannot be validated');
return null;
}
catch (error) {
logger.debug(`Error getting source code from FileContentManager: ${error}`);
}
}
if (!sourceCodeMemoryCache) {
return null;
}
const cacheKey = crypto.createHash('md5').update(filePath).digest('hex');
const cachedSourceCode = sourceCodeMemoryCache.get(cacheKey);
if (cachedSourceCode) {
return cachedSourceCode;
}
return null;
}
export function getAllSourceCodeFromCache() {
const result = new Map();
if (fileContentManager) {
logger.debug('FileContentManager does not currently support retrieving all entries');
}
if (!sourceCodeMemoryCache) {
return result;
}
const cachedEntries = sourceCodeMemoryCache.getAll();
logger.info(`Retrieved ${cachedEntries.size} source code entries from cache`);
return cachedEntries;
}
async function parseCodeIncrementally(parser, sourceCode) {
const initialChunkSize = 100 * 1024;
const initialChunk = sourceCode.slice(0, Math.min(initialChunkSize, sourceCode.length));
let tree = parser.parse(initialChunk);
if (sourceCode.length <= initialChunkSize) {
return tree;
}
const chunkSize = 500 * 1024;
const totalChunks = Math.ceil((sourceCode.length - initialChunkSize) / chunkSize);
for (let i = 0; i < totalChunks; i++) {
tree = parser.parse(sourceCode, tree);
if (i % 5 === 0) {
const memStats = getMemoryStats();
logger.debug({
chunk: i + 1,
totalChunks,
progress: `${Math.round(((i + 1) / totalChunks) * 100)}%`,
memoryUsage: memStats.formatted.heapUsed
}, `Incremental parsing progress`);
if (global.gc && memStats.memoryUsagePercentage > 0.7) {
global.gc();
}
}
}
return tree;
}
export async function parseSourceCode(sourceCode, fileExtension) {
if (!grammarManager) {
await initializeParser();
}
const tree = await parseCode(sourceCode, fileExtension);
if (!tree) {
throw new Error(`Failed to parse source code for extension ${fileExtension}`);
}
return {
ast: tree.rootNode,
language: fileExtension.replace('.', '')
};
}
export function cleanupParser() {
if (astMemoryCache) {
astMemoryCache.clear();
}
if (sourceCodeMemoryCache) {
sourceCodeMemoryCache.clear();
}
if (fileContentManager) {
fileContentManager.clearCache();
}
if (memoryManager) {
memoryManager.runGarbageCollection();
}
if (processLifecycleManager) {
processLifecycleManager.cleanup();
processLifecycleManager = null;
}
if (memoryLeakDetector) {
memoryLeakDetector.cleanup();
memoryLeakDetector = null;
}
grammarManager = null;
memoryManager = null;
astMemoryCache = null;
sourceCodeMemoryCache = null;
parseCache = null;
sourceCodeCache = null;
parseTreeTieredCache = null;
sourceCodeTieredCache = null;
fileContentManager = null;
logger.info('Parser cleaned up and resources released.');
}
export function getMemoryManager() {
return memoryManager;
}
export function formatBytes(bytes) {
if (bytes === 0)
return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
export function getMemoryStats() {
const memUsage = process.memoryUsage();
const systemTotal = os.totalmem();
return {
heapUsed: memUsage.heapUsed,
heapTotal: memUsage.heapTotal,
rss: memUsage.rss,
systemTotal,
memoryUsagePercentage: memUsage.rss / systemTotal,
formatted: {
heapUsed: formatBytes(memUsage.heapUsed),
heapTotal: formatBytes(memUsage.heapTotal),
rss: formatBytes(memUsage.rss),
systemTotal: formatBytes(systemTotal)
}
};
}
export { ParserFromPackage as Parser };