UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

198 lines (197 loc) 8.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContextExtractor = void 0; const typescript_1 = __importDefault(require("typescript")); const astUtils_1 = require("../utils/astUtils"); /** * Extracts enhanced context information for translation keys */ class ContextExtractor { /** * Extracts code context for a call expression * @param call The call expression node * @param sourceFile The source file * @returns Code context object */ extractContextForCall(call, sourceFile) { return (0, astUtils_1.getContextCode)(call, sourceFile); } /** * Enhances a translation key with additional context information * @param key Translation key to enhance * @param sourceFile Source file containing the key * @returns Enhanced translation key with usage context */ enhanceKeyWithContext(key, sourceFile) { // Make sure we have context code const contextCode = key.contextCode || this.extractCodeLinesAround(sourceFile, key.location.line); // Extract additional usage context const usageContext = this.extractUsageContext(key, sourceFile); // Return the enhanced key return { ...key, contextCode, usageContext, }; } /** * Extracts code lines around a specific line * @param sourceFile Source file * @param lineNumber Line number (1-based) * @returns Object with before, current, and after lines */ extractCodeLinesAround(sourceFile, lineNumber) { const fileLines = sourceFile.text.split("\n"); // Transform from 0-based (TypeScript API) to 1-based (our line numbers) // Since we're already receiving a 1-based line number, we don't need to adjust it const lineIndex = lineNumber - 1; // Convert to 0-based index for array access const beforeLine = lineIndex > 0 ? fileLines[lineIndex - 1].trim() : ""; const currentLine = fileLines[lineIndex].trim(); const afterLine = lineIndex < fileLines.length - 1 ? fileLines[lineIndex + 1].trim() : ""; return { before: beforeLine, line: currentLine, after: afterLine, }; } /** * Extracts detailed usage context for a translation key * @param translationKey Translation key * @param sourceFile Source file * @returns Enhanced usage context information */ extractUsageContext(translationKey, sourceFile) { let isInJSX = false; let isInConditional = false; let isInEventHandler = false; let parentComponent; let renderCount = 0; // Find the call expression for this key const callNode = this.findCallNode(translationKey, sourceFile); if (callNode) { // Analyze the call node's context const context = this.analyzeNodeContext(callNode, sourceFile); isInJSX = context.isInJSX; isInConditional = context.isInConditional; isInEventHandler = context.isInEventHandler; parentComponent = context.parentComponent; renderCount = context.renderCount; } return { isInJSX, isInConditional, parentComponent, isInEventHandler, renderCount, }; } /** * Finds the call expression node for a translation key * @param translationKey Translation key * @param sourceFile Source file * @returns Call expression node if found */ findCallNode(translationKey, sourceFile) { const findNode = (node) => { if (typescript_1.default.isCallExpression(node)) { const { line, character } = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, node.getStart()); // Check if this is the node for our translation key // Compare both line and column position for accuracy if (line + 1 === translationKey.location.line && character + 1 === translationKey.location.column && typescript_1.default.isIdentifier(node.expression) && node.arguments.length > 0 && typescript_1.default.isStringLiteral(node.arguments[0]) && node.arguments[0].text === translationKey.key) { return node; } } for (const child of node.getChildren(sourceFile)) { const result = findNode(child); if (result) return result; } return undefined; }; return findNode(sourceFile); } /** * Analyzes the context of a node * @param node Node to analyze * @param sourceFile Source file * @returns Context analysis */ analyzeNodeContext(node, sourceFile) { let isInJSX = false; let isInConditional = false; let isInEventHandler = false; let parentComponent; let renderCount = 0; // Check if the node is inside JSX let parent = node.parent; while (parent) { // Check for JSX if (typescript_1.default.isJsxExpression(parent)) { isInJSX = true; } // Check for conditional rendering if (typescript_1.default.isConditionalExpression(parent) || typescript_1.default.isIfStatement(parent) || (typescript_1.default.isBinaryExpression(parent) && (parent.operatorToken.kind === typescript_1.default.SyntaxKind.AmpersandAmpersandToken || parent.operatorToken.kind === typescript_1.default.SyntaxKind.BarBarToken))) { isInConditional = true; } // Check for event handlers if (typescript_1.default.isJsxAttribute(parent) && parent.name.getText().startsWith("on")) { isInEventHandler = true; } // Count render function occurrences - Fixed version if (typescript_1.default.isMethodDeclaration(parent) && typescript_1.default.isIdentifier(parent.name) && parent.name.text === "render") { renderCount++; } else if (typescript_1.default.isFunctionDeclaration(parent) && parent.name && parent.name.text === "render") { renderCount++; } else if (typescript_1.default.isVariableDeclaration(parent) && typescript_1.default.isIdentifier(parent.name) && parent.name.text === "render") { renderCount++; } // Find parent component function if ((typescript_1.default.isFunctionDeclaration(parent) || typescript_1.default.isArrowFunction(parent) || typescript_1.default.isFunctionExpression(parent)) && !parentComponent) { // For function declaration, get the name if (typescript_1.default.isFunctionDeclaration(parent) && parent.name) { parentComponent = parent.name.text; } // For variable declaration with arrow function, get variable name else if (parent.parent && typescript_1.default.isVariableDeclaration(parent.parent)) { if (typescript_1.default.isIdentifier(parent.parent.name)) { parentComponent = parent.parent.name.text; } } } parent = parent.parent; } return { isInJSX, isInConditional, parentComponent, isInEventHandler, renderCount, }; } } exports.ContextExtractor = ContextExtractor;