UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

309 lines (308 loc) 11 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.replacer = void 0; exports.isNodeCommented = isNodeCommented; exports.isLikelyFrontendFunction = isLikelyFrontendFunction; exports.isLikelyEventHandler = isLikelyEventHandler; exports.extractDependencies = extractDependencies; exports.extractCalledFunctions = extractCalledFunctions; exports.hasJsxReturn = hasJsxReturn; exports.safeNodeText = safeNodeText; exports.serializeJsxElement = serializeJsxElement; exports.isSimpleSetter = isSimpleSetter; exports.usesReactHooks = usesReactHooks; exports.usesFrontendAPIs = usesFrontendAPIs; exports.usesThisKeyword = usesThisKeyword; exports.hasReactSpecificOperations = hasReactSpecificOperations; exports.hasReducerPattern = hasReducerPattern; exports.hasStateSpreadPattern = hasStateSpreadPattern; exports.generateComponentId = generateComponentId; const typescript_1 = __importDefault(require("typescript")); const path = __importStar(require("path")); const reactOperations_1 = require("../../constants/reactOperations"); function isNodeCommented(sourceFile, node) { const nodeStart = node.getFullStart(); const nodeEnd = node.getEnd(); const sourceText = sourceFile.getFullText(); const textBeforeNode = sourceText.substring(0, nodeStart); const lastNewLineIndex = textBeforeNode.lastIndexOf("\n"); const lineStart = lastNewLineIndex === -1 ? 0 : lastNewLineIndex + 1; if (textBeforeNode.substring(lineStart).trim().startsWith("//")) { return true; } const textAroundNode = sourceText.substring(Math.max(0, nodeStart - 2), nodeEnd + 2); if (textAroundNode.startsWith("/*") && textAroundNode.endsWith("*/")) { return true; } return false; } function isLikelyFrontendFunction(fileName, functionName) { const frontendIndicators = [ "component", "page", "view", "screen", "reducer", "action", "store", "context", "hook", "provider", "render", "styles", ]; const lowerFileName = fileName.toLowerCase(); const lowerFunctionName = functionName.toLowerCase(); return frontendIndicators.some((indicator) => lowerFileName.includes(indicator) || lowerFunctionName.includes(indicator)); } function isLikelyEventHandler(functionName) { const eventHandlerPrefixes = ["handle", "on"]; const lowerFunctionName = functionName.toLowerCase(); return eventHandlerPrefixes.some((prefix) => lowerFunctionName.startsWith(prefix)); } function extractDependencies(node) { const dependencies = []; function visit(n) { if (typescript_1.default.isIdentifier(n) && !typescript_1.default.isPropertyAccessExpression(n.parent)) { dependencies.push(n.getText()); } typescript_1.default.forEachChild(n, visit); } visit(node); return Array.from(new Set(dependencies)); } function extractCalledFunctions(node) { const calledFunctions = []; function visit(n) { if (typescript_1.default.isCallExpression(n) && typescript_1.default.isIdentifier(n.expression)) { calledFunctions.push(n.expression.getText()); } typescript_1.default.forEachChild(n, visit); } visit(node); return Array.from(new Set(calledFunctions)); } function hasJsxReturn(node) { if (typescript_1.default.isJsxElement(node) || typescript_1.default.isJsxFragment(node) || typescript_1.default.isJsxSelfClosingElement(node)) { return true; } if (typescript_1.default.isReturnStatement(node) && node.expression) { return hasJsxReturn(node.expression); } return node.getChildren().some((child) => hasJsxReturn(child)); } function safeNodeText(node) { if (!node) return ""; try { return node.getText(); } catch { return ""; } } // Helper function to safely serialize JSX elements function serializeJsxElement(node) { try { return node.getText(); } catch { return ""; } } const replacer = (key, value) => { if (value instanceof Set) { return Array.from(value); } // Handle TypeScript nodes if (key === "element" && value && typeof value.getText === "function") { return value.getText(); } if (key === "condition" && value && typeof value.getText === "function") { return value.getText(); } return value; }; exports.replacer = replacer; function isSimpleSetter(node) { if (!typescript_1.default.isArrowFunction(node)) { return false; } try { // Check if it's a simple setter pattern: (value) => set({ prop: value }) const body = node.body; if (typescript_1.default.isCallExpression(body)) { const callee = body.expression; if (typescript_1.default.isIdentifier(callee) && callee.text === "set") { return true; } } // Check if it's a block with a single set statement if (typescript_1.default.isBlock(body) && body.statements.length === 1) { const statement = body.statements[0]; if (typescript_1.default.isExpressionStatement(statement)) { const expr = statement.expression; if (typescript_1.default.isCallExpression(expr)) { const callee = expr.expression; if (typescript_1.default.isIdentifier(callee) && callee.text === "set") { return true; } } } } return false; } catch (error) { console.warn("Error in isSimpleSetter:", error); return false; } } function usesReactHooks(node) { if (typescript_1.default.isCallExpression(node) && typescript_1.default.isIdentifier(node.expression)) { const hookNames = [ "useState", "useEffect", "useContext", "useReducer", "useCallback", "useMemo", "useRef", ]; if (hookNames.includes(node.expression.text)) { return true; } } return node.getChildren().some((child) => usesReactHooks(child)); } function usesFrontendAPIs(node) { if (typescript_1.default.isPropertyAccessExpression(node)) { const frontendAPIs = [ "localStorage", "sessionStorage", "document", "window", "navigator", "history", "location", ]; if (frontendAPIs.includes(node.expression.getText())) { return true; } } if (typescript_1.default.isCallExpression(node) && typescript_1.default.isIdentifier(node.expression)) { const frontendFunctions = [ "alert", "confirm", "prompt", "setTimeout", "setInterval", "requestAnimationFrame", ]; if (frontendFunctions.includes(node.expression.text)) { return true; } } return node.getChildren().some((child) => usesFrontendAPIs(child)); } function usesThisKeyword(node) { if (node.kind === typescript_1.default.SyntaxKind.ThisKeyword) { return true; } return node.getChildren().some((child) => usesThisKeyword(child)); } function hasReactSpecificOperations(node) { try { const nodeText = node.getText().toLowerCase(); // Early return for simple setters if (isSimpleSetter(node)) { return true; } // Get all identifiers in the node const identifiers = []; const collectIdentifiers = (n) => { if (typescript_1.default.isIdentifier(n)) { identifiers.push(n.text.toLowerCase()); } typescript_1.default.forEachChild(n, collectIdentifiers); }; collectIdentifiers(node); return reactOperations_1.REACT_OPERATIONS.some((op) => identifiers.includes(op) || nodeText.includes(op)); } catch (error) { console.warn("Error in hasReactSpecificOperations:", error); // Default to false if we can't determine return false; } } function hasReducerPattern(node) { let hasSwitchOrIfOnActionType = false; const checkActionType = (expr) => { if (typescript_1.default.isPropertyAccessExpression(expr)) { return (expr.name.text === "type" && expr.expression.getText() === "action"); } return false; }; typescript_1.default.forEachChild(node, (child) => { if (typescript_1.default.isSwitchStatement(child) && checkActionType(child.expression)) { hasSwitchOrIfOnActionType = true; } if (typescript_1.default.isIfStatement(child) && child.expression && checkActionType(child.expression)) { hasSwitchOrIfOnActionType = true; } }); return hasSwitchOrIfOnActionType; } function hasStateSpreadPattern(node) { let hasSpreadOperator = false; let spreadsState = false; const visitor = (n) => { if (typescript_1.default.isSpreadElement(n)) { hasSpreadOperator = true; if (n.expression.getText() === "state") { spreadsState = true; } } if (!spreadsState) { typescript_1.default.forEachChild(n, visitor); } }; typescript_1.default.forEachChild(node, visitor); return hasSpreadOperator && spreadsState; } /** * Generate unique component ID combining file path and component name */ function generateComponentId(component) { const fileName = path.basename(component.fullPath, path.extname(component.fullPath)); return `${fileName}#${component.name}`; }