UNPKG

@notjustcoders/ioc-arise

Version:

Arise type-safe IoC containers from your code. Zero overhead, zero coupling.

330 lines 15.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ASTParser = void 0; const fs_1 = require("fs"); const napi_1 = require("@ast-grep/napi"); const one_logger_client_sdk_1 = require("@notjustcoders/one-logger-client-sdk"); const index_js_1 = require("../errors/index.js"); class ASTParser { parseFile(filePath) { try { const content = (0, fs_1.readFileSync)(filePath, 'utf-8'); const ast = napi_1.ts.parse(content); return ast.root(); } catch (error) { throw index_js_1.ErrorFactory.fileReadError(filePath, error instanceof Error ? error.message : String(error)); } } findClassesImplementingInterfaces(root) { return root.findAll({ rule: { pattern: 'class $CLASS implements $INTERFACE { $$$ }' } }); } findClassesExtendingClasses(root) { return root.findAll({ rule: { pattern: 'class $CLASS extends $PARENT { $$$ }' } }); } findAllClasses(root) { // Find regular class declarations const regularClasses = root.findAll({ rule: { kind: 'class_declaration' } }); // Find abstract class declarations using pattern matching const abstractClasses = root.findAll({ rule: { pattern: 'abstract class $NAME { $$$ }' } }); // Also try to find exported abstract classes const exportedAbstractClasses = root.findAll({ rule: { pattern: 'export abstract class $NAME { $$$ }' } }); // Combine all found classes return [...regularClasses, ...abstractClasses, ...exportedAbstractClasses]; } extractClassName(classNode) { one_logger_client_sdk_1.logger.log('Extracting class name from node:', { result: classNode.text().substring(0, 100) + '...' }); // Try different approaches to extract class name let className = classNode.getMatch('CLASS')?.text(); if (className) { one_logger_client_sdk_1.logger.log('Found class name using CLASS match:', className); return className; } // Try to match the class declaration pattern directly const classText = classNode.text(); const classMatch = classText.match(/class\s+([A-Za-z_][A-Za-z0-9_]*)/); if (classMatch) { className = classMatch[1]; one_logger_client_sdk_1.logger.log('Found class name using regex:', className); return className; } one_logger_client_sdk_1.logger.log('Failed to extract class name'); return undefined; } extractInterfaceName(classNode) { return classNode.getMatch('INTERFACE')?.text(); } extractParentClassName(classNode) { return classNode.getMatch('PARENT')?.text(); } isAbstractClass(classNode) { const classText = classNode.text(); console.log(`DEBUG: isAbstractClass - classText: ${classText}`); const isAbstract = classText.includes('abstract class'); console.log(`DEBUG: isAbstractClass - result: ${isAbstract}`); return isAbstract; } extractConstructorParameters(classNode) { const parameters = []; try { // Find constructor within the class const constructorNodes = classNode.findAll({ rule: { kind: 'method_definition', pattern: "$NAME", regex: "^constructor" } }); one_logger_client_sdk_1.logger.log(`Found ${constructorNodes.length} constructor nodes`); if (constructorNodes.length === 0) { return parameters; } const constructorNode = constructorNodes[0]; // Find formal parameters within the constructor const parameterNodes = constructorNode.findAll({ rule: { kind: 'formal_parameters' } }); if (parameterNodes.length === 0) { return parameters; } const formalParams = parameterNodes[0]; // Find individual parameters (both required and optional) const requiredParamNodes = formalParams.findAll({ rule: { kind: 'required_parameter' } }); const optionalParamNodes = formalParams.findAll({ rule: { kind: 'optional_parameter' } }); const paramNodes = [...requiredParamNodes, ...optionalParamNodes]; one_logger_client_sdk_1.logger.log(`Found ${paramNodes.length} parameter nodes`); for (const paramNode of paramNodes) { const paramText = paramNode.text(); one_logger_client_sdk_1.logger.log('Parameter text:', paramText); // Parse parameter using regex to extract details const paramMatch = paramText.match(/(?:(private|public|protected)\s+)?(\w+)(\?)?\s*:\s*(\w+)/); if (paramMatch) { const [, accessModifier, name, optional, type] = paramMatch; parameters.push({ name: name.trim(), type: type.trim(), isOptional: !!optional, accessModifier: accessModifier }); one_logger_client_sdk_1.logger.log(`Extracted parameter: ${name}: ${type}${optional ? '?' : ''} (${accessModifier || 'none'})`); } } } catch (error) { one_logger_client_sdk_1.logger.warn('Warning: Could not extract constructor parameters:', { error }); } one_logger_client_sdk_1.logger.log('Final parameters:', { parameters }); return parameters; } extractJSDocComments(root) { const classScopes = new Map(); try { // Find all JSDoc comments in the file const comments = root.findAll({ rule: { kind: 'comment' } }); one_logger_client_sdk_1.logger.log(`Found ${comments.length} comments in file`); // Find all class declarations const classDeclarations = root.findAll({ rule: { kind: 'class_declaration' } }); one_logger_client_sdk_1.logger.log(`Found ${classDeclarations.length} class declarations in file`); for (const comment of comments) { const commentText = comment.text(); one_logger_client_sdk_1.logger.log(`Comment text: ${commentText}`); if (commentText.includes('/**') && commentText.includes('@scope')) { one_logger_client_sdk_1.logger.log(`Found @scope comment: ${commentText}`); const scopeMatch = commentText.match(/@scope\s+(singleton|transient)/); if (scopeMatch) { const scope = scopeMatch[1]; const commentRange = comment.range(); one_logger_client_sdk_1.logger.log(`Comment range: line ${commentRange.start.line} to ${commentRange.end.line}`); // Find the class declaration that comes immediately after this comment for (const classDecl of classDeclarations) { const classRange = classDecl.range(); one_logger_client_sdk_1.logger.log(`Class range: line ${classRange.start.line} to ${classRange.end.line}`); // Check if the class comes after the comment if (classRange.start.line > commentRange.end.line) { const className = this.extractClassName(classDecl); one_logger_client_sdk_1.logger.log(`Attempting to extract class name from class at line ${classRange.start.line}`); one_logger_client_sdk_1.logger.log(`Extracted class name: ${className}`); if (className) { one_logger_client_sdk_1.logger.log(`Found JSDoc scope annotation: ${className} -> ${scope}`); classScopes.set(className, scope); break; // Take the first class after this comment } else { one_logger_client_sdk_1.logger.log(`Failed to extract class name from class declaration`); } } } } } } } catch (error) { one_logger_client_sdk_1.logger.warn('Warning: Could not extract JSDoc comments:', { error }); } one_logger_client_sdk_1.logger.log(`Final classScopes map:`, { classScopes }); return classScopes; } extractScopeFromJSDoc(className, jsDocScopes) { return jsDocScopes.get(className) || 'singleton'; } extractTypeAliases(root) { const typeAliases = new Map(); try { // Find all import statements and parse them manually const allImports = root.findAll({ rule: { kind: 'import_statement' } }); one_logger_client_sdk_1.logger.log(`Found ${allImports.length} total import statements`); for (const importNode of allImports) { const importText = importNode.text(); one_logger_client_sdk_1.logger.log(`Import statement: ${importText}`); // Manual regex parsing - handles variable whitespace const aliasMatch = importText.match(/import\s*{\s*([\w]+)\s+as\s+([\w]+)\s*}\s+from/); if (aliasMatch) { const [, original, alias] = aliasMatch; typeAliases.set(alias.trim(), original.trim()); one_logger_client_sdk_1.logger.log(`Manual regex found type alias: ${alias.trim()} -> ${original.trim()}`); } } } catch (error) { one_logger_client_sdk_1.logger.warn('Warning: Could not extract type aliases:', { error }); } return typeAliases; } extractImportMappings(root) { const importMappings = new Map(); try { // Find all import statements const allImports = root.findAll({ rule: { kind: 'import_statement' } }); one_logger_client_sdk_1.logger.log(`Found ${allImports.length} import statements for mapping`); for (const importNode of allImports) { const importText = importNode.text(); one_logger_client_sdk_1.logger.log(`Processing import: ${importText}`); // Extract import path const pathMatch = importText.match(/from\s+['"]([^'"]+)['"]/); if (!pathMatch) continue; const importPath = pathMatch[1]; // Handle different import patterns // Named imports: import { A, B } from './path' const namedImportsMatch = importText.match(/import\s*{\s*([^}]+)\s*}\s*from/); if (namedImportsMatch) { const namedImports = namedImportsMatch[1].split(','); for (const namedImport of namedImports) { const trimmed = namedImport.trim(); // Handle aliases: A as B const aliasMatch = trimmed.match(/([\w]+)\s+as\s+([\w]+)/); if (aliasMatch) { const [, original, alias] = aliasMatch; importMappings.set(alias.trim(), importPath); one_logger_client_sdk_1.logger.log(`Mapped alias ${alias.trim()} -> ${importPath}`); } else { importMappings.set(trimmed, importPath); one_logger_client_sdk_1.logger.log(`Mapped named import ${trimmed} -> ${importPath}`); } } } // Default imports: import A from './path' const defaultImportMatch = importText.match(/import\s+([\w]+)\s+from/); if (defaultImportMatch && !namedImportsMatch) { const defaultImport = defaultImportMatch[1]; importMappings.set(defaultImport, importPath); one_logger_client_sdk_1.logger.log(`Mapped default import ${defaultImport} -> ${importPath}`); } } } catch (error) { one_logger_client_sdk_1.logger.warn('Warning: Could not extract import mappings:', { error }); } one_logger_client_sdk_1.logger.log(`Final import mappings:`, { importMappings }); return importMappings; } extractInterfaces(root) { const interfaces = []; try { // Find all interface declarations const interfaceNodes = root.findAll({ rule: { kind: 'interface_declaration' } }); one_logger_client_sdk_1.logger.log(`Found ${interfaceNodes.length} interface declarations`); for (const interfaceNode of interfaceNodes) { const interfaceText = interfaceNode.text(); one_logger_client_sdk_1.logger.log(`Interface text: ${interfaceText}`); // Extract interface name using regex const interfaceMatch = interfaceText.match(/interface\s+([A-Za-z_][A-Za-z0-9_]*)/); if (interfaceMatch) { const interfaceName = interfaceMatch[1]; // Check if interface has at least one method const methodNodes = interfaceNode.findAll({ rule: { kind: 'method_signature' } }); // Consider interface valid if it has at least one method if (methodNodes.length > 0) { interfaces.push(interfaceName); one_logger_client_sdk_1.logger.log(`Found valid interface: ${interfaceName} with ${methodNodes.length} methods `); } else { one_logger_client_sdk_1.logger.log(`Skipping empty interface: ${interfaceName}`); } } } } catch (error) { one_logger_client_sdk_1.logger.warn('Warning: Could not extract interfaces:', { error }); } one_logger_client_sdk_1.logger.log(`Final interfaces:`, { interfaces }); return interfaces; } } exports.ASTParser = ASTParser; //# sourceMappingURL=ast-parser.js.map