sicua
Version:
A tool for analyzing project structure and dependencies
114 lines (113 loc) • 4.3 kB
JavaScript
;
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;
}