Files
Housekeeping-web-admin/node_modules/@base44/vite-plugin/dist/processors/collection-id-processor.js
T
2026-06-24 09:48:54 +02:00

199 lines
7.5 KiB
JavaScript

import { DATA_COLLECTION_ID, DATA_COLLECTION_REFERENCE, MAX_JSX_DEPTH, } from "../consts.js";
import { JSXUtils } from "../jsx-utils.js";
import { JSXAttributeUtils, PathNavigationUtils, ExpressionAnalysisUtils, } from "./utils/shared-utils.js";
import { CollectionTracingUtils } from "./utils/collection-tracing-utils.js";
export class CollectionIdProcessor {
types;
attributeUtils;
pathUtils;
expressionUtils;
tracingUtils;
constructor(types) {
this.types = types;
this.attributeUtils = new JSXAttributeUtils(types);
this.pathUtils = new PathNavigationUtils(types);
this.expressionUtils = new ExpressionAnalysisUtils(types);
this.tracingUtils = new CollectionTracingUtils(types);
}
process(path) {
if (this.attributeUtils.hasAttribute(path, DATA_COLLECTION_ID))
return;
const info = this.tryDirectMapInChildren(path) ??
this.tryGetByIdInChildren(path) ??
this.tryComponentRootWithCollectionProp(path);
if (!info)
return;
const target = this.pathUtils.findDOMElementTarget(path) ?? path;
this.attributeUtils.addStringAttribute(target, DATA_COLLECTION_ID, info.id);
if (info.references.length > 0) {
this.attributeUtils.addStringAttribute(target, DATA_COLLECTION_REFERENCE, info.references.join(","));
}
}
tryDirectMapInChildren(path) {
const jsxElement = path.parentPath;
if (!jsxElement?.isJSXElement())
return null;
const children = jsxElement.get("children");
for (const child of children) {
if (!child.isJSXExpressionContainer())
continue;
const expression = child.get("expression");
if (expression.isJSXEmptyExpression())
continue;
const mapSource = this.extractMapSource(expression);
if (mapSource)
return mapSource;
}
return null;
}
extractMapSource(expr) {
if (expr.isCallExpression()) {
return this.traceMapCall(expr);
}
if (expr.isLogicalExpression()) {
if (expr.node.operator === "&&") {
const right = expr.get("right");
return this.extractMapSource(right);
}
}
if (expr.isConditionalExpression()) {
const consequent = expr.get("consequent");
const alternate = expr.get("alternate");
return (this.extractMapSource(consequent) ?? this.extractMapSource(alternate));
}
if (expr.isOptionalCallExpression()) {
return this.traceOptionalMapCall(expr);
}
return null;
}
traceMapCall(callPath) {
const callee = callPath.get("callee");
if (!callee.isMemberExpression())
return null;
const property = callee.get("property");
if (!property.isIdentifier() ||
(property.node.name !== "map" && property.node.name !== "flatMap")) {
return null;
}
if (!this.callbackProducesJSX(callPath))
return null;
const arrayObj = callee.get("object");
return this.tracingUtils.traceCollectionSource(arrayObj);
}
traceOptionalMapCall(callPath) {
const callee = callPath.get("callee");
if (!callee.isMemberExpression() &&
!callee.isOptionalMemberExpression()) {
return null;
}
const property = callee.get("property");
if (!property.isIdentifier() ||
(property.node.name !== "map" && property.node.name !== "flatMap")) {
return null;
}
const arrayObj = callee.get("object");
return this.tracingUtils.traceCollectionSource(arrayObj);
}
callbackProducesJSX(callPath) {
const args = callPath.get("arguments");
const callback = args[0];
if (!callback)
return false;
if (callback.isArrowFunctionExpression()) {
const body = callback.get("body");
if (body.isBlockStatement()) {
return JSXUtils.doesBlockReturnJSX(body.node);
}
return JSXUtils.producesJSX(body.node);
}
if (callback.isFunctionExpression()) {
return JSXUtils.doesBlockReturnJSX(callback.node.body);
}
return false;
}
tryGetByIdInChildren(path, depth = 0) {
if (depth > MAX_JSX_DEPTH)
return null;
const jsxElement = path.parentPath;
if (!jsxElement?.isJSXElement())
return null;
const children = jsxElement.get("children");
for (const child of children) {
const info = this.checkChildForGetById(child, depth);
if (info)
return info;
}
for (const attr of path.node.attributes) {
if (this.types.isJSXAttribute(attr) &&
attr.value &&
this.types.isJSXExpressionContainer(attr.value)) {
const expr = attr.value.expression;
if (this.types.isMemberExpression(expr)) {
const info = this.tracingUtils.traceGetByIdSource(path);
if (info)
return info;
}
}
}
return null;
}
checkChildForGetById(child, depth) {
if (child.isJSXExpressionContainer()) {
const expr = child.get("expression");
if (expr.isMemberExpression() ||
expr.isOptionalMemberExpression()) {
return this.tracingUtils.traceGetByIdSource(child);
}
}
if (child.isJSXElement()) {
const childOpening = child.get("openingElement");
return this.tryGetByIdInChildren(childOpening, depth + 1);
}
return null;
}
tryComponentRootWithCollectionProp(path) {
if (!this.pathUtils.isRootReturnElement(path))
return null;
const fn = this.pathUtils.findEnclosingFunction(path);
if (!fn)
return null;
let info = null;
fn.traverse({
CallExpression: (callPath) => {
if (info)
return;
const callee = callPath.get("callee");
if (!callee.isMemberExpression())
return;
const prop = callee.get("property");
if (!prop.isIdentifier() ||
(prop.node.name !== "map" && prop.node.name !== "flatMap")) {
return;
}
if (!this.callbackProducesJSX(callPath))
return;
// Skip map calls that are directly embedded as JSX children — those
// are already handled by tryDirectMapInChildren on their immediate
// parent, so attributing them to the component root would be wrong.
if (this.isCallInsideJSXChildren(callPath))
return;
const arrayObj = callee.get("object");
info = this.tracingUtils.traceCollectionSource(arrayObj);
},
});
return info;
}
isCallInsideJSXChildren(path) {
let current = path.parentPath;
while (current) {
if (current.isFunction())
break;
if (current.isJSXExpressionContainer()) {
return current.parentPath?.isJSXElement() ?? false;
}
current = current.parentPath;
}
return false;
}
}
//# sourceMappingURL=collection-id-processor.js.map