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

152 lines (145 loc) • 8.76 kB
/* jscpd:ignore-start */ import { SfCommand, Flags, optionalOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; import c from "chalk"; import * as path from "path"; import fs from "fs-extra"; import { Messages } from '@salesforce/core'; import { WebSocketClient } from '../../../common/websocketClient.js'; import { isCI, uxLog } from '../../../common/utils/index.js'; import { MetadataUtils } from '../../../common/metadata-utils/index.js'; import { generateFlowMarkdownFile, generateHistoryDiffMarkdown, generateMarkdownFileWithMermaid } from '../../../common/utils/mermaidUtils.js'; import { CONSTANTS } from '../../../config/index.js'; import { setConnectionVariables } from '../../../common/utils/orgUtils.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); export default class Flow2Markdown extends SfCommand { static title = 'Flow to Markdown'; static description = ` ## Command Behavior **Generates comprehensive Markdown documentation from a Salesforce Flow metadata file.** This command automates the creation of human-readable documentation for Salesforce Flows, making it easier to understand their logic and behavior. It can process a single Flow file or multiple Flow files, generating a Markdown file for each. Key features include: - **Detailed Flow Description:** Extracts and presents the Flow's structure, elements, and decision logic in a clear, organized Markdown format. - **AI-Powered Summarization (Optional):** If [AI integration](${CONSTANTS.DOC_URL_ROOT}/salesforce-ai-setup/) is configured, the documentation will include an AI-generated summary of the Flow's purpose and functionality. - **Mermaid Diagram Generation:** Integrates with Mermaid to visualize the Flow's structure, providing a graphical representation alongside the textual description. - **History Diff (Optional):** Can generate a Markdown file showing the historical differences of the Flow, useful for tracking changes over time. - **PDF Export (Optional):** Allows for the generation of the documentation in PDF format for easy sharing and archiving. - **Interactive File Selection:** If no input file is specified, the command interactively prompts the user to select Flow files. <details markdown="1"> <summary>Technical explanations</summary> The command leverages several internal utilities and external libraries to achieve its functionality: - **Flow Metadata Parsing:** Reads and parses the XML content of Salesforce Flow metadata files (.flow-meta.xml). - **Markdown Generation:** Utilizes exttt{generateFlowMarkdownFile} to transform the parsed Flow data into a structured Markdown format. - **Mermaid Integration:** Employs exttt{generateMarkdownFileWithMermaid} to embed Mermaid diagrams within the Markdown output, which are then rendered by compatible Markdown viewers. - **AI Integration:** If enabled, it interacts with an AI service (via exttt{describeWithAi} option) to generate a high-level summary of the Flow. - **Git History Analysis:** For the \ --with-history\ flag, it uses exttt{generateHistoryDiffMarkdown} to analyze Git history and present changes to the Flow. - **File System Operations:** Uses exttt{fs-extra} for file system operations like reading input files, creating output directories (e.g., exttt{docs/flows/}), and writing Markdown and PDF files. - **Salesforce CLI Integration:** Uses exttt{@salesforce/sf-plugins-core} for command-line parsing and exttt{setConnectionVariables} for Salesforce organization context. - **WebSocket Communication:** Interacts with a WebSocket client ( exttt{WebSocketClient.requestOpenFile}) to open the generated Markdown file in a VS Code tab, enhancing user experience. </details> `; static examples = [ '$ sf hardis:doc:flow2markdown', '$ sf hardis:doc:flow2markdown --inputfile force-app/main/default/flows/MyFlow.flow-meta.xml', '$ sf hardis:doc:flow2markdown --pdf', '$ sf hardis:doc:flow2markdown --inputfile force-app/main/default/flows/MyFlow.flow-meta.xml --pdf', ]; static flags = { inputfile: Flags.string({ char: 'x', description: 'Path to Flow metadata file. If not specified, the command will prompt the user', }), outputfile: Flags.string({ char: 'f', description: 'Force the path and name of output markdown file. Must end with .md', }), "with-history": Flags.boolean({ default: false, description: "Generate a markdown file with the history diff of the Flow", }), pdf: Flags.boolean({ description: 'Also generate the documentation in PDF format', }), debug: Flags.boolean({ char: 'd', default: false, description: messages.getMessage('debugMode'), }), websocket: Flags.string({ description: messages.getMessage('websocket'), }), skipauth: Flags.boolean({ description: 'Skip authentication check when a default username is required', }), "target-org": optionalOrgFlagWithDeprecations }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default static requiresProject = false; withHistory = false; withPdf = false; singleFileMode = false; inputFiles; outputFile; debugMode = false; /* jscpd:ignore-end */ async run() { const { flags } = await this.parse(Flow2Markdown); const outputFiles = []; this.inputFiles = flags.inputfile ? [flags.inputfile] : null; this.outputFile = flags.outputfile || null; this.withHistory = flags["with-history"] === true ? true : false; this.withPdf = flags.pdf === true ? true : false; this.singleFileMode = this.inputFiles != null && this.inputFiles.length == 1; this.debugMode = flags.debug || false; await setConnectionVariables(flags['target-org']?.getConnection(), true); // Required for some notifications providers like Email, or for Agentforce if (this.inputFiles === null && !isCI) { this.inputFiles = await MetadataUtils.promptMultipleFlows(); } if (this.singleFileMode && !this.outputFile) { await fs.ensureDir(path.join("docs", "flows")); this.outputFile = path.join("docs", "flows", path.basename(this.inputFiles[0]).replace(".flow-meta.xml", ".md")); } for (const inputFile of this.inputFiles) { let outputFile = this.outputFile; if (!this.singleFileMode) { await fs.ensureDir(path.join("docs", "flows")); outputFile = path.join("docs", "flows", path.basename(inputFile).replace(".flow-meta.xml", ".md")); } const flowName = path.basename(inputFile, ".flow-meta.xml"); uxLog("log", this, c.grey(`Generating Markdown for Flow ${inputFile}.`)); const flowXml = (await fs.readFile(inputFile, "utf8")).toString(); const genRes = await generateFlowMarkdownFile(flowName, flowXml, outputFile, { collapsedDetails: false, describeWithAi: true, flowDependencies: {} }); if (!genRes) { throw new Error("Error generating markdown file"); } if (this.debugMode) { await fs.copyFile(outputFile, outputFile.replace(".md", ".mermaid.md")); } const gen2res = await generateMarkdownFileWithMermaid(outputFile, outputFile, null, this.withPdf); if (!gen2res) { throw new Error("Error generating mermaid markdown file"); } if (this.withHistory) { try { await generateHistoryDiffMarkdown(inputFile, this.debugMode); } catch (e) { uxLog("warning", this, c.yellow(`Error generating history-diff Markdown: ${e.message}.`)); } } // Open file in a new VS Code tab if available WebSocketClient.requestOpenFile(outputFile); WebSocketClient.sendReportFileMessage(outputFile, path.basename(outputFile).replace(".md", "") + " Documentation", 'report'); outputFiles.push(outputFile); } uxLog("action", this, c.green(`Markdown documentation generated for ${this.inputFiles.length} Flow(s).`)); for (const outputFile of outputFiles) { uxLog("log", this, c.grey(`- ${outputFile}`)); } // Return an object to be displayed with --json return { outputFiles: outputFiles }; } } //# sourceMappingURL=flow2markdown.js.map