UNPKG

@bitloops/bl-transpiler

Version:
655 lines 29.9 kB
import { BitloopsTypesMapping } from '../../../helpers/mappings.js'; import { ReturnStatementNode } from './nodes/statements/ReturnStatementNode.js'; import { isArray, isObject } from '../../../helpers/typeGuards/typeGuards.js'; import { IdentifierExpressionNode } from './nodes/Expression/IdentifierExpression.js'; import { MemberDotExpressionNode } from './nodes/Expression/MemberDot/MemberDotExpressionNode.js'; import { MethodCallExpressionNode } from './nodes/Expression/MethodCallExpression.js'; import { ConstDeclarationNode } from './nodes/statements/ConstDeclarationNode.js'; import { VariableDeclarationNode } from './nodes/statements/variableDeclaration.js'; import { EntityEvaluationNode } from './nodes/Expression/Evaluation/EntityEvaluation.js'; import { ValueObjectEvaluationNode } from './nodes/Expression/Evaluation/ValueObjectEvaluation.js'; import { DomainServiceEvaluationNode } from './nodes/Expression/Evaluation/DomainServiceEvaluationNode.js'; export class IntermediateASTTree { currentNode; rootNode; constructor(rootNode) { this.rootNode = rootNode; this.currentNode = rootNode; } /** * It inserts its child to currentNode * and makes it the new currentNode */ insertChild(childNode) { this.currentNode.addChild(childNode); this.currentNode = childNode; } /** * It inserts the node as child to the parent of the currentNode * and makes it the new currentNode */ insertSibling(siblingNode) { const parentNode = this.currentNode.getParent(); parentNode.addChild(siblingNode); this.currentNode = siblingNode; } /** * Sets the CurrentNode back to RootNode */ setCurrentNodeToRoot() { this.currentNode = this.rootNode; } getCurrentNode() { return this.currentNode; } getRootNode() { return this.rootNode; } getRootChildrenNodesByType(nodeType) { const rootChildren = this.rootNode.getChildren(); const classTypeNodes = []; for (const child of rootChildren) { if (child.getNodeType() === nodeType) { classTypeNodes.push(child); } } return classTypeNodes; } getRootChildrenNodesValueByType(nodeType) { const nodes = this.getRootChildrenNodesByType(nodeType); return nodes.map((node) => node.getValue()); } mergeWithTree(tree) { tree.rootNode.getChildren().map((childNode) => { this.rootNode.addChild(childNode); }); return this; } /** A * B - F * C-E G-H-I * D K */ traverse(currentNode, cb) { if (currentNode.isLeaf()) { return cb(currentNode); } cb(currentNode); const nodeChildren = currentNode.getChildren(); for (const child of nodeChildren) { this.traverse(child, cb); } } validateParentRefs() { this.traverse(this.rootNode, (node) => { const nodeChildren = node.getChildren(); for (const child of nodeChildren) { if (child.getParent() !== node) { throw new Error('Invalid parent ref for child'); } } }); } traverseBFS(currentNode, cb) { if (currentNode.isLeaf()) { return cb(currentNode); } cb(currentNode); const nodeChildren = currentNode.getChildren(); for (const child of nodeChildren) { cb(child); } for (const child of nodeChildren) { this.traverseBFS(child, cb); } } // TODO implement this copy() { return this; } buildValueRecursiveBottomUp(currentNode) { if (currentNode.isLeaf()) { return this.buildNodeValue(currentNode); } const nodeChildren = currentNode.getChildren(); for (const child of nodeChildren) { this.buildValueRecursiveBottomUp(child); } this.buildNodeValue(currentNode); } buildNodeValue(node) { if (node.isRoot()) return; const nodeValue = node.getValue()[node.getClassNodeName()]; if (node.isLeaf()) { return node.buildLeafValue(nodeValue); } if (isArray(nodeValue)) { return node.buildArrayValue(); } if (isObject(nodeValue)) { return node.buildObjectValue(); } return node.buildLeafValue(nodeValue); } getClassTypeNodes() { return this.getRootNode().getChildren(); } getClassTypeByIdentifier(identifier) { const classTypeNodes = this.getClassTypeNodes(); return (classTypeNodes.find((classTypeNode) => classTypeNode.getClassName() === identifier) || null); } getAggregateIdentifier(identifier) { return this.getNodeWithPolicy(this.rootNode, (node) => { return (node.IsEntityIdentifierNode() && node.getValue()?.entityIdentifier == identifier); }); } getAggregateNodeWithIdentifier(identifier) { const rootEntityNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TRootEntity); const isEntityIdentifierNode = (node) => node.getNodeType() === BitloopsTypesMapping.TEntityIdentifier; const isRootEntityNode = (node) => node.getNodeType() === BitloopsTypesMapping.TRootEntity; let rootEntityFound = null; for (const rootEntityNode of rootEntityNodes) { this.traverse(rootEntityNode, (node) => { if (isEntityIdentifierNode(node) && identifier === node.getValue().entityIdentifier && isRootEntityNode(rootEntityNode)) { rootEntityFound = rootEntityNode; } }); } return rootEntityFound; } getReadModelByIdentifier(identifier) { const readModelNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TReadModel); return (readModelNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier) || null); } getEntityByIdentifier = (identifier) => { const entityNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TEntity); let entityFound = null; for (const entityNode of entityNodes) { const entityIdentifier = entityNode.getIdentifier(); if (identifier === entityIdentifier.getValue().entityIdentifier) { entityFound = entityNode; } } return entityFound; }; getRootEntityByIdentifier = (identifier) => { const rootEntityNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TRootEntity); return (rootEntityNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier) ?? null); }; getValueObjectByIdentifier = (identifier) => { const valueObjectDeclarationNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TValueObject); return (valueObjectDeclarationNodes.find((node) => node.getIdentifierValue() === identifier) ?? null); }; getRepoPortByIdentifier = (identifier) => { const repoPortNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TRepoPort); return repoPortNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getQueryByIdentifier = (identifier) => { const queryNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TQuery); return queryNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getCommandByIdentifier = (identifier) => { const commandNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TCommand); return commandNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getQueryHandlerByIdentifier = (identifier) => { const queryHandlerNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TQueryHandler); return queryHandlerNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getDomainEventByIdentifier = (identifier) => { const domainEventNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TDomainEvent); return domainEventNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getIntegrationEventByIdentifier = (identifier) => { const integrationEventNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TIntegrationEvent); return integrationEventNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getStructByIdentifier = (identifier) => { const structNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TStruct); return structNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getPropsByIdentifier = (identifier) => { const propsNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TProps); return propsNodes.find((node) => node.getIdentifier().getIdentifierName() === identifier); }; getPropsFieldTypeOfDomainCreateByFieldIdentifier(parameterNode, identifier) { const propsNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TProps); const typeNode = parameterNode.getType(); const identifierTypeNode = typeNode.getBitloopsIdentifierTypeNode(); const propsTypeNodeValue = identifierTypeNode.getIdentifierName(); const isPropsNode = (node) => node.getNodeType() === BitloopsTypesMapping.TProps; for (const propsNode of propsNodes) { if (isPropsNode(propsNode) && propsNode.getIdentifierValue() === propsTypeNodeValue) { const fieldsListNode = propsNode.getFieldListNode(); const fieldNodes = fieldsListNode.getFieldNodes(); for (const fieldNode of fieldNodes) { const fieldIdentifier = fieldNode.getIdentifierNode(); if (fieldIdentifier.getValue().identifier === identifier) { return fieldNode.getTypeNode().getValue(); } } } } return null; } getMemberDotExpressions(intermediateASTNode) { const policy = (node) => node instanceof MemberDotExpressionNode; return this.getNodesWithPolicy(intermediateASTNode, policy); } getIdentifiersOfDomainEvaluations(statements) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (!expression || !expression.isEvaluation()) { return false; } const evaluation = expression.getEvaluationChild(); const evaluationIsDomainEvaluation = evaluation instanceof EntityEvaluationNode || evaluation instanceof ValueObjectEvaluationNode; if (!evaluationIsDomainEvaluation) { return false; } return true; }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getIdentifiersOfThisMethodCallExpressionsWithTwoMemberDots(statements) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (!expression) { return false; } if (expression.isThisMethodCallExpressionWithTwoMemberDots()) { return true; } return false; }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getIdentifiersOfDomainServiceResults(statements) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (expression.isDomainServiceEvaluationExpression()) { return true; } return false; }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getIdentifiersOfPackageEvaluations(statements) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (!expression) { return false; } if (expression.isPackageEvaluationExpression()) { return true; } return false; }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getIdentifiersOfAggregates(statements, parameters) { return [ ...this.getIdentifiersOfAggregatesFromRepoGetById(statements, parameters), ...this.getIdentifiersOfAggregatesFromEntityEvaluation(statements), ]; } getIdentifiersOfAggregatesFromRepoGetById(statements, parameters) { const repoGetterMethod = 'getById'; const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (!expression.isThisMethodCallExpressionWithTwoMemberDots()) { return false; } const { methodName } = expression.getIdentifierAndMethodNameOfThisMethodCall(); if (methodName !== repoGetterMethod) { return false; } return true; }; const result = []; for (const statement of statements) { const nodes = this.getNodesWithPolicy(statement, policy); for (const node of nodes) { const identifier = node.getIdentifier()?.getIdentifierName(); // Based on our policy, these should be variable declarations with getById method calls on some repos, // By finding the repo, we can find the entity identifier const expression = node.getExpressionValues(); const { identifier: dependencyIdentifier, methodName } = expression.getIdentifierAndMethodNameOfThisMethodCall(); if (methodName !== repoGetterMethod) { continue; } const dependency = parameters.find((parameter) => parameter.getIdentifier() === dependencyIdentifier); if (!dependency) { continue; } if (!dependency.hasRepoPortType()) { continue; } const repoPortDependencyIdentifier = dependency .getType() .getBitloopsIdentifierTypeNode() .getIdentifierName(); const reportNode = this.getRepoPortByIdentifier(repoPortDependencyIdentifier); if (!reportNode) { continue; } const entityIdentifierNode = reportNode.getEntityIdentifier(); const isEntityRepo = entityIdentifierNode !== null; if (!isEntityRepo) { continue; } const entityIdentifier = entityIdentifierNode.getIdentifierName(); if (identifier && entityIdentifier) { result.push({ identifier, entityIdentifier }); } } } return result; } getIdentifiersOfAggregatesFromEntityEvaluation(statements) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (expression.isAggregateEvaluationExpression()) { return true; } return false; }; const result = []; for (const statement of statements) { const nodes = this.getNodesWithPolicy(statement, policy); for (const node of nodes) { const identifier = node.getIdentifier()?.getIdentifierName(); // Based on our policy, these should be variable declarations with aggregate evaluation as expressions const expression = node.getExpressionValues(); if (!expression.isAggregateEvaluationExpression()) { throw new Error('This should not happen'); } const evaluation = expression.getEvaluation(); if (!evaluation.isEntityEvaluation()) { throw new Error('This should not happen'); } const entityIdentifier = evaluation.getEntityIdentifier(); if (identifier && entityIdentifier) { result.push({ identifier, entityIdentifier }); } } } return result; } getResultsOfDomainServiceMethods(statements, domainServiceIdentifiers) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (expression.isMethodCallOnIdentifier(domainServiceIdentifiers)) { return true; } return false; }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getResultOfAggregateMethodsThatReturnOkError(statements, aggregateIdentifiers) { const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); const entityIdentifiers = aggregateIdentifiers.map((i) => i.identifier); if (!expression.isMethodCallOnIdentifier(entityIdentifiers)) { return false; } // Now we need to find whether the method returns an OK,Error const { entityName, methodName } = expression.getEntityMethodCallInfo(); const entityType = aggregateIdentifiers.find((i) => i.identifier === entityName); const rootEntity = this.getRootEntityByIdentifier(entityType.entityIdentifier); if (!rootEntity) { throw new Error(`Could not find root entity with identifier ${entityType.entityIdentifier}`); } const publicMethod = rootEntity?.findPublicMethodByName(methodName); return publicMethod?.returnsOkError(); }; return this.getIdentifierOfVariableConstDeclaration(statements, policy); } getIdentifierOfVariableConstDeclaration(statements, policy) { const identifiers = []; for (const statement of statements) { const nodes = this.getNodesWithPolicy(statement, policy); for (const node of nodes) { const identifier = node.getIdentifier()?.getIdentifierName(); if (identifier) { identifiers.push(identifier); } } } return identifiers; } getIdentifiersOfDomainTypes(parameters) { const identifiers = []; for (const parameter of parameters) { const typeNode = parameter.getType(); if (typeNode.isBitloopsIdentifierType() || typeNode.isPrimaryWithBitloopsIdentifierTypeChild()) { const identifierTypeNode = typeNode.getBitloopsIdentifierTypeNode(); if (identifierTypeNode.isValueObjectIdentifier() || identifierTypeNode.isEntityIdentifier()) { identifiers.push(parameter.getIdentifier()); } } } return identifiers; } getIdentifierExpressionNodesInStatements(statements, identifiers) { const getIdentifierPredicate = (node) => node instanceof IdentifierExpressionNode && identifiers.includes(node.identifierName); const nodes = []; for (const statement of statements) { const identifiersOfStatements = this.getNodesWithPolicy(statement, getIdentifierPredicate); nodes.push(...identifiersOfStatements); } return nodes; } updateIdentifierExpressionNodesAfterStatement(baseStatement, identifierToReplace, newIdentifier) { const nodeIsOurIdentifierPredicate = (node) => node instanceof IdentifierExpressionNode && node.identifierName === identifierToReplace && node.isUsedByIsInstanceOfExpression() === false; const nodes = []; let nextStatement = baseStatement.getNextSibling(); while (nextStatement !== null) { const identifiersOfStatements = this.getNodesWithPolicy(nextStatement, nodeIsOurIdentifierPredicate); nodes.push(...identifiersOfStatements); nextStatement = nextStatement.getNextSibling(); } nodes.forEach((node) => (node.identifierName = newIdentifier)); } getReturnStatementsOfNode(intermediateASTNode) { const policy = (node) => node instanceof ReturnStatementNode; return this.getNodesWithPolicy(intermediateASTNode, policy); } getMethodCallsThatUseThisDependencies(dependencies, statements) { const policy = (node) => node instanceof MethodCallExpressionNode && dependencies.some((dep) => node.isThisDependencyMethodCall(dep)); const result = statements.reduce((acc, statement) => { const methodCalls = this.getNodesWithPolicy(statement, policy); return [...acc, ...methodCalls]; }, []); return result; } getDomainCreateOfEntity(entityNode) { const domainCreate = entityNode.getDomainCreateNode(); const propsNode = domainCreate.getParameterNode(); return propsNode; } getDomainCreateOfValueObject(valueObjectDeclarationNode) { const domainCreate = valueObjectDeclarationNode.getCreateNode(); const propsNode = domainCreate.getParameterNode(); return propsNode; } getPropsNodeOfEntity(entityNode) { const domainCreate = this.getDomainCreateOfEntity(entityNode); const typeNode = domainCreate.getType(); const identifierTypeNode = typeNode.getBitloopsIdentifierTypeNode(); const propsIdentifier = identifierTypeNode.getIdentifierName(); const propsNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TProps); const isPropsNode = (node) => node.getNodeType() === BitloopsTypesMapping.TProps; for (const propsNode of propsNodes) { if (isPropsNode(propsNode) && propsNode.getIdentifierValue() === propsIdentifier) { return propsNode; } } return null; } getPropsNodeOfValueObject(valueObjectNode) { const domainCreate = this.getDomainCreateOfValueObject(valueObjectNode); const typeNode = domainCreate.getType(); const identifierTypeNode = typeNode.getBitloopsIdentifierTypeNode(); const propsIdentifier = identifierTypeNode.getIdentifierName(); const propsNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TProps); const isPropsNode = (node) => node.getNodeType() === BitloopsTypesMapping.TProps; for (const propsNode of propsNodes) { if (isPropsNode(propsNode) && propsNode.getIdentifierValue() === propsIdentifier) { return propsNode; } } return null; } getValueObjectFieldsWithOnePrimitiveProperty(fieldListNode) { const valueObjectFieldsOfProp = fieldListNode.getValueObjectFields(); const valueObjectNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TValueObject); const result = []; const propsNodes = this.getRootChildrenNodesByType(BitloopsTypesMapping.TProps); const fieldsWithOnePrimitiveProperty = valueObjectFieldsOfProp.reduce((acc, field) => { const valueObjectIdentifier = field.fieldType; const valueObjectNode = this.findValueObject(valueObjectNodes, valueObjectIdentifier); if (!valueObjectNode) { throw new Error(`ValueObject ${field.fieldType} not found`); } const propsIdentifier = valueObjectNode.getPropsIdentifier(); const valueObjectPropsNode = this.findProps(propsNodes, propsIdentifier); if (!valueObjectPropsNode) { throw new Error(`Props ${propsIdentifier} not found`); } const hasOnlyOnePrimField = valueObjectPropsNode.hasOnlyOnePrimitiveField(); if (hasOnlyOnePrimField.result === false) { return acc; } const initialPropIdentifier = field.fieldValue; return [...acc, { fieldValue: initialPropIdentifier, fieldType: hasOnlyOnePrimField.type }]; }, result); return fieldsWithOnePrimitiveProperty; } findValueObject(valueObjectNodes, identifier) { return valueObjectNodes.find((node) => node.getIdentifierValue() === identifier); } findProps(propsNodes, identifier) { return propsNodes.find((node) => node.getIdentifierValue() === identifier); } getNodesWithPolicy(rootNode, predicate) { const resultNodes = []; this.traverse(rootNode, (node) => { if (predicate(node)) { resultNodes.push(node); return; } }); return resultNodes ?? null; } getNodeWithPolicy(rootNode, predicate) { let resultNode; this.traverse(rootNode, (node) => { if (predicate(node)) { resultNode = node; return; } }); return resultNode ?? null; } getIdentifiersOfDomainServiceEvaluations(statements) { const identifiers = []; const policy = (node) => { const statementIsVariableDeclaration = node instanceof ConstDeclarationNode || node instanceof VariableDeclarationNode; if (!statementIsVariableDeclaration) { return false; } const expression = node.getExpressionValues(); if (!expression.isEvaluation()) { return false; } const evaluation = expression.getEvaluationChild(); const evaluationIsDomainServiceEvaluation = evaluation instanceof DomainServiceEvaluationNode; if (!evaluationIsDomainServiceEvaluation) { return false; } return true; }; for (const statement of statements) { const nodes = this.getNodesWithPolicy(statement, policy); for (const node of nodes) { const identifier = node.getIdentifier()?.getIdentifierName(); if (identifier) { identifiers.push(identifier); } } } return identifiers; } getMethodDefinitionTypesOfRepoPort(repoPortNode) { let methodTypes = {}; const methodDefinitionNodes = repoPortNode.getMethodDefinitionNodes(); for (const methodDefinitionNode of methodDefinitionNodes) { const identifierNode = methodDefinitionNode.getIdentifierNode(); const typeNode = methodDefinitionNode.getTypeNode(); methodTypes[identifierNode.getIdentifierName()] = typeNode; } if (repoPortNode.isReadRepoPort()) { if (repoPortNode.extendsCRUDReadRepoPort) { const readMethodTypes = repoPortNode.getReadMethodTypes(); methodTypes = { ...methodTypes, ...readMethodTypes }; } } else if (repoPortNode.isWriteRepoPort()) { if (repoPortNode.extendsCRUDWriteRepoPort) { const writeMethodTypes = repoPortNode.getWriteMethodTypes(); methodTypes = { ...methodTypes, ...writeMethodTypes }; } } const extendIdentifiers = repoPortNode.getExtendsRepoPortIdentifiersExcludingCRUDOnes(); for (const extendIdentifier of extendIdentifiers) { const extendedRepoPortNode = this.getRepoPortByIdentifier(extendIdentifier); return { ...methodTypes, ...this.getMethodDefinitionTypesOfRepoPort(extendedRepoPortNode) }; } return methodTypes; } } //# sourceMappingURL=IntermediateASTTree.js.map