sicua
Version:
A tool for analyzing project structure and dependencies
189 lines (188 loc) • 6.9 kB
JavaScript
;
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;