UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

189 lines (188 loc) 6.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NodeUtils = void 0; const typescript_1 = __importDefault(require("typescript")); const ASTUtils_1 = require("../../../utils/ast/ASTUtils"); const reactSpecific_1 = require("../../../utils/ast/reactSpecific"); /** * Utility functions for finding and analyzing nodes in the TypeScript AST */ class NodeUtils { /** * Finds a component node in the source file by name */ static findComponentNode(node, componentName, typeChecker) { if (typescript_1.default.isFunctionDeclaration(node)) { // Function declaration component if (node.name && node.name.text === componentName && (0, reactSpecific_1.isReactComponent)(node, typeChecker)) { return node; } } else if (typescript_1.default.isVariableDeclaration(node)) { // Variable declaration (including arrow functions) if (typescript_1.default.isIdentifier(node.name) && node.name.text === componentName) { if (node.initializer) { if (typescript_1.default.isArrowFunction(node.initializer) || typescript_1.default.isFunctionExpression(node.initializer)) { return node.initializer; } } return node; } } else if (typescript_1.default.isExportAssignment(node)) { // Export default if (typescript_1.default.isIdentifier(node.expression) && node.expression.text === componentName) { return node.expression; } } let result; typescript_1.default.forEachChild(node, (child) => { if (!result) { result = NodeUtils.findComponentNode(child, componentName, typeChecker); } }); return result; } /** * Extracts state names from an array binding pattern (useState) */ static extractStateNames(declaration) { try { if (!ASTUtils_1.ASTUtils.safeIsArrayBindingPattern(declaration.name)) { return ["", ""]; } const elements = declaration.name.elements; if (elements.length < 2) return ["", ""]; const getName = (element) => { try { if (typescript_1.default.isBindingElement(element) && typescript_1.default.isIdentifier(element.name)) { return ASTUtils_1.ASTUtils.safeGetNodeText(element.name); } } catch { // Handle any potential errors in accessing node properties } return ""; }; return [getName(elements[0]), getName(elements[1])]; } catch { return ["", ""]; } } /** * Determines the scope of a node (render, effect, event, or other) */ static determineScope(node) { let current = node; while (current) { if (typescript_1.default.isJsxElement(current)) return "render"; if (typescript_1.default.isCallExpression(current) && typescript_1.default.isIdentifier(current.expression) && current.expression.text.startsWith("use")) return "effect"; if (ASTUtils_1.ASTUtils.isEventHandler(current)) return "event"; current = current.parent; } return "other"; } /** * Finds import statements in a source file */ static findImports(sourceFile) { const imports = []; typescript_1.default.forEachChild(sourceFile, (node) => { if (typescript_1.default.isImportDeclaration(node) && typescript_1.default.isStringLiteral(node.moduleSpecifier)) { imports.push(node.moduleSpecifier.text); } }); return imports; } /** * Finds a conditional expression for a JSX render */ static findRenderCondition(node) { let current = node; while (current) { if (typescript_1.default.isConditionalExpression(current)) { return current.condition; } if (typescript_1.default.isIfStatement(current)) { return current.expression; } if (typescript_1.default.isBinaryExpression(current) && (current.operatorToken.kind === typescript_1.default.SyntaxKind.AmpersandAmpersandToken || current.operatorToken.kind === typescript_1.default.SyntaxKind.BarBarToken)) { return current; } current = current.parent; } return undefined; } /** * Finds error states referenced in a condition */ static findRelatedErrorStates(condition, errorStates) { if (!condition) return []; const relatedStates = []; const visit = (node) => { if (typescript_1.default.isIdentifier(node)) { const name = node.getText(); if (errorStates.has(name)) { relatedStates.push(name); } } typescript_1.default.forEachChild(node, visit); }; visit(condition); return [...new Set(relatedStates)]; } /** * Checks if a try-catch block contains fallback rendering */ static detectFallbackRender(catchClause) { let hasFallback = false; const visit = (node) => { if (typescript_1.default.isJsxElement(node) || typescript_1.default.isJsxSelfClosingElement(node)) { hasFallback = true; return; } typescript_1.default.forEachChild(node, visit); }; visit(catchClause); return hasFallback; } /** * Checks if a catch clause contains error logging */ static hasErrorLogging(catchClause) { let hasLogging = false; const visit = (node) => { if (typescript_1.default.isCallExpression(node)) { const text = node.expression.getText(); if (text.includes("console.error") || text.includes("log") || text.includes("report")) { hasLogging = true; return; } } typescript_1.default.forEachChild(node, visit); }; visit(catchClause); return hasLogging; } } exports.NodeUtils = NodeUtils;