UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

390 lines (389 loc) 14.9 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.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 ""; }