UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

477 lines (476 loc) 18.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FunctionNameResolver = void 0; const typescript_1 = __importDefault(require("typescript")); /** * Source of function name resolution */ var FunctionNameSource; (function (FunctionNameSource) { FunctionNameSource["DIRECT_NAME"] = "direct_name"; FunctionNameSource["VARIABLE_DECLARATION"] = "variable_declaration"; FunctionNameSource["PROPERTY_ASSIGNMENT"] = "property_assignment"; FunctionNameSource["EXPORT_ASSIGNMENT"] = "export_assignment"; FunctionNameSource["PARENT_CONTEXT"] = "parent_context"; FunctionNameSource["COMPUTED_EXPRESSION"] = "computed_expression"; FunctionNameSource["FALLBACK"] = "fallback"; })(FunctionNameSource || (FunctionNameSource = {})); /** * Utility class for comprehensive function name resolution */ class FunctionNameResolver { constructor(config = {}) { this.config = { includeAnonymousIndicator: true, useParentContext: true, resolveComputedNames: true, maxComputedNameLength: 50, fallbackPrefix: "Anonymous", ...config, }; } /** * Resolves function name (backward compatibility) * @param node The function-like node * @returns Simple function name string */ resolveName(node) { const resolved = this.resolveNameDetailed(node); return resolved.name; } /** * Resolves function name with detailed information * @param node The function-like node * @returns Detailed name resolution result */ resolveNameDetailed(node) { try { // Try direct name resolution first const directResult = this.tryDirectNameResolution(node); if (directResult.confidence > 0.8) { return directResult; } // Try variable declaration context const variableResult = this.tryVariableDeclarationResolution(node); if (variableResult.confidence > 0.8) { return variableResult; } // Try property assignment context const propertyResult = this.tryPropertyAssignmentResolution(node); if (propertyResult.confidence > 0.8) { return propertyResult; } // Try export context const exportResult = this.tryExportResolution(node); if (exportResult.confidence > 0.8) { return exportResult; } // Try parent context if enabled if (this.config.useParentContext) { const parentResult = this.tryParentContextResolution(node); if (parentResult.confidence > 0.6) { return parentResult; } } // Try computed name resolution if enabled if (this.config.resolveComputedNames) { const computedResult = this.tryComputedNameResolution(node); if (computedResult.confidence > 0.5) { return computedResult; } } // Return fallback return this.createFallbackResult(node); } catch (error) { return this.createErrorFallback(); } } /** * Tries to resolve name directly from the node */ tryDirectNameResolution(node) { // Function declaration with name if (typescript_1.default.isFunctionDeclaration(node) && node.name) { return { name: node.name.text, isAnonymous: false, isComputed: false, hasParentContext: false, source: FunctionNameSource.DIRECT_NAME, confidence: 1.0, alternativeNames: [], }; } // Method declaration with identifier name if (typescript_1.default.isMethodDeclaration(node) && typescript_1.default.isIdentifier(node.name)) { return { name: node.name.text, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.DIRECT_NAME, confidence: 1.0, alternativeNames: [], }; } // Function expression with name if (typescript_1.default.isFunctionExpression(node) && node.name) { return { name: node.name.text, isAnonymous: false, isComputed: false, hasParentContext: false, source: FunctionNameSource.DIRECT_NAME, confidence: 0.9, alternativeNames: [], }; } return this.createLowConfidenceResult("", FunctionNameSource.DIRECT_NAME); } /** * Tries to resolve name from variable declaration context */ tryVariableDeclarationResolution(node) { if (typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node)) { const parent = node.parent; if (typescript_1.default.isVariableDeclaration(parent) && typescript_1.default.isIdentifier(parent.name)) { return { name: parent.name.text, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.VARIABLE_DECLARATION, confidence: 0.9, alternativeNames: [], }; } // Handle destructuring assignment if (typescript_1.default.isVariableDeclaration(parent) && typescript_1.default.isObjectBindingPattern(parent.name)) { const destructuredName = this.resolveDestructuredName(parent.name, node); if (destructuredName) { return { name: destructuredName, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.VARIABLE_DECLARATION, confidence: 0.8, alternativeNames: [], }; } } } return this.createLowConfidenceResult("", FunctionNameSource.VARIABLE_DECLARATION); } /** * Tries to resolve name from property assignment context */ tryPropertyAssignmentResolution(node) { if (typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node)) { const parent = node.parent; // Property assignment if (typescript_1.default.isPropertyAssignment(parent)) { const propertyName = this.resolvePropertyName(parent.name); if (propertyName) { return { name: propertyName, isAnonymous: false, isComputed: typescript_1.default.isComputedPropertyName(parent.name), hasParentContext: true, source: FunctionNameSource.PROPERTY_ASSIGNMENT, confidence: 0.85, alternativeNames: [], }; } } // Binary expression (e.g., object.method = function) if (typescript_1.default.isBinaryExpression(parent) && parent.operatorToken.kind === typescript_1.default.SyntaxKind.EqualsToken) { const leftSide = parent.left; if (typescript_1.default.isPropertyAccessExpression(leftSide)) { return { name: leftSide.name.text, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.PROPERTY_ASSIGNMENT, confidence: 0.8, alternativeNames: [this.getFullPropertyPath(leftSide)], }; } } // Shorthand property assignment if (typescript_1.default.isShorthandPropertyAssignment(parent)) { return { name: parent.name.text, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.PROPERTY_ASSIGNMENT, confidence: 0.9, alternativeNames: [], }; } } return this.createLowConfidenceResult("", FunctionNameSource.PROPERTY_ASSIGNMENT); } /** * Tries to resolve name from export context */ tryExportResolution(node) { const parent = node.parent; // Export default if (typescript_1.default.isExportAssignment(parent) && parent.expression === node) { const sourceFile = node.getSourceFile(); const fileName = this.getFileBaseName(sourceFile.fileName); return { name: `Default${fileName}`, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.EXPORT_ASSIGNMENT, confidence: 0.7, alternativeNames: [fileName, "DefaultExport"], }; } // Named export with variable declaration if (typescript_1.default.isVariableDeclaration(parent) && typescript_1.default.isIdentifier(parent.name)) { const grandParent = parent.parent?.parent; if (typescript_1.default.isVariableStatement(grandParent) && grandParent.modifiers?.some((mod) => mod.kind === typescript_1.default.SyntaxKind.ExportKeyword)) { return { name: parent.name.text, isAnonymous: false, isComputed: false, hasParentContext: true, source: FunctionNameSource.EXPORT_ASSIGNMENT, confidence: 0.9, alternativeNames: [], }; } } return this.createLowConfidenceResult("", FunctionNameSource.EXPORT_ASSIGNMENT); } /** * Tries to resolve name from parent context */ tryParentContextResolution(node) { let current = node.parent; const contextNames = []; while (current && contextNames.length < 3) { // Class method context if (typescript_1.default.isClassDeclaration(current) && current.name) { contextNames.push(`${current.name.text}Method`); break; } // Namespace context if (typescript_1.default.isModuleDeclaration(current) && current.name) { contextNames.push(current.name.getText()); } // Interface method context if (typescript_1.default.isInterfaceDeclaration(current) && current.name) { contextNames.push(`${current.name.text}Method`); break; } current = current.parent; } if (contextNames.length > 0) { const contextName = contextNames.reverse().join("."); return { name: contextName, isAnonymous: true, isComputed: false, hasParentContext: true, source: FunctionNameSource.PARENT_CONTEXT, confidence: 0.6, alternativeNames: contextNames, }; } return this.createLowConfidenceResult("", FunctionNameSource.PARENT_CONTEXT); } /** * Tries to resolve computed property names */ tryComputedNameResolution(node) { const parent = node.parent; if (typescript_1.default.isPropertyAssignment(parent) && typescript_1.default.isComputedPropertyName(parent.name)) { const expression = parent.name.expression; const computedText = expression.getText(); if (computedText.length <= this.config.maxComputedNameLength) { // Try to evaluate simple computed expressions const simplifiedName = this.simplifyComputedExpression(computedText); return { name: `Computed_${simplifiedName}`, isAnonymous: false, isComputed: true, hasParentContext: true, source: FunctionNameSource.COMPUTED_EXPRESSION, confidence: 0.5, alternativeNames: [computedText, simplifiedName], }; } } return this.createLowConfidenceResult("", FunctionNameSource.COMPUTED_EXPRESSION); } /** * Resolves property name from various property name types */ resolvePropertyName(name) { if (typescript_1.default.isIdentifier(name)) { return name.text; } if (typescript_1.default.isStringLiteral(name) || typescript_1.default.isNumericLiteral(name)) { return name.text; } if (typescript_1.default.isComputedPropertyName(name) && this.config.resolveComputedNames) { const expression = name.expression; if (typescript_1.default.isStringLiteral(expression)) { return expression.text; } if (typescript_1.default.isIdentifier(expression)) { return `[${expression.text}]`; } } return null; } /** * Resolves name from destructuring pattern */ resolveDestructuredName(pattern, targetNode) { // This is a simplified implementation - in practice, you'd need to match // the specific element that corresponds to the target node for (const element of pattern.elements) { if (typescript_1.default.isBindingElement(element) && typescript_1.default.isIdentifier(element.name)) { return element.name.text; } } return null; } /** * Gets full property access path */ getFullPropertyPath(expr) { const parts = []; let current = expr; while (typescript_1.default.isPropertyAccessExpression(current)) { parts.unshift(current.name.text); current = current.expression; } if (typescript_1.default.isIdentifier(current)) { parts.unshift(current.text); } return parts.join("."); } /** * Simplifies computed expressions for naming */ simplifyComputedExpression(expression) { // Remove quotes and brackets let simplified = expression.replace(/['"[\]]/g, ""); // Replace common patterns simplified = simplified.replace(/\./g, "_"); simplified = simplified.replace(/[^a-zA-Z0-9_]/g, ""); // Truncate if too long if (simplified.length > 20) { simplified = simplified.substring(0, 20) + "..."; } return simplified || "Unknown"; } /** * Gets base file name without extension */ getFileBaseName(fileName) { const baseName = fileName.split("/").pop()?.split(".")[0]; return baseName ? this.capitalizeFirstLetter(baseName) : "File"; } /** * Capitalizes first letter of string */ capitalizeFirstLetter(str) { return str.charAt(0).toUpperCase() + str.slice(1); } /** * Creates a low confidence result */ createLowConfidenceResult(name, source) { return { name: name || `${this.config.fallbackPrefix}Function`, isAnonymous: true, isComputed: false, hasParentContext: false, source, confidence: 0.1, alternativeNames: [], }; } /** * Creates fallback result */ createFallbackResult(node) { const nodeKind = typescript_1.default.SyntaxKind[node.kind]; const fallbackName = this.config.includeAnonymousIndicator ? `${this.config.fallbackPrefix}${nodeKind}` : this.config.fallbackPrefix; return { name: fallbackName, isAnonymous: true, isComputed: false, hasParentContext: false, source: FunctionNameSource.FALLBACK, confidence: 0.0, alternativeNames: [nodeKind, "Anonymous Function"], }; } /** * Creates error fallback result */ createErrorFallback() { return { name: "ErrorFunction", isAnonymous: true, isComputed: false, hasParentContext: false, source: FunctionNameSource.FALLBACK, confidence: 0.0, alternativeNames: ["Error", "Unknown"], }; } /** * Checks if a name resolution is reliable */ isReliableName(resolved) { return resolved.confidence > 0.7 && !resolved.isAnonymous; } /** * Gets the best alternative name */ getBestAlternativeName(resolved) { if (resolved.alternativeNames.length > 0) { return resolved.alternativeNames[0]; } return resolved.name; } /** * Normalizes function name for consistency */ normalizeName(name) { // Remove invalid characters let normalized = name.replace(/[^a-zA-Z0-9_$]/g, "_"); // Ensure doesn't start with number if (/^[0-9]/.test(normalized)) { normalized = "_" + normalized; } // Handle empty or too short names if (normalized.length < 2) { normalized = "fn_" + normalized; } return normalized; } } exports.FunctionNameResolver = FunctionNameResolver;