UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

114 lines (113 loc) 4.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractJSXStructure = extractJSXStructure; exports.parseJSXElement = parseJSXElement; exports.extractJSXProps = extractJSXProps; exports.hasClassName = hasClassName; const typescript_1 = __importDefault(require("typescript")); const ASTUtils_1 = require("../../../utils/ast/ASTUtils"); /** * Extracts JSX structure from a component node * @param node Component AST node * @param sourceFile Source file containing the component * @returns JSX structure or undefined if none found */ function extractJSXStructure(node, sourceFile) { // Find all JSX elements in the component const jsxElements = [ ...ASTUtils_1.ASTUtils.findNodes(sourceFile, typescript_1.default.isJsxElement), ...ASTUtils_1.ASTUtils.findNodes(sourceFile, typescript_1.default.isJsxSelfClosingElement), ]; if (!jsxElements.length) return undefined; // Find the root JSX element that's within this component's scope const rootJSX = findRootJSXElement(jsxElements, node); if (!rootJSX) return undefined; return parseJSXElement(rootJSX); } /** * Finds the root JSX element for a component * @param jsxElements All JSX elements in the source file * @param componentNode Component node * @returns Root JSX element or undefined */ function findRootJSXElement(jsxElements, componentNode) { // Find JSX element that's within the component's scope but not nested in other JSX return jsxElements.find((element) => { // Check if the element is within the component's scope let current = element; let foundInComponent = false; let nestedInOtherJSX = false; while (current) { if (current === componentNode) { foundInComponent = true; break; } // Check if this JSX is nested in another JSX if (typescript_1.default.isJsxElement(current) || typescript_1.default.isJsxSelfClosingElement(current)) { if (current !== element) { nestedInOtherJSX = true; break; } } current = current.parent; } return foundInComponent && !nestedInOtherJSX; }); } /** * Parses a JSX element into a JSX structure * @param element JSX element to parse * @returns Parsed JSX structure */ function parseJSXElement(element) { if (typescript_1.default.isJsxSelfClosingElement(element)) { return { tagName: element.tagName.getText(), props: extractJSXProps(element.attributes), children: [], }; } return { tagName: element.openingElement.tagName.getText(), props: extractJSXProps(element.openingElement.attributes), children: element.children .filter((child) => typescript_1.default.isJsxElement(child) || typescript_1.default.isJsxSelfClosingElement(child)) .map((child) => parseJSXElement(child)), }; } /** * Extracts props from JSX attributes * @param attributes JSX attributes object * @returns Array of prop signatures */ function extractJSXProps(attributes) { return attributes.properties.filter(typescript_1.default.isJsxAttribute).map((attr) => ({ name: attr.name.getText(), type: attr.initializer?.getText() ?? "true", required: true, // Assuming all present JSX props are required })); } /** * Checks if a JSX element has a particular className * @param element JSX element to check * @param className Class name to search for * @returns True if element has the class */ function hasClassName(element, className) { const attributes = typescript_1.default.isJsxElement(element) ? element.openingElement.attributes : element.attributes; for (const attr of attributes.properties) { if (typescript_1.default.isJsxAttribute(attr) && attr.name.getText() === "className") { if (attr.initializer && attr.initializer.getText().includes(className)) { return true; } } } return false; }