@cuba-platform/front-generator
Version:
CUBA Platform front-end clients generator
205 lines • 9.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createEntityClass = exports.generateEntities = void 0;
const path = require("path");
const ts = require("typescript");
const ts_helpers_1 = require("../../../common/ts-helpers");
const entity_views_generation_1 = require("./entity-views-generation");
const import_utils_1 = require("../import-utils");
const constants_1 = require("../../../common/constants");
const utils_1 = require("../../../common/utils");
const model_utils_1 = require("./model-utils");
/**
*
* Generate TS entity classes from ProjectModel, write generated files to destDir
*
* @param projectModel project model entities generated from
* @param destDir where created TS files should be placed, also need to compute correct imports in generated TS files
* @param fs Yeoman MemFs editor
* @return model context contains entity and enum maps with fqn as key
*/
function generateEntities(projectModel, destDir, fs) {
const { entitiesMap, enumsMap } = (0, model_utils_1.collectModelContext)(projectModel);
for (const [, entityInfo] of entitiesMap) {
const { entity } = entityInfo;
const ctx = {
entitiesMap, entity, enumsMap, isBaseProjectEntity: entityInfo.isBaseProjectEntity
};
const { importInfos, classDeclaration } = createEntityClass(ctx);
const includes = (0, import_utils_1.createIncludes)(importInfos, createImportInfo(entityInfo, ctx.isBaseProjectEntity));
const views = (0, entity_views_generation_1.createEntityViewTypes)(entity, projectModel);
const entityPath = !entityInfo.isBaseProjectEntity ? constants_1.ENTITIES_DIR : path.posix.join(constants_1.ENTITIES_DIR, constants_1.BASE_ENTITIES_DIR);
fs.write(path.posix.join(destDir, entityPath, (0, utils_1.getEntityModulePath)(entity) + '.ts'), (0, ts_helpers_1.renderTSNodes)([...includes, classDeclaration, ...views]));
}
if (enumsMap.size > 0)
fs.write(path.posix.join(destDir, constants_1.ENUMS_DIR, constants_1.ENUMS_FILE + '.ts'), (0, ts_helpers_1.renderTSNodes)([...enumsMap.values()], '\n\n'));
return { entitiesMap, enumsMap };
}
exports.generateEntities = generateEntities;
function createEntityClass(ctx) {
const heritageInfo = createEntityClassHeritage(ctx);
const importInfos = [];
if (heritageInfo.parentEntity) {
importInfos.push(createImportInfo(heritageInfo.parentEntity, ctx.isBaseProjectEntity));
}
const classMembersInfo = createEntityClassMembers(ctx);
if (classMembersInfo.importInfos) {
importInfos.push(...classMembersInfo.importInfos);
}
return {
classDeclaration: ts.createClassDeclaration(undefined, [
ts.createToken(ts.SyntaxKind.ExportKeyword)
], ctx.entity.className, undefined, heritageInfo.heritageClauses, classMembersInfo.classMembers),
importInfos
};
}
exports.createEntityClass = createEntityClass;
function createEntityClassHeritage(ctx) {
const { entity, entitiesMap } = ctx;
if (!entity.parentClassName || !entity.parentPackage) {
return { heritageClauses: [] };
}
if (entity.parentClassName === 'AbstractSearchFolder') { // todo AbstractSearchFolder does not have name (?)
return { heritageClauses: [] };
}
const parentEntityInfo = entitiesMap.get(entity.parentPackage + '.' + entity.parentClassName);
if (!parentEntityInfo) {
return { heritageClauses: [] };
}
return {
heritageClauses: [ts.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [
ts.createExpressionWithTypeArguments([], ts.createIdentifier(entity.parentClassName))
])],
parentEntity: parentEntityInfo
};
}
function createEntityClassMembers(ctx) {
const { entity } = ctx;
const basicClassMembers = entity.name != null
? [
ts.createProperty(undefined, [ts.createToken(ts.SyntaxKind.StaticKeyword)], 'NAME', undefined, undefined, ts.createLiteral(entity.name))
]
: [];
if (!entity.attributes) {
return { classMembers: basicClassMembers, importInfos: [] };
}
const importInfos = [];
const allClassMembers = [...basicClassMembers, ...entity.attributes
.filter(entityAttr => {
if (entity.idAttributeName == null || entity.idAttributeName === 'id' || entityAttr.name === entity.idAttributeName) {
return true;
}
else {
// An edge case when we have a non-ID string attribute named "id", and a differently named ID attribute.
// We don't include the former to the TS class, so that we don't end up with two properties named "id".
return entityAttr.name !== 'id';
}
})
.map(entityAttr => {
var _a;
const attributeTypeInfo = createAttributeType(entityAttr, ctx);
if (attributeTypeInfo.importInfo) {
importInfos.push(attributeTypeInfo.importInfo);
}
const idAttrName = (_a = ctx.entity.idAttributeName) !== null && _a !== void 0 ? _a : 'id';
const typeNode = isIdAttr(entityAttr.name, idAttrName)
? getIdTypeNode(entityAttr.mappingType)
: createUnionWithNull(attributeTypeInfo.node);
// REST API puts the id into the property "id" regardless of the actual attribute name.
const attrName = entityAttr.name === idAttrName ? 'id' : entityAttr.name;
return ts.createProperty(undefined, undefined, attrName, ts.createToken(ts.SyntaxKind.QuestionToken), typeNode, undefined);
})];
return { classMembers: allClassMembers, importInfos };
}
function createUnionWithNull(node) {
return ts.factory.createUnionTypeNode([
node,
ts.factory.createLiteralTypeNode(ts.factory.createToken(ts.SyntaxKind.NullKeyword))
]);
}
/**
* TS attribute type - could be primitive, enum or entity class (single or array, depends on relation cardinality)
*
* @param entityAttr attribute which type should be computed
* @param ctx context of attribute owner class
*/
function createAttributeType(entityAttr, ctx) {
let node;
let refEntity;
let enumDeclaration;
const { mappingType } = entityAttr;
// primitive
if (mappingType === 'DATATYPE') {
switch (entityAttr.type.fqn) {
case 'java.lang.Boolean':
node = ts.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
break;
case 'java.lang.Integer':
node = ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
break;
case 'java.lang.String':
node = ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
break;
default:
node = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
break;
}
}
//objects
if (mappingType === 'ASSOCIATION' || mappingType === 'COMPOSITION') {
refEntity = ctx.entitiesMap.get(entityAttr.type.fqn);
if (refEntity) {
switch (entityAttr.cardinality) {
case 'MANY_TO_MANY':
case 'ONE_TO_MANY':
node = ts.createArrayTypeNode(ts.createTypeReferenceNode(entityAttr.type.className, undefined));
break;
case 'ONE_TO_ONE':
case 'MANY_TO_ONE':
default:
node = ts.createTypeReferenceNode(entityAttr.type.className, undefined);
break;
}
}
}
//enums
if (mappingType == 'ENUM') {
enumDeclaration = ctx.enumsMap.get(entityAttr.type.fqn);
if (enumDeclaration) {
node = ts.createTypeReferenceNode(enumDeclaration.name.text, undefined);
}
}
if (!node) {
node = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
}
let importInfo = undefined;
if (refEntity && refEntity.entity)
importInfo = createImportInfo(refEntity, ctx.isBaseProjectEntity);
if (enumDeclaration)
importInfo = (0, import_utils_1.enumImportInfo)(enumDeclaration, ctx.isBaseProjectEntity ? '../..' : '..');
return {
node,
importInfo
};
}
function createImportInfo(importedEntity, isCurrentEntityBase) {
if (isCurrentEntityBase && importedEntity.isBaseProjectEntity) {
//we don't need BASE prefix if current entity and imported base entity in same base/ directory
return {
importPath: './' + (0, utils_1.getEntityModulePath)(importedEntity.entity),
className: importedEntity.entity.className
};
}
else {
return (0, import_utils_1.entityImportInfo)(importedEntity);
}
}
function isIdAttr(entityAttrName, idAttrName) {
return entityAttrName === idAttrName;
}
function getIdTypeNode(idMappingTime) {
return idMappingTime == "EMBEDDED"
? ts.factory.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword)
: ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
}
//# sourceMappingURL=entities-generation.js.map