UNPKG

sf-decomposer

Version:

Decompose Salesforce metadata into granular, VCS-friendly files; recompose for deployment.

67 lines 3.94 kB
'use strict'; import { basename, dirname } from 'node:path'; import { resolveDecomposeOptionsForType } from '../helpers/configOverrides.js'; import { CONCURRENCY_LIMITS } from '../helpers/constants.js'; import { pLimit } from '../helpers/pLimit.js'; import { getRegistryValuesBySuffix } from '../metadata/getRegistryValuesBySuffix.js'; import { resolveEffectiveMetadataTypes } from '../metadata/parseManifest.js'; import { updateForceignoreFile } from '../service/core/updateForceignore.js'; import { updateGitattributesFile } from '../service/core/updateGitattributes.js'; import { decomposeFileHandler } from '../service/decompose/decomposeFileHandler.js'; export async function decomposeMetadataTypes(options) { const { metadataTypes, prepurge, postpurge, format, ignoreDirs, strategy, decomposeNestedPerms, manifest, overrides, updateForceignore, updateGitattributes, log, repoRoot, } = options; const { manifestFilter, effectiveTypes } = await resolveEffectiveMetadataTypes(metadataTypes, manifest, ignoreDirs, repoRoot, log); if (effectiveTypes.length === 0) { log('No metadata types to decompose after applying the manifest filter.'); return { metadata: [] }; } // Limit concurrent metadata type processing to prevent file system overload const limit = pLimit(CONCURRENCY_LIMITS.METADATA_TYPES); const processed = []; const processedMeta = []; let effectiveRepoRoot; const tasks = effectiveTypes.map((metadataType) => limit(async () => { const manifestXmlPaths = manifestFilter?.parentXmlsBySuffix.get(metadataType); // Type-scope resolved options serve as the base for component-scope resolution further // down the call stack. Hard strategy rules (labels / loyaltyProgramSetup) are applied per // file inside the disassembler so they remain in force even when a component-scope override // tries to flip the strategy. const typeResolved = resolveDecomposeOptionsForType(metadataType, { format, strategy, decomposeNestedPerms, prepurge, postpurge }, overrides); let metaAttributes; let ignorePath; try { ({ metaAttributes, ignorePath } = await getRegistryValuesBySuffix(metadataType, 'decompose', ignoreDirs, repoRoot, typeResolved.uniqueIdElements)); } catch (err) { /* istanbul ignore if -- @preserve: preserves non-manifest behavior; unreachable via known CLI types */ if (!manifestFilter) throw err; /* istanbul ignore next -- @preserve: getRegistryValuesBySuffix always throws Error instances */ const message = err instanceof Error ? err.message : String(err); log(`Skipping ${metadataType}: ${message}`); return; } await decomposeFileHandler(metaAttributes, typeResolved, ignorePath, overrides, manifestXmlPaths); processed.push(metadataType); if (updateForceignore || updateGitattributes) { processedMeta.push({ directoryName: basename(metaAttributes.metadataPaths[0]), metaSuffix: metaAttributes.metaSuffix, format: typeResolved.format, }); effectiveRepoRoot ??= dirname(ignorePath); } log(`All metadata files have been decomposed for the metadata type: ${metadataType}`); })); await Promise.all(tasks); if (updateForceignore && processedMeta.length > 0 && effectiveRepoRoot) { await updateForceignoreFile(processedMeta, effectiveRepoRoot); log('Updated .forceignore with decomposed file paths.'); } if (updateGitattributes && processedMeta.length > 0 && effectiveRepoRoot) { await updateGitattributesFile(processedMeta, effectiveRepoRoot); log('Updated .gitattributes with root metadata file patterns.'); } return { metadata: processed }; } //# sourceMappingURL=decomposeMetadataTypes.js.map