UNPKG

@jayree/sfdx-plugin-manifest

Version:

A Salesforce CLI plugin containing commands for creating manifest files from Salesforce orgs or git commits of sfdx projects.

160 lines 8.54 kB
/* * Copyright 2025, jayree * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // https://github.com/forcedotcom/source-tracking/blob/main/src/shared/localComponentSetArray.ts import { ComponentSet, RegistryAccess, DestructiveChangesType, } from '@salesforce/source-deploy-retrieve'; import { Logger } from '@salesforce/core'; import equal from 'fast-deep-equal'; import { getString } from '@salesforce/ts-types'; import { supportsPartialDelete, pathIsInFolder } from '@salesforce/source-tracking/lib/shared/functions.js'; import { isDefined } from '@salesforce/source-tracking/lib/shared/guards.js'; import { parseMetadataXml } from '@salesforce/source-deploy-retrieve/lib/src/utils/index.js'; export const getGroupedFiles = (input, byPackageDir = false) => (byPackageDir ? getSequential(input) : getNonSequential(input)).filter((group) => group.deletes.length || group.adds.length || group.modifies.length); const getSequential = ({ packageDirs, adds, modifies, deletes }) => packageDirs.map((pkgDir) => ({ path: pkgDir.name, adds: adds.filter(pathIsInFolder(pkgDir.name)), modifies: modifies.filter(pathIsInFolder(pkgDir.name)), deletes: deletes.filter(pathIsInFolder(pkgDir.name)), })); const getNonSequential = ({ packageDirs, adds, modifies, deletes }) => [ { adds, modifies, deletes, path: packageDirs.map((dir) => dir.name).join(';'), }, ]; export const getComponentSets = ({ groupings, sourceApiVersion, registry = new RegistryAccess(), resolverForNonDeletes, resolverForDeletes, virtualTreeContainer, }) => { const logger = Logger.childFromRoot('gitDiff:gitComponentSetArray'); return groupings .map((grouping) => { logger.debug(`building componentSet for ${grouping.path} (deletes: ${grouping.deletes.length} adds: ${grouping.adds.length} modifies: ${grouping.modifies.length})`); const componentSet = new ComponentSet(undefined, registry); if (sourceApiVersion) { componentSet.sourceApiVersion = sourceApiVersion; } const filterSourceBehaviorOptionsBetaDeletions = (component) => { const customLabelsType = registry.getTypeByName('customlabels'); if (component.type === customLabelsType) { logger.debug(`remove '${component.xml}' from deletes due to sourceBehaviourOptionsBeta.`); return customLabelsType.strategies?.transformer !== 'decomposedLabels'; } return true; }; grouping.deletes .flatMap((filename) => resolverForDeletes.getComponentsFromPath(filename)) .filter(isDefined) .filter(filterSourceBehaviorOptionsBetaDeletions) .forEach((component) => { // if the component supports partial delete AND there are files that are not deleted, // set the component for deploy, not for delete. if (supportsPartialDelete(component) && component.content && virtualTreeContainer.exists(component.content)) { // all bundle types have a directory name try { resolverForNonDeletes .getComponentsFromPath(component.content) .filter(isDefined) .map((nonDeletedComponent) => componentSet.add(nonDeletedComponent)); } catch { logger.warn(`unable to find component at ${component.content}. That's ok if it was supposed to be deleted`); } } else { componentSet.add(component, DestructiveChangesType.POST); } }); grouping.adds .flatMap((filename) => resolverForNonDeletes.getComponentsFromPath(filename)) .filter(isDefined) .forEach((component) => componentSet.add(component)); grouping.modifies .flat() .filter((filename) => { if (!parseMetadataXml(filename)) { resolverForNonDeletes .getComponentsFromPath(filename) .filter(isDefined) .forEach((component) => componentSet.add(component)); return false; } return true; }) .map((filename) => { const [ref2Component] = resolverForNonDeletes.getComponentsFromPath(filename).filter(isDefined); // git path only conaints files const [ref1Component] = resolverForDeletes.getComponentsFromPath(filename).filter(isDefined); // git path only conaints files return { ref1Component, ref2Component, filename }; }) .filter((comp) => { if (resolverForDeletes.forceIgnoredPaths.has(comp.filename) || resolverForNonDeletes.forceIgnoredPaths.has(comp.filename)) { return false; } return true; }) .filter((comp) => { if (equal(comp.ref1Component.parseXmlSync(comp.filename), comp.ref2Component.parseXmlSync(comp.filename))) { return false; } return true; }) .filter((comp) => { if (comp.ref1Component.type.strictDirectoryName === true || !comp.ref1Component.type.children) { resolverForNonDeletes .getComponentsFromPath(comp.filename) .filter(isDefined) .forEach((component) => componentSet.add(component)); return false; } return true; }) .forEach((comp) => { const getUniqueIdentifier = (component) => `${component.type.name}#${getString(component, component.type.uniqueIdElement)}`; const ref2ChildUniqueIdArray = comp.ref2Component .getChildren() .map((childComponent) => getUniqueIdentifier(childComponent)); const ref1ChildUniqueIdArray = comp.ref1Component .getChildren() .map((childComponent) => getUniqueIdentifier(childComponent)); comp.ref1Component .getChildren() .filter((childComponent) => !ref2ChildUniqueIdArray.includes(getUniqueIdentifier(childComponent))) .map((component) => componentSet.add(component, DestructiveChangesType.POST)); // deleted comp.ref2Component .getChildren() .filter((childComponent) => !ref1ChildUniqueIdArray.includes(getUniqueIdentifier(childComponent))) .map((component) => componentSet.add(component)); // added const childComponentsInRef1AndRef2 = comp.ref1Component .getChildren() .filter((childComponent) => ref2ChildUniqueIdArray.includes(getUniqueIdentifier(childComponent))); // modified? for (const childComponentRef1 of childComponentsInRef1AndRef2) { const [childComponentRef2] = comp.ref2Component .getChildren() .filter((childComponent) => getUniqueIdentifier(childComponentRef1) === getUniqueIdentifier(childComponent)); if (!equal(childComponentRef1.parseXmlSync(), childComponentRef2.parseXmlSync())) { componentSet.add(childComponentRef2); // modified! -> add to added } } }); // there may have been ignored files, but componentSet.add doesn't automatically track them. // We'll manually set the ignored paths from what the resolver has been tracking componentSet.forceIgnoredPaths = new Set([...(componentSet.forceIgnoredPaths ?? [])] .concat(Array.from(resolverForDeletes.forceIgnoredPaths)) .concat(Array.from(resolverForNonDeletes.forceIgnoredPaths))); return componentSet; }) .filter((componentSet) => componentSet.size > 0 || componentSet.forceIgnoredPaths?.size); }; //# sourceMappingURL=gitComponentSetArray.js.map