UNPKG

mxdocgen

Version:

A small tool that mimics the documentation generation capabilities offered in Mendix Studio Pro, but with greater flexibility. It uses the Mendix Model SDK to extract information from a Mendix model, which is then fed into a set of templates to generate

207 lines (186 loc) 6.52 kB
import { domainmodels, IModel, microflows, projects } from "mendixmodelsdk"; import uuid from "uuid"; import { humanReadableDataType } from "../sdk/datatypes"; import { documentLowerTypeName, documentTypeName, isMicroflow, typeName } from "../sdk/documenttypes"; import { Processor } from "./processor"; import { DefaultAssociationTemplateData, DefaultAttributeTemplateData, DefaultDomainModelTemplateData, DefaultEntityTemplateData, DefaultMicroflowTemplateData, DefaultTemplateData } from "./defaulttemplatedata"; import { humanReadableAttributeType } from "../sdk/attributetypes"; import { multiplicity } from "../sdk/associations"; import IModule = projects.IModule; import MicroflowParameterObject = microflows.MicroflowParameterObject; import IFolderBase = projects.IFolderBase; import IMicroflow = microflows.IMicroflow; import Microflow = microflows.Microflow; import IDocument = projects.IDocument; import IDomainModel = domainmodels.IDomainModel; import IEntity = domainmodels.IEntity; import IAttribute = domainmodels.IAttribute; import IAssociation = domainmodels.IAssociation; import StoredValue = domainmodels.StoredValue; import Generalization = domainmodels.Generalization; import AssociationOwner = domainmodels.AssociationOwner; export class DefaultProcessor implements Processor<DefaultTemplateData> { constructor( private moduleFilter?: (module: IModule) => boolean, private documentFilter?: (document: IDocument) => boolean ) {} async process(model: IModel): Promise<DefaultTemplateData> { return { Name: "Documentation", Modules: await Promise.all( Array.from(this.modules(model)) .sort((a, b) => a.name.localeCompare(b.name)) .map(module => this.processModule(module)) ) }; } protected async processModule(module: IModule) { const documents = Array.from(this.documents(module)); return { ID: uuid(), Name: module.name, DomainModel: await this.processDomainModel(module.domainModel), HasMicroflows: documents.find(isMicroflow) !== undefined, Microflows: { ID: uuid(), TypeName: typeName(Microflow), Microflows: await Promise.all( documents .filter(isMicroflow) .sort((a, b) => a.name.localeCompare(b.name)) .map(microflow => this.processMicroflow(microflow)) ) } }; } protected async processDomainModel( domainModel: IDomainModel ): Promise<DefaultDomainModelTemplateData> { const loadedDomainModel = await domainModel.load(); const entities = loadedDomainModel.entities .slice() .sort((a, b) => a.name.localeCompare(b.name)); return { ID: loadedDomainModel.id, Documentation: loadedDomainModel.documentation, HasEntities: entities.length > 0, Entities: await Promise.all( entities.map(entity => this.processEntity(entity, loadedDomainModel)) ) }; } protected async processEntity( entity: IEntity, domainModel: IDomainModel ): Promise<DefaultEntityTemplateData> { const loadedEntity = await entity.load(); const attributes = loadedEntity.attributes .slice() .sort((a, b) => a.name.localeCompare(b.name)); const associations = domainModel.associations .filter( association => association.parent === entity || (association.child === entity && association.owner === AssociationOwner.Both) ) .sort((a, b) => a.name.localeCompare(b.name)); return { ID: loadedEntity.id, Name: loadedEntity.name, Documentation: loadedEntity.documentation, Generalization: loadedEntity.generalization instanceof Generalization ? loadedEntity.generalization.generalizationQualifiedName : undefined, HasAttributes: attributes.length > 0, Attributes: await Promise.all( attributes.map(attribute => this.processAttribute(attribute)) ), HasAssociations: associations.length > 0, Associations: await Promise.all( associations.map(association => this.processAssociation(association, loadedEntity)) ) }; } protected async processAttribute(attribute: IAttribute): Promise<DefaultAttributeTemplateData> { const loadedAttribute = await attribute.load(); return { Name: loadedAttribute.name, Documentation: loadedAttribute.documentation, Type: humanReadableAttributeType(loadedAttribute.type), DefaultValue: (loadedAttribute.value as StoredValue)?.defaultValue }; } protected async processAssociation( association: IAssociation, entity: IEntity ): Promise<DefaultAssociationTemplateData> { const loadedAssociation = await association.load(); return { Name: loadedAssociation.name, Documentation: loadedAssociation.documentation, Multiplicity: multiplicity(loadedAssociation), OtherSide: loadedAssociation.parent === entity ? loadedAssociation.child.qualifiedName! : loadedAssociation.parent.qualifiedName! }; } protected async processMicroflow(microflow: IMicroflow): Promise<DefaultMicroflowTemplateData> { const loadedMicroflow = await microflow.load(); const microflowParameterObjects = loadedMicroflow.objectCollection.objects .filter( microflowObject => microflowObject.structureTypeName === MicroflowParameterObject.structureTypeName ) .map(microflowObject => microflowObject as MicroflowParameterObject) .sort((a, b) => a.name.localeCompare(b.name)); return { ID: uuid(), Name: microflow.name, Documentation: loadedMicroflow.documentation, TypeName: documentTypeName(loadedMicroflow), LowerTypeName: documentLowerTypeName(loadedMicroflow), HasParameters: microflowParameterObjects.length > 0, Parameters: microflowParameterObjects.map(microflowParameter => ({ Name: microflowParameter.name, Type: humanReadableDataType(microflowParameter.variableType), Documentation: microflowParameter.documentation })), ReturnType: humanReadableDataType(microflow.microflowReturnType), "Return type": humanReadableDataType(microflow.microflowReturnType) }; } protected *modules(model: IModel): Iterable<IModule> { for (let module of model.allModules()) { if (!this.moduleFilter || this.moduleFilter(module)) { yield module; } } } protected *documents(module: IModule): Iterable<IDocument> { for (let document of this.listDocuments(module)) { if (!this.documentFilter || this.documentFilter(document)) { yield document; } } } protected listDocuments(folderBase: IFolderBase): Array<IDocument> { return [ ...folderBase.documents, ...folderBase.folders.flatMap(folder => this.listDocuments(folder)) ]; } }