UNPKG

sf-decomposer

Version:

Split large Salesforce metadata files into version-control-friendly pieces and rebuild deployment-ready files.

115 lines 5.39 kB
'use strict'; import { readdir, stat } from 'node:fs/promises'; import { join, dirname, basename } from 'node:path'; import { ReassembleXMLFileHandler } from 'config-disassembler'; import { CONCURRENCY_LIMITS } from '../../helpers/constants.js'; import { pLimit } from '../../helpers/pLimit.js'; import { reassembleLabels } from './reassembleLabels.js'; import { renameBotVersionFile } from './renameBotVersionFiles.js'; export async function recomposeFileHandler(metaAttributes, postpurge, manifestXmlPaths) { const { metaSuffix, strictDirectoryName, folderType, metadataPaths } = metaAttributes; if (manifestXmlPaths && manifestXmlPaths.size > 0) { await recomposeFromManifest(manifestXmlPaths, metaSuffix, strictDirectoryName, folderType, postpurge); return; } // Limit concurrent package directory processing const limit = pLimit(CONCURRENCY_LIMITS.PACKAGE_DIRS); const tasks = metadataPaths.map((metadataPath) => limit(async () => { if (metaSuffix === 'labels') { await reassembleLabels(metadataPath, metaSuffix, postpurge); } else { let recurse = false; if (strictDirectoryName || folderType) recurse = true; await reassembleDirectories(metadataPath, metaSuffix, recurse, postpurge); } if (metaSuffix === 'bot') await renameBotVersionFile(metadataPath); })); await Promise.all(tasks); } async function recomposeFromManifest(manifestXmlPaths, metaSuffix, strictDirectoryName, folderType, postpurge) { const limit = pLimit(CONCURRENCY_LIMITS.PACKAGE_DIRS); const xmlPaths = Array.from(manifestXmlPaths); if (metaSuffix === 'labels') { const labelDirs = new Set(xmlPaths.map((xml) => dirname(xml))); const tasks = Array.from(labelDirs).map((labelDir) => limit(() => reassembleLabels(labelDir, metaSuffix, postpurge))); await Promise.all(tasks); return; } if (strictDirectoryName || folderType) { // For strict types (e.g., bot), each parent xml lives in its own directory. // Dedupe by that parent directory and reassemble each decomposed child subdir. const parentDirs = new Set(xmlPaths.map((xml) => dirname(xml))); const tasks = Array.from(parentDirs).map((parentDir) => limit(() => reassembleDirectories(parentDir, metaSuffix, false, postpurge))); await Promise.all(tasks); /* istanbul ignore else -- @preserve: bot is the only strict-directory type that needs post-processing in tests. Stryker disable next-line ConditionalExpression */ if (metaSuffix === 'bot') { // renameBotVersionFile expects the parent metadata directory (e.g. .../bots), // not the individual bot directory. Walk up one level and dedupe. const botContainerDirs = new Set(Array.from(parentDirs).map((parentDir) => dirname(parentDir))); for (const botContainerDir of botContainerDirs) { // eslint-disable-next-line no-await-in-loop await renameBotVersionFile(botContainerDir); } } return; } const tasks = xmlPaths.map((xmlPath) => limit(async () => { const decomposedDir = decomposedDirForXml(xmlPath, metaSuffix); // Skip files that were never decomposed (e.g. metadata consisting only of leaf elements). if (!(await directoryExists(decomposedDir))) return; reassembleHandler(decomposedDir, `${metaSuffix}-meta.xml`, postpurge); })); await Promise.all(tasks); } function decomposedDirForXml(xmlPath, metaSuffix) { const metaEnding = `.${metaSuffix}-meta.xml`; const fileName = basename(xmlPath); const stem = fileName.slice(0, -metaEnding.length); return join(dirname(xmlPath), stem); } async function directoryExists(path) { try { const stats = await stat(path); return stats.isDirectory(); } catch { // Stryker disable next-line BlockStatement return false; } } export function reassembleHandler(filePath, fileExtension, postPurge) { const handler = new ReassembleXMLFileHandler(); handler.reassemble({ filePath, fileExtension, postPurge, }); } async function reassembleDirectories(metadataPath, metaSuffix, recurse, postpurge) { const subdirectories = (await readdir(metadataPath)).map((file) => join(metadataPath, file)); // Limit concurrent stat operations const statLimit = pLimit(CONCURRENCY_LIMITS.FILE_OPERATIONS); const dirStats = await Promise.all(subdirectories.map((subdirectory) => statLimit(async () => ({ subdirectory, isDirectory: (await stat(subdirectory)).isDirectory(), })))); // Limit concurrent subdirectory processing const processLimit = pLimit(CONCURRENCY_LIMITS.SUBDIRECTORIES); const tasks = dirStats .filter(({ isDirectory }) => isDirectory) .map(({ subdirectory }) => processLimit(async () => { if (recurse) { // recursively call this function and set recurse to false await reassembleDirectories(subdirectory, metaSuffix, false, postpurge); } else { reassembleHandler(subdirectory, `${metaSuffix}-meta.xml`, postpurge); } })); await Promise.all(tasks); } //# sourceMappingURL=recomposeFileHandler.js.map