UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

372 lines (371 loc) 16.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JSXReturnAnalyzer = void 0; const t = __importStar(require("@babel/types")); const traverse_1 = __importDefault(require("@babel/traverse")); const ConditionalParser_1 = require("../parsers/ConditionalParser"); const utils_1 = require("../utils"); const reactSpecific_1 = require("../../../utils/ast/reactSpecific"); /** * Analyzer for JSX return statements and their conditional rendering patterns */ class JSXReturnAnalyzer { constructor(config) { this.config = config || { maxDepth: 10, includeExternalComponents: true, excludePatterns: [], onlyAnalyzeRoutes: [], includeHtmlElements: false, htmlElementFilter: { includeAll: false, includeTags: [], excludeTags: [], captureTextContent: false, maxTextLength: 100, }, }; this.conditionalParser = new ConditionalParser_1.ConditionalParser(this.config); } /** * Analyzes all return statements in an AST for JSX content and conditional patterns */ analyzeAST(ast, sourceCode) { const returnStatements = []; (0, traverse_1.default)(ast, { // Look for function components (function declarations and arrow functions) FunctionDeclaration: (path) => { // Only analyze React components (functions that return JSX) if ((0, reactSpecific_1.isReactComponentBabel)(path.node)) { const functionReturns = this.analyzeFunctionNode(path.node, sourceCode); returnStatements.push(...functionReturns); } }, // Arrow function expressions assigned to variables (const Component = () => {}) VariableDeclarator: (path) => { if (t.isArrowFunctionExpression(path.node.init)) { if ((0, reactSpecific_1.isReactComponentBabel)(path.node.init)) { const functionReturns = this.analyzeFunctionNode(path.node.init, sourceCode); returnStatements.push(...functionReturns); } } }, // Export default arrow functions ExportDefaultDeclaration: (path) => { if (t.isArrowFunctionExpression(path.node.declaration)) { const functionReturns = this.analyzeFunctionNode(path.node.declaration, sourceCode); returnStatements.push(...functionReturns); } else if (t.isFunctionExpression(path.node.declaration)) { const functionReturns = this.analyzeFunctionNode(path.node.declaration, sourceCode); returnStatements.push(...functionReturns); } }, }); return returnStatements; } /** * NEW: Analyzes a specific component node for JSX content and conditional patterns */ analyzeComponentNode(componentNode, sourceCode) { const returnStatements = []; // Check if it's a function-like node if (t.isFunctionDeclaration(componentNode) || t.isArrowFunctionExpression(componentNode) || t.isFunctionExpression(componentNode)) { const functionReturns = this.analyzeFunctionNode(componentNode, sourceCode); returnStatements.push(...functionReturns); } else { // If it's not a function node, traverse it to find function declarations within (0, traverse_1.default)(componentNode, { FunctionDeclaration: (path) => { if ((0, reactSpecific_1.isReactComponentBabel)(path.node)) { const functionReturns = this.analyzeFunctionNode(path.node, sourceCode); returnStatements.push(...functionReturns); } }, ArrowFunctionExpression: (path) => { if ((0, reactSpecific_1.isReactComponentBabel)(path.node)) { const functionReturns = this.analyzeFunctionNode(path.node, sourceCode); returnStatements.push(...functionReturns); } }, FunctionExpression: (path) => { if ((0, reactSpecific_1.isReactComponentBabel)(path.node)) { const functionReturns = this.analyzeFunctionNode(path.node, sourceCode); returnStatements.push(...functionReturns); } }, }, undefined, {}); } return returnStatements; } /** * Recursively checks if a statement contains a JSX return */ statementContainsJSXReturn(statement) { if (t.isReturnStatement(statement) && statement.argument) { return (0, utils_1.nodeContainsJSX)(statement.argument); } else if (t.isIfStatement(statement)) { return (this.statementContainsJSXReturn(statement.consequent) || (statement.alternate ? this.statementContainsJSXReturn(statement.alternate) : false)); } else if (t.isBlockStatement(statement)) { return statement.body.some((stmt) => this.statementContainsJSXReturn(stmt)); } return false; } /** * Analyzes a single function for JSX return patterns */ analyzeFunctionNode(functionNode, sourceCode) { const returnStatements = []; if (t.isArrowFunctionExpression(functionNode) && !t.isBlockStatement(functionNode.body)) { // Handle implicit return const implicitReturn = this.analyzeImplicitReturn(functionNode.body, sourceCode); if (implicitReturn) { returnStatements.push(implicitReturn); } } else if (functionNode.body && t.isBlockStatement(functionNode.body)) { // Analyze all statements in the function body returnStatements.push(...this.analyzeBlockStatement(functionNode.body, sourceCode)); } return returnStatements; } /** * Analyzes a block statement for JSX returns and conditional patterns */ analyzeBlockStatement(block, sourceCode) { const returnStatements = []; for (const statement of block.body) { if (t.isReturnStatement(statement)) { const analyzed = this.analyzeReturnStatement(statement, sourceCode); if (analyzed) { returnStatements.push(analyzed); } } else if (t.isIfStatement(statement)) { // Handle if/else statements const conditionalReturns = this.analyzeConditionalReturns(statement, sourceCode); returnStatements.push(...conditionalReturns); } else if (t.isSwitchStatement(statement)) { // Handle switch statements const switchReturns = this.analyzeSwitchStatement(statement, sourceCode); returnStatements.push(...switchReturns); } else if (t.isBlockStatement(statement)) { // Handle nested blocks returnStatements.push(...this.analyzeBlockStatement(statement, sourceCode)); } } return returnStatements; } /** * Analyzes switch statements for conditional returns */ analyzeSwitchStatement(switchStatement, sourceCode) { const returnStatements = []; for (const caseClause of switchStatement.cases) { for (const statement of caseClause.consequent) { if (t.isReturnStatement(statement)) { const analyzed = this.analyzeReturnStatement(statement, sourceCode); if (analyzed) { returnStatements.push(analyzed); } } else if (t.isBlockStatement(statement)) { returnStatements.push(...this.analyzeBlockStatement(statement, sourceCode)); } } } return returnStatements; } /** * Analyzes a return statement for JSX and conditional patterns - ENHANCED */ analyzeReturnStatement(returnStatement, sourceCode) { if (!returnStatement.argument || !(0, reactSpecific_1.containsJSX)(returnStatement)) { return null; } // Analyze conditional patterns (already enhanced in ConditionalParser) const conditionalPatterns = this.conditionalParser.analyzeExpression(returnStatement.argument, sourceCode); // Extract component references (components only) const componentReferences = (0, utils_1.extractComponentReferencesFromNode)(returnStatement.argument, sourceCode); // NEW: Extract HTML element references if enabled let htmlElementReferences = []; if (this.config.includeHtmlElements) { const filter = this.config.htmlElementFilter; htmlElementReferences = (0, utils_1.extractHTMLElementReferencesFromNode)(returnStatement.argument, sourceCode, filter.includeTags, filter.excludeTags, filter.includeAll); } return { hasConditional: conditionalPatterns.length > 0, conditionalPatterns, componentReferences, htmlElementReferences, // NEW: Include HTML elements position: (0, utils_1.getCodePosition)(returnStatement), }; } /** * Analyzes implicit returns in arrow functions - ENHANCED */ analyzeImplicitReturn(expression, sourceCode) { if (!(0, utils_1.nodeContainsJSX)(expression)) { return null; } // Analyze conditional patterns (already enhanced in ConditionalParser) const conditionalPatterns = this.conditionalParser.analyzeExpression(expression, sourceCode); // Extract component references (components only) const componentReferences = (0, utils_1.extractComponentReferencesFromNode)(expression, sourceCode); // NEW: Extract HTML element references if enabled let htmlElementReferences = []; if (this.config.includeHtmlElements) { const filter = this.config.htmlElementFilter; htmlElementReferences = (0, utils_1.extractHTMLElementReferencesFromNode)(expression, sourceCode, filter.includeTags, filter.excludeTags, filter.includeAll); } return { hasConditional: conditionalPatterns.length > 0, conditionalPatterns, componentReferences, htmlElementReferences, // NEW: Include HTML elements position: (0, utils_1.getCodePosition)(expression), }; } /** * Analyzes conditional return statements within if/else blocks */ analyzeConditionalReturns(ifStatement, sourceCode) { const returnStatements = []; // Analyze consequent (if block) const consequentReturns = this.extractReturnsFromStatement(ifStatement.consequent, sourceCode); returnStatements.push(...consequentReturns); // Analyze alternate (else block) if (ifStatement.alternate) { const alternateReturns = this.extractReturnsFromStatement(ifStatement.alternate, sourceCode); returnStatements.push(...alternateReturns); } return returnStatements; } /** * Extracts return statements from various statement types */ extractReturnsFromStatement(statement, sourceCode) { const returnStatements = []; if (t.isReturnStatement(statement)) { const analyzed = this.analyzeReturnStatement(statement, sourceCode); if (analyzed) { returnStatements.push(analyzed); } } else if (t.isBlockStatement(statement)) { returnStatements.push(...this.analyzeBlockStatement(statement, sourceCode)); } else if (t.isIfStatement(statement)) { returnStatements.push(...this.analyzeConditionalReturns(statement, sourceCode)); } else if (t.isSwitchStatement(statement)) { returnStatements.push(...this.analyzeSwitchStatement(statement, sourceCode)); } return returnStatements; } /** * NEW: Updates configuration for HTML element tracking */ updateConfig(config) { this.config = config; this.conditionalParser = new ConditionalParser_1.ConditionalParser(this.config); } /** * NEW: Gets current configuration */ getConfig() { return { ...this.config }; } /** * NEW: Enables HTML element tracking */ enableHtmlElementTracking() { this.config.includeHtmlElements = true; this.conditionalParser = new ConditionalParser_1.ConditionalParser(this.config); } /** * NEW: Disables HTML element tracking */ disableHtmlElementTracking() { this.config.includeHtmlElements = false; this.conditionalParser = new ConditionalParser_1.ConditionalParser(this.config); } /** * NEW: Analyzes a specific JSX expression for both components and HTML elements */ analyzeJSXExpression(expression, sourceCode) { const componentReferences = (0, utils_1.extractComponentReferencesFromNode)(expression, sourceCode); let htmlElementReferences = []; if (this.config.includeHtmlElements) { const filter = this.config.htmlElementFilter; htmlElementReferences = (0, utils_1.extractHTMLElementReferencesFromNode)(expression, sourceCode, filter.includeTags, filter.excludeTags, filter.includeAll); } const conditionalPatterns = this.conditionalParser.analyzeExpression(expression, sourceCode); return { components: componentReferences, htmlElements: htmlElementReferences, hasConditional: conditionalPatterns.length > 0, conditionalPatterns, }; } /** * NEW: Gets analysis statistics */ getAnalysisStats(returnStatements) { const totalReturns = returnStatements.length; const conditionalReturns = returnStatements.filter((stmt) => stmt.hasConditional).length; let totalComponents = 0; let totalHtmlElements = 0; let totalConditionalPatterns = 0; for (const statement of returnStatements) { totalComponents += statement.componentReferences.length; totalHtmlElements += statement.htmlElementReferences.length; totalConditionalPatterns += statement.conditionalPatterns.length; } return { totalReturns, conditionalReturns, totalComponents, totalHtmlElements, totalConditionalPatterns, }; } } exports.JSXReturnAnalyzer = JSXReturnAnalyzer;