sicua
Version:
A tool for analyzing project structure and dependencies
372 lines (371 loc) • 16.3 kB
JavaScript
"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;