@@ -17,6 +17,7 @@ import {
1717 SfProject ,
1818} from '@salesforce/core' ;
1919import { isString } from '@salesforce/ts-types' ;
20+ import { objectHasSomeRealValues } from '../utils/decomposed' ;
2021import { MetadataApiDeploy , MetadataApiDeployOptions } from '../client/metadataApiDeploy' ;
2122import { MetadataApiRetrieve } from '../client/metadataApiRetrieve' ;
2223import type { MetadataApiRetrieveOptions } from '../client/types' ;
@@ -406,74 +407,60 @@ export class ComponentSet extends LazyCollection<MetadataComponent> {
406407 * @returns Object representation of a package manifest
407408 */
408409 public async getObject ( destructiveType ?: DestructiveChangesType ) : Promise < PackageManifestObject > {
409- const version = await this . getApiVersion ( ) ;
410-
411410 // If this ComponentSet has components marked for delete, we need to
412411 // only include those components in a destructiveChanges.xml and
413412 // all other components in the regular manifest.
414- let components = this . components ;
415- if ( this . getTypesOfDestructiveChanges ( ) . length ) {
416- components = destructiveType ? this . destructiveComponents [ destructiveType ] : this . manifestComponents ;
417- }
413+ const components = this . getTypesOfDestructiveChanges ( ) . length
414+ ? destructiveType
415+ ? this . destructiveComponents [ destructiveType ]
416+ : this . manifestComponents
417+ : this . components ;
418418
419- const typeMap = new Map < string , string [ ] > ( ) ;
419+ const typeMap = new Map < string , Set < string > > ( ) ;
420420
421- const addToTypeMap = ( type : MetadataType , fullName : string ) : void => {
422- if ( type . isAddressable !== false ) {
423- const typeName = type . name ;
424- if ( ! typeMap . has ( typeName ) ) {
425- typeMap . set ( typeName , [ ] ) ;
426- }
427- const typeEntry = typeMap . get ( typeName ) ;
428- if ( fullName === ComponentSet . WILDCARD && ! type . supportsWildcardAndName && ! destructiveType ) {
429- // if the type doesn't support mixed wildcards and specific names, overwrite the names to be a wildcard
430- typeMap . set ( typeName , [ fullName ] ) ;
431- } else if (
432- typeEntry &&
433- ! typeEntry . includes ( fullName ) &&
434- ( ! typeEntry . includes ( ComponentSet . WILDCARD ) || type . supportsWildcardAndName )
435- ) {
436- // if the type supports both wildcards and names, add them regardless
437- typeMap . get ( typeName ) ?. push ( fullName ) ;
438- }
439- }
440- } ;
441-
442- for ( const key of components . keys ( ) ) {
421+ [ ...components . entries ( ) ] . map ( ( [ key , cmpMap ] ) => {
443422 const [ typeId , fullName ] = splitOnFirstDelimiter ( key ) ;
444- let type = this . registry . getTypeByName ( typeId ) ;
445-
446- if ( type . folderContentType ) {
447- type = this . registry . getTypeByName ( type . folderContentType ) ;
448- }
449- addToTypeMap (
450- type ,
451- // they're reassembled like CustomLabels.MyLabel
452- this . registry . getParentType ( type . name ) ?. strategies ?. recomposition === 'startEmpty' && fullName . includes ( '.' )
453- ? fullName . split ( '.' ) [ 1 ]
454- : fullName
455- ) ;
423+ const type = this . registry . getTypeByName ( typeId ) ;
456424
457425 // Add children
458- const componentMap = components . get ( key ) ;
459- if ( componentMap ) {
460- for ( const comp of componentMap . values ( ) ) {
461- for ( const child of comp . getChildren ( ) ) {
462- addToTypeMap ( child . type , child . fullName ) ;
463- }
426+ [ ...( cmpMap ?. values ( ) ?? [ ] ) ]
427+ . flatMap ( ( c ) => c . getChildren ( ) )
428+ . map ( ( child ) => addToTypeMap ( { typeMap, type : child . type , fullName : child . fullName , destructiveType } ) ) ;
429+
430+ // logic: if this is a decomposed type, skip its inclusion in the manifest if the parent is "empty"
431+ if (
432+ type . strategies ?. transformer === 'decomposed' &&
433+ // exclude (ex: CustomObjectTranslation) where there are no addressable children
434+ Object . values ( type . children ?. types ?? { } ) . some ( ( t ) => t . unaddressableWithoutParent !== true ) &&
435+ Object . values ( type . children ?. types ?? { } ) . some ( ( t ) => t . isAddressable !== false )
436+ ) {
437+ const parentComp = [ ...( cmpMap ?. values ( ) ?? [ ] ) ] . find ( ( c ) => c . fullName === fullName ) ;
438+ if ( parentComp ?. xml && ! objectHasSomeRealValues ( type ) ( parentComp . parseXmlSync ( ) ) ) {
439+ return ;
464440 }
465441 }
466- }
442+
443+ addToTypeMap ( {
444+ typeMap,
445+ type : type . folderContentType ? this . registry . getTypeByName ( type . folderContentType ) : type ,
446+ fullName :
447+ this . registry . getParentType ( type . name ) ?. strategies ?. recomposition === 'startEmpty' && fullName . includes ( '.' )
448+ ? // they're reassembled like CustomLabels.MyLabel
449+ fullName . split ( '.' ) [ 1 ]
450+ : fullName ,
451+ destructiveType,
452+ } ) ;
453+ } ) ;
467454
468455 const typeMembers = Array . from ( typeMap . entries ( ) )
469- . map ( ( [ typeName , members ] ) => ( { members : members . sort ( ) , name : typeName } ) )
456+ . map ( ( [ typeName , members ] ) => ( { members : [ ... members ] . sort ( ) , name : typeName } ) )
470457 . sort ( ( a , b ) => ( a . name > b . name ? 1 : - 1 ) ) ;
471458
472459 return {
473460 Package : {
474461 ...{
475462 types : typeMembers ,
476- version,
463+ version : await this . getApiVersion ( ) ,
477464 } ,
478465 ...( this . fullName ? { fullName : this . fullName } : { } ) ,
479466 } ,
@@ -750,3 +737,28 @@ const splitOnFirstDelimiter = (input: string): [string, string] => {
750737 const indexOfSplitChar = input . indexOf ( KEY_DELIMITER ) ;
751738 return [ input . substring ( 0 , indexOfSplitChar ) , input . substring ( indexOfSplitChar + 1 ) ] ;
752739} ;
740+
741+ /** side effect: mutates the typeMap property */
742+ const addToTypeMap = ( {
743+ typeMap,
744+ type,
745+ fullName,
746+ destructiveType,
747+ } : {
748+ typeMap : Map < string , Set < string > > ;
749+ type : MetadataType ;
750+ fullName : string ;
751+ destructiveType ?: DestructiveChangesType ;
752+ } ) : void => {
753+ if ( type . isAddressable === false ) return ;
754+ if ( fullName === ComponentSet . WILDCARD && ! type . supportsWildcardAndName && ! destructiveType ) {
755+ // if the type doesn't support mixed wildcards and specific names, overwrite the names to be a wildcard
756+ typeMap . set ( type . name , new Set ( [ fullName ] ) ) ;
757+ return ;
758+ }
759+ const existing = typeMap . get ( type . name ) ?? new Set < string > ( ) ;
760+ if ( ! existing . has ( ComponentSet . WILDCARD ) || type . supportsWildcardAndName ) {
761+ // if the type supports both wildcards and names, add them regardless
762+ typeMap . set ( type . name , existing . add ( fullName ) ) ;
763+ }
764+ } ;
0 commit comments