UNPKG

sfdx-hardis

Version:

Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards

180 lines • 8.24 kB
import c from "chalk"; import { glob } from "glob"; import fs from "fs-extra"; import * as path from "path"; import { XMLParser } from "fast-xml-parser"; import { GLOB_IGNORE_PATTERNS } from "../utils/projectUtils.js"; import { uxLog } from "../utils/index.js"; let ALL_LINKS_CACHE = []; let ALL_OBJECTS_CACHE = []; export class ObjectModelBuilder { mainCustomObject; relatedCustomObjects; allLinks = []; allObjects = []; selectedObjectsNames = new Set(); selectedObjects = []; selectedLinks = []; constructor(mainCustomObject = "all", relatedCustomObjects = []) { this.mainCustomObject = mainCustomObject; if (this.mainCustomObject !== "all") { this.selectedObjectsNames.add(this.mainCustomObject); } this.relatedCustomObjects = relatedCustomObjects; } async buildObjectsMermaidSchema() { await this.buildAllLinks(); await this.buildAllObjects(); await this.selectObjects(); await this.selectLinks(); const mermaidSchema = await this.generateMermaidSchema(); return mermaidSchema; } async generateMermaidSchema() { let mermaidSchema = `graph TD\n`; for (const object of this.selectedObjects) { const objectClass = object.name === this.mainCustomObject ? "mainObject" : !object.name.endsWith("__c") ? "object" : object.name.split("__").length > 2 ? "customObjectManaged" : "customObject"; mermaidSchema += `${object.name}["${object.label}"]:::${objectClass}\n`; if (fs.existsSync(`docs/objects/${object.name}.md`)) { mermaidSchema += `click ${object.name} "/objects/${object.name}/"\n`; } } mermaidSchema += `\n`; let pos = 0; const masterDetailPos = []; const lookupPos = []; for (const link of this.selectedLinks) { if (link.type === "MasterDetail") { mermaidSchema += `${link.from} ==>|${link.field}| ${link.to}\n`; masterDetailPos.push(pos); } else { mermaidSchema += `${link.from} -->|${link.field}| ${link.to}\n`; lookupPos.push(pos); } pos++; } mermaidSchema += `\n`; mermaidSchema += `classDef object fill:#D6E9FF,stroke:#0070D2,stroke-width:3px,rx:12px,ry:12px,shadow:drop,color:#333; classDef customObject fill:#FFF4C2,stroke:#CCAA00,stroke-width:3px,rx:12px,ry:12px,shadow:drop,color:#333; classDef customObjectManaged fill:#FFD8B2,stroke:#CC5500,stroke-width:3px,rx:12px,ry:12px,shadow:drop,color:#333; classDef mainObject fill:#FFB3B3,stroke:#A94442,stroke-width:4px,rx:14px,ry:14px,shadow:drop,color:#333,font-weight:bold; `; if (masterDetailPos.length > 0) { mermaidSchema += "linkStyle " + masterDetailPos.join(",") + " stroke:#4C9F70,stroke-width:4px;\n"; } if (lookupPos.length > 0) { mermaidSchema += "linkStyle " + lookupPos.join(",") + " stroke:#A6A6A6,stroke-width:2px;\n"; } // Use Graph LR if there are too many lines for a nice mermaid display if (mermaidSchema.split("\n").length > 50) { mermaidSchema = mermaidSchema.replace("graph TD", "graph LR"); } return mermaidSchema; } async buildAllLinks() { if (ALL_LINKS_CACHE.length > 0) { this.allLinks = ALL_LINKS_CACHE; return; } // List all object links in the project const findFieldsPattern = `**/objects/**/fields/**.field-meta.xml`; const matchingFieldFiles = (await glob(findFieldsPattern, { cwd: process.cwd(), ignore: GLOB_IGNORE_PATTERNS })).map(file => file.replace(/\\/g, '/')); for (const fieldFile of matchingFieldFiles) { const objectName = fieldFile.substring(fieldFile.indexOf('objects/')).split("/")[1]; if (objectName.endsWith("__dlm") || objectName.endsWith("__dll")) { continue; } const fieldXml = fs.readFileSync(fieldFile, "utf8").toString(); const fieldDetail = new XMLParser().parse(fieldXml); if (fieldDetail?.CustomField?.type === "MasterDetail" || fieldDetail?.CustomField?.type === "Lookup") { const fieldName = path.basename(fieldFile, ".field-meta.xml"); if (fieldDetail?.CustomField?.referenceTo) { const link = { from: objectName, to: fieldDetail.CustomField.referenceTo, field: fieldName, relationshipName: fieldDetail?.CustomField?.relationshipName || fieldDetail?.CustomField?.referenceTo, type: fieldDetail.CustomField.type }; this.allLinks.push(link); } else { uxLog(this, c.yellow(`Warning: ${objectName}.${fieldName} has no referenceTo value so has been ignored.`)); } } } ALL_LINKS_CACHE = [...this.allLinks]; } async buildAllObjects() { if (ALL_OBJECTS_CACHE.length > 0) { this.allObjects = ALL_OBJECTS_CACHE; return; } // Get custom objects info const findObjectsPattern = `**/objects/**/*.object-meta.xml`; const matchingObjectFiles = (await glob(findObjectsPattern, { cwd: process.cwd(), ignore: GLOB_IGNORE_PATTERNS })).map(file => file.replace(/\\/g, '/')); for (const objectFile of matchingObjectFiles) { const objectName = path.basename(objectFile, ".object-meta.xml"); const objectXml = fs.readFileSync(objectFile, "utf8").toString(); const objectDetail = new XMLParser().parse(objectXml); const object = { name: objectName, label: objectDetail?.CustomObject?.label || objectName, description: objectDetail?.CustomObject?.description || '', }; this.allObjects.push(object); } ALL_OBJECTS_CACHE = [...this.allObjects]; } async selectObjects() { for (const link of this.allLinks) { if (this.relatedCustomObjects.includes(link.from) || this.relatedCustomObjects.includes(link.to)) { this.selectedObjectsNames.add(link.from); this.selectedObjectsNames.add(link.to); } else if (this.mainCustomObject === "all") { this.selectedObjectsNames.add(link.from); this.selectedObjectsNames.add(link.to); } else { if (link.from === this.mainCustomObject || link.to === this.mainCustomObject) { this.selectedObjectsNames.add(link.from); this.selectedObjectsNames.add(link.to); } } } for (const object of this.allObjects) { if (this.selectedObjectsNames.has(object.name)) { this.selectedObjects.push(object); } } // Complete with objects with missing .object-meta.xml file for (const object of this.selectedObjectsNames) { if (!this.selectedObjects.some(obj => obj.name === object)) { this.selectedObjects.push({ name: object, label: object, description: '' }); } } } async selectLinks() { if (this.selectedObjectsNames.size > 10) { for (const link of this.allLinks) { if ((link.from === this.mainCustomObject || link.to === this.mainCustomObject) && (this.selectedObjectsNames.has(link.from) && this.selectedObjectsNames.has(link.to))) { this.selectedLinks.push(link); } } } else { for (const link of this.allLinks) { if (this.selectedObjectsNames.has(link.from) && this.selectedObjectsNames.has(link.to)) { this.selectedLinks.push(link); } } } } } //# sourceMappingURL=objectModelBuilder.js.map