sicua
Version:
A tool for analyzing project structure and dependencies
477 lines (476 loc) • 18.3 kB
JavaScript
;
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;