sicua
Version:
A tool for analyzing project structure and dependencies
390 lines (389 loc) • 14.9 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.isReactComponent = isReactComponent;
exports.isReactComponentDefinition = isReactComponentDefinition;
exports.isReactComponentElement = isReactComponentElement;
exports.getJSXElementName = getJSXElementName;
exports.isHTMLElement = isHTMLElement;
exports.isReactComponentBabel = isReactComponentBabel;
exports.containsJSX = containsJSX;
exports.getBabelFunctionName = getBabelFunctionName;
const typescript_1 = __importDefault(require("typescript"));
const t = __importStar(require("@babel/types"));
/**
* Enhanced TypeScript version - Checks if a node is a React component
* Combines TypeScript type checking with deep JSX analysis
*/
function isReactComponent(node, typeChecker) {
// Check if it's a function-like node
if (!typescript_1.default.isFunctionDeclaration(node) &&
!typescript_1.default.isArrowFunction(node) &&
!typescript_1.default.isFunctionExpression(node) &&
!typescript_1.default.isMethodDeclaration(node)) {
return false;
}
// Get function name for naming convention check
const functionName = getFunctionName(node);
// Check naming convention (PascalCase) - React components should start with uppercase
if (functionName && functionName[0] !== functionName[0].toUpperCase()) {
return false;
}
// Primary method: Use TypeScript type checker if available (most reliable)
if (typeChecker) {
const signature = typeChecker.getSignatureFromDeclaration(node);
if (signature) {
const returnType = typeChecker.getReturnTypeOfSignature(signature);
const returnTypeString = typeChecker.typeToString(returnType);
// Check for React return types
if (returnTypeString.includes("JSX.Element") ||
returnTypeString.includes("ReactElement") ||
returnTypeString.includes("ReactNode") ||
returnTypeString.includes("Element") ||
returnTypeString.includes("React.ReactElement") ||
returnTypeString.includes("React.ReactNode")) {
return true;
}
}
}
// Fallback: Enhanced JSX pattern analysis
return containsJSXPatterns(node);
}
/**
* Check if a custom FunctionDefinition object is a React component
*/
function isReactComponentDefinition(funcDef) {
// If reactSpecific info is already analyzed, use it
if (funcDef.reactSpecific) {
return true;
}
// Check naming convention (PascalCase)
if (!funcDef.name || funcDef.name[0] !== funcDef.name[0].toUpperCase()) {
return false;
}
// Check patterns for React-specific usage
if (funcDef.patterns &&
funcDef.patterns.some((pattern) => pattern.toString().toLowerCase().includes("jsx") ||
pattern.toString().toLowerCase().includes("react"))) {
return true;
}
// Check dependencies for React-related imports
if (funcDef.dependencies &&
funcDef.dependencies.some((dep) => dep.name === "React" ||
dep.name === "react" ||
dep.name.includes("jsx") ||
dep.name.includes("JSX"))) {
return true;
}
// If function is exported and follows naming convention, likely a component
if (funcDef.isExported && funcDef.name[0] === funcDef.name[0].toUpperCase()) {
return true;
}
return false;
}
/**
* Check if a JSX element represents a React component (vs HTML element)
*/
function isReactComponentElement(node) {
const elementName = getJSXElementName(node.openingElement.name);
// React components start with uppercase, HTML elements with lowercase
return (elementName.length > 0 && elementName[0] === elementName[0].toUpperCase());
}
/**
* Get the name of a JSX element
*/
function getJSXElementName(name) {
if (t.isJSXIdentifier(name)) {
return name.name;
}
if (t.isJSXMemberExpression(name)) {
// Handle cases like <Component.SubComponent>
const object = getJSXElementName(name.object);
const property = name.property.name;
return `${object}.${property}`;
}
if (t.isJSXNamespacedName(name)) {
// Handle cases like <namespace:Component>
return `${name.namespace.name}:${name.name.name}`;
}
return "Unknown";
}
/**
* Check if a JSX element is an HTML element (vs React component)
*/
function isHTMLElement(node) {
return !isReactComponentElement(node);
}
/**
* Extract function name from various TypeScript function node types
*/
function getFunctionName(node) {
if (typescript_1.default.isFunctionDeclaration(node) && node.name) {
return node.name.text;
}
if (typescript_1.default.isMethodDeclaration(node) && typescript_1.default.isIdentifier(node.name)) {
return node.name.text;
}
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 parent.name.text;
}
// Check for property assignment (e.g., obj.MyComponent = () => ...)
if (typescript_1.default.isPropertyAssignment(parent) && typescript_1.default.isIdentifier(parent.name)) {
return parent.name.text;
}
// Check for export assignment (e.g., export const MyComponent = () => ...)
if (typescript_1.default.isBindingElement(parent) && typescript_1.default.isIdentifier(parent.name)) {
return parent.name.text;
}
}
return "";
}
/**
* Enhanced JSX pattern detection for TypeScript nodes
*/
function containsJSXPatterns(node) {
const nodeText = node.getText();
// Check for obvious JSX patterns
const hasBasicJSX = (nodeText.includes("return (") || nodeText.includes("return<")) &&
(nodeText.includes("</") || nodeText.includes("/>"));
// Check for React.createElement calls
const hasReactCalls = nodeText.includes("React.createElement") ||
nodeText.includes("jsx(") ||
nodeText.includes("jsxs(") ||
nodeText.includes("_jsx(") ||
nodeText.includes("_jsxs(");
if (hasBasicJSX || hasReactCalls) {
return true;
}
// Deep JSX analysis using TypeScript AST traversal
return hasDeepJSXPatterns(node);
}
/**
* Deep JSX pattern analysis using TypeScript AST traversal
*/
function hasDeepJSXPatterns(node) {
let foundJSX = false;
function visit(child) {
if (foundJSX)
return;
// Check for JSX elements
if (typescript_1.default.isJsxElement(child) ||
typescript_1.default.isJsxSelfClosingElement(child) ||
typescript_1.default.isJsxFragment(child)) {
foundJSX = true;
return;
}
// Check return statements
if (typescript_1.default.isReturnStatement(child) && child.expression) {
if (containsJSXInExpression(child.expression)) {
foundJSX = true;
return;
}
}
// Check arrow function bodies
if (typescript_1.default.isArrowFunction(child) && child.body) {
if (typescript_1.default.isExpression(child.body) && containsJSXInExpression(child.body)) {
foundJSX = true;
return;
}
}
typescript_1.default.forEachChild(child, visit);
}
typescript_1.default.forEachChild(node, visit);
return foundJSX;
}
/**
* Check if an expression contains JSX (handles complex expressions)
*/
function containsJSXInExpression(expr) {
// Direct JSX
if (typescript_1.default.isJsxElement(expr) ||
typescript_1.default.isJsxSelfClosingElement(expr) ||
typescript_1.default.isJsxFragment(expr)) {
return true;
}
// Conditional expressions (ternary)
if (typescript_1.default.isConditionalExpression(expr)) {
return (containsJSXInExpression(expr.whenTrue) ||
containsJSXInExpression(expr.whenFalse));
}
// Binary expressions (&&, ||)
if (typescript_1.default.isBinaryExpression(expr)) {
return (containsJSXInExpression(expr.left) ||
containsJSXInExpression(expr.right));
}
// Array expressions (for .map() etc.)
if (typescript_1.default.isArrayLiteralExpression(expr)) {
return expr.elements.some((element) => element && typescript_1.default.isExpression(element) && containsJSXInExpression(element));
}
// Parenthesized expressions
if (typescript_1.default.isParenthesizedExpression(expr)) {
return containsJSXInExpression(expr.expression);
}
// Call expressions (might return JSX)
if (typescript_1.default.isCallExpression(expr)) {
// Check if it's a .map() call that might return JSX
const text = expr.getText();
if (text.includes(".map(") &&
(text.includes("</") || text.includes("/>"))) {
return true;
}
}
return false;
}
/**
* Babel version - Enhanced with better JSX detection
* Checks if a function node is a React component
*/
function isReactComponentBabel(node, functionName) {
// Check naming convention if name is provided
if (functionName && functionName[0] !== functionName[0].toUpperCase()) {
return false;
}
// Check if arrow function with direct JSX return
if (t.isArrowFunctionExpression(node) && !t.isBlockStatement(node.body)) {
return containsJSX(node.body);
}
// Check function body for JSX returns
if (node.body && t.isBlockStatement(node.body)) {
return node.body.body.some((statement) => {
if (t.isReturnStatement(statement) && statement.argument) {
return containsJSX(statement.argument);
}
return false;
});
}
return false;
}
/**
* Enhanced JSX detection for Babel AST nodes
*/
function containsJSX(node) {
// Direct JSX elements
if (t.isJSXElement(node) || t.isJSXFragment(node)) {
return true;
}
// Conditional expressions (ternary operators)
if (t.isConditionalExpression(node)) {
return containsJSX(node.consequent) || containsJSX(node.alternate);
}
// Logical expressions (&&, ||)
if (t.isLogicalExpression(node)) {
return containsJSX(node.left) || containsJSX(node.right);
}
// Binary expressions
if (t.isBinaryExpression(node)) {
return containsJSX(node.left) || containsJSX(node.right);
}
// Array expressions (for mapping)
if (t.isArrayExpression(node)) {
return node.elements.some((element) => element && containsJSX(element));
}
// JSX expression containers
if (t.isJSXExpressionContainer(node) &&
!t.isJSXEmptyExpression(node.expression)) {
return containsJSX(node.expression);
}
// Return statements
if (t.isReturnStatement(node) && node.argument) {
return containsJSX(node.argument);
}
// Block statements (function bodies)
if (t.isBlockStatement(node)) {
return node.body.some((statement) => {
if (t.isReturnStatement(statement) && statement.argument) {
return containsJSX(statement.argument);
}
return containsJSX(statement);
});
}
// Arrow function bodies
if (t.isArrowFunctionExpression(node)) {
return containsJSX(node.body);
}
// Function bodies
if ((t.isFunctionDeclaration(node) || t.isFunctionExpression(node)) &&
node.body) {
return containsJSX(node.body);
}
// Call expressions (might be component calls or .map() returning JSX)
if (t.isCallExpression(node)) {
return node.arguments.some((arg) => containsJSX(arg));
}
// Member expressions (for chained calls like items.map(...))
if (t.isMemberExpression(node)) {
return containsJSX(node.object) || containsJSX(node.property);
}
// Parenthesized expressions
if (t.isParenthesizedExpression(node)) {
return containsJSX(node.expression);
}
// Sequence expressions
if (t.isSequenceExpression(node)) {
return node.expressions.some((expr) => containsJSX(expr));
}
// Check for JSX children in nodes that might have them
if ("children" in node && Array.isArray(node.children)) {
return node.children.some((child) => containsJSX(child));
}
return false;
}
/**
* Utility function to get function name from Babel AST nodes
*/
function getBabelFunctionName(node, parent) {
// Function declarations
if (t.isFunctionDeclaration(node) && node.id) {
return node.id.name;
}
// Check parent context for arrow functions and function expressions
if (parent) {
// Variable declarations: const MyComponent = () => {}
if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
return parent.id.name;
}
// Property assignments: obj.MyComponent = () => {}
if (t.isAssignmentExpression(parent) && t.isMemberExpression(parent.left)) {
const property = parent.left.property;
if (t.isIdentifier(property)) {
return property.name;
}
}
// Object method: { MyComponent() {} }
if (t.isObjectMethod(parent) && t.isIdentifier(parent.key)) {
return parent.key.name;
}
// Object property: { MyComponent: () => {} }
if (t.isObjectProperty(parent) && t.isIdentifier(parent.key)) {
return parent.key.name;
}
}
return "";
}