@@ -29,8 +29,18 @@ export type ManifestOption = {
29
29
} ;
30
30
31
31
type MetadataOption = {
32
+ /**
33
+ * Array of metadata type:name pairs to include in the ComponentSet.
34
+ */
32
35
metadataEntries : string [ ] ;
36
+ /**
37
+ * Array of filesystem directory paths to search for local metadata to include in the ComponentSet.
38
+ */
33
39
directoryPaths : string [ ] ;
40
+ /**
41
+ * Array of metadata type:name pairs to exclude from the ComponentSet.
42
+ */
43
+ excludedEntries ?: string [ ] ;
34
44
/**
35
45
* Array of metadata type:name pairs to delete before the deploy. Use of wildcards is not allowed.
36
46
*/
@@ -60,6 +70,14 @@ export type ComponentSetOptions = {
60
70
61
71
type MetadataMap = Map < string , string [ ] > ;
62
72
73
+ let logger : Logger ;
74
+ const getLogger = ( ) : Logger => {
75
+ if ( ! logger ) {
76
+ logger = Logger . childFromRoot ( 'ComponentSetBuilder' ) ;
77
+ }
78
+ return logger ;
79
+ } ;
80
+
63
81
export class ComponentSetBuilder {
64
82
/**
65
83
* Builds a ComponentSet that can be used for source conversion,
@@ -71,14 +89,13 @@ export class ComponentSetBuilder {
71
89
*/
72
90
73
91
public static async build ( options : ComponentSetOptions ) : Promise < ComponentSet > {
74
- const logger = Logger . childFromRoot ( 'componentSetBuilder' ) ;
75
92
let componentSet : ComponentSet | undefined ;
76
93
77
94
const { sourcepath, manifest, metadata, packagenames, org } = options ;
78
95
const registry = new RegistryAccess ( undefined , options . projectDir ) ;
79
96
80
97
if ( sourcepath ) {
81
- logger . debug ( `Building ComponentSet from sourcepath: ${ sourcepath . join ( ', ' ) } ` ) ;
98
+ getLogger ( ) . debug ( `Building ComponentSet from sourcepath: ${ sourcepath . join ( ', ' ) } ` ) ;
82
99
const fsPaths = sourcepath . map ( validateAndResolvePath ) ;
83
100
componentSet = ComponentSet . fromSource ( {
84
101
fsPaths,
@@ -88,16 +105,16 @@ export class ComponentSetBuilder {
88
105
89
106
// Return empty ComponentSet and use packageNames in the connection via `.retrieve` options
90
107
if ( packagenames ) {
91
- logger . debug ( `Building ComponentSet for packagenames: ${ packagenames . toString ( ) } ` ) ;
108
+ getLogger ( ) . debug ( `Building ComponentSet for packagenames: ${ packagenames . toString ( ) } ` ) ;
92
109
componentSet ??= new ComponentSet ( undefined , registry ) ;
93
110
}
94
111
95
112
// Resolve manifest with source in package directories.
96
113
if ( manifest ) {
97
- logger . debug ( `Building ComponentSet from manifest: ${ manifest . manifestPath } ` ) ;
114
+ getLogger ( ) . debug ( `Building ComponentSet from manifest: ${ manifest . manifestPath } ` ) ;
98
115
assertFileExists ( manifest . manifestPath ) ;
99
116
100
- logger . debug ( `Searching in packageDir: ${ manifest . directoryPaths . join ( ', ' ) } for matching metadata` ) ;
117
+ getLogger ( ) . debug ( `Searching in packageDir: ${ manifest . directoryPaths . join ( ', ' ) } for matching metadata` ) ;
101
118
componentSet = await ComponentSet . fromManifest ( {
102
119
manifestPath : manifest . manifestPath ,
103
120
resolveSourcePaths : manifest . directoryPaths ,
@@ -108,9 +125,10 @@ export class ComponentSetBuilder {
108
125
} ) ;
109
126
}
110
127
111
- // Resolve metadata entries with source in package directories.
112
- if ( metadata ) {
113
- logger . debug ( `Building ComponentSet from metadata: ${ metadata . metadataEntries . toString ( ) } ` ) ;
128
+ // Resolve metadata entries with source in package directories, unless we are building a ComponentSet
129
+ // from metadata in an org.
130
+ if ( metadata && ! org ) {
131
+ getLogger ( ) . debug ( `Building ComponentSet from metadata: ${ metadata . metadataEntries . toString ( ) } ` ) ;
114
132
const directoryPaths = metadata . directoryPaths ;
115
133
componentSet ??= new ComponentSet ( undefined , registry ) ;
116
134
const componentSetFilter = new ComponentSet ( undefined , registry ) ;
@@ -122,7 +140,7 @@ export class ComponentSetBuilder {
122
140
. map ( addToComponentSet ( componentSet ) )
123
141
. map ( addToComponentSet ( componentSetFilter ) ) ;
124
142
125
- logger . debug ( `Searching for matching metadata in directories: ${ directoryPaths . join ( ', ' ) } ` ) ;
143
+ getLogger ( ) . debug ( `Searching for matching metadata in directories: ${ directoryPaths . join ( ', ' ) } ` ) ;
126
144
127
145
// add destructive changes if defined. Because these are deletes, all entries
128
146
// are resolved to SourceComponents
@@ -170,25 +188,8 @@ export class ComponentSetBuilder {
170
188
// Resolve metadata entries with an org connection
171
189
if ( org ) {
172
190
componentSet ??= new ComponentSet ( undefined , registry ) ;
173
-
174
- logger . debug (
175
- `Building ComponentSet from targetUsername: ${ org . username } ${
176
- metadata ? `filtered by metadata: ${ metadata . metadataEntries . toString ( ) } ` : ''
177
- } `
178
- ) ;
179
-
180
- const mdMap = metadata
181
- ? buildMapFromComponents ( metadata . metadataEntries . map ( entryToTypeAndName ( registry ) ) )
182
- : ( new Map ( ) as MetadataMap ) ;
183
-
184
- const fromConnection = await ComponentSet . fromConnection ( {
185
- usernameOrConnection : ( await StateAggregator . getInstance ( ) ) . aliases . getUsername ( org . username ) ?? org . username ,
186
- componentFilter : getOrgComponentFilter ( org , mdMap , metadata ) ,
187
- metadataTypes : mdMap . size ? Array . from ( mdMap . keys ( ) ) : undefined ,
188
- registry,
189
- } ) ;
190
-
191
- fromConnection . toArray ( ) . map ( addToComponentSet ( componentSet ) ) ;
191
+ const orgComponentSet = await this . resolveOrgComponents ( registry , org , metadata ) ;
192
+ orgComponentSet . toArray ( ) . map ( addToComponentSet ( componentSet ) ) ;
192
193
}
193
194
194
195
// there should have been a componentSet created by this point.
@@ -197,9 +198,35 @@ export class ComponentSetBuilder {
197
198
componentSet . sourceApiVersion ??= options . sourceapiversion ;
198
199
componentSet . projectDirectory = options . projectDir ;
199
200
200
- logComponents ( logger , componentSet ) ;
201
+ logComponents ( componentSet ) ;
201
202
return componentSet ;
202
203
}
204
+
205
+ private static async resolveOrgComponents (
206
+ registry : RegistryAccess ,
207
+ org : OrgOption ,
208
+ metadata ?: MetadataOption
209
+ ) : Promise < ComponentSet > {
210
+ let mdMap = new Map ( ) as MetadataMap ;
211
+ let debugMsg = `Building ComponentSet from metadata in an org using targetUsername: ${ org . username } ` ;
212
+ if ( metadata ) {
213
+ if ( metadata . metadataEntries ?. length ) {
214
+ debugMsg += ` filtering on metadata: ${ metadata . metadataEntries . toString ( ) } ` ;
215
+ }
216
+ if ( metadata . excludedEntries ?. length ) {
217
+ debugMsg += ` excluding metadata: ${ metadata . excludedEntries . toString ( ) } ` ;
218
+ }
219
+ mdMap = buildMapFromMetadata ( metadata , registry ) ;
220
+ }
221
+ getLogger ( ) . debug ( debugMsg ) ;
222
+
223
+ return ComponentSet . fromConnection ( {
224
+ usernameOrConnection : ( await StateAggregator . getInstance ( ) ) . aliases . getUsername ( org . username ) ?? org . username ,
225
+ componentFilter : getOrgComponentFilter ( org , mdMap , metadata ) ,
226
+ metadataTypes : mdMap . size ? Array . from ( mdMap . keys ( ) ) : undefined ,
227
+ registry,
228
+ } ) ;
229
+ }
203
230
}
204
231
205
232
const addToComponentSet =
@@ -234,27 +261,27 @@ const assertNoWildcardInDestructiveEntries = (mdEntry: MetadataTypeAndMetadataNa
234
261
235
262
/** This is only for debug output of matched files based on the command flags.
236
263
* It will log up to 20 file matches. */
237
- const logComponents = ( logger : Logger , componentSet : ComponentSet ) : void => {
238
- logger . debug ( `Matching metadata files (${ componentSet . size } ):` ) ;
264
+ const logComponents = ( componentSet : ComponentSet ) : void => {
265
+ getLogger ( ) . debug ( `Matching metadata files (${ componentSet . size } ):` ) ;
239
266
240
267
const components = componentSet . getSourceComponents ( ) . toArray ( ) ;
241
268
242
269
components
243
270
. slice ( 0 , 20 )
244
271
. map ( ( cmp ) => cmp . content ?? cmp . xml ?? cmp . fullName )
245
- . map ( ( m ) => logger . debug ( m ) ) ;
246
- if ( components . length > 20 ) logger . debug ( `(showing 20 of ${ componentSet . size } matches)` ) ;
272
+ . map ( ( m ) => getLogger ( ) . debug ( m ) ) ;
273
+ if ( components . length > 20 ) getLogger ( ) . debug ( `(showing 20 of ${ componentSet . size } matches)` ) ;
247
274
248
- logger . debug ( `ComponentSet apiVersion = ${ componentSet . apiVersion ?? '<not set>' } ` ) ;
249
- logger . debug ( `ComponentSet sourceApiVersion = ${ componentSet . sourceApiVersion ?? '<not set>' } ` ) ;
275
+ getLogger ( ) . debug ( `ComponentSet apiVersion = ${ componentSet . apiVersion ?? '<not set>' } ` ) ;
276
+ getLogger ( ) . debug ( `ComponentSet sourceApiVersion = ${ componentSet . sourceApiVersion ?? '<not set>' } ` ) ;
250
277
} ;
251
278
252
279
const getOrgComponentFilter = (
253
280
org : OrgOption ,
254
281
mdMap : MetadataMap ,
255
282
metadata ?: MetadataOption
256
283
) : FromConnectionOptions [ 'componentFilter' ] =>
257
- metadata
284
+ metadata ?. metadataEntries ?. length
258
285
? ( component : Partial < FileProperties > ) : boolean => {
259
286
if ( component . type && component . fullName ) {
260
287
const mdMapEntry = mdMap . get ( component . type ) ;
@@ -312,11 +339,33 @@ const typeAndNameToMetadataComponents =
312
339
. filter ( ( cs ) => minimatch ( cs . fullName , metadataName ) )
313
340
: [ { type, fullName : metadataName } ] ;
314
341
315
- // TODO: use Map.groupBy when it's available
316
- const buildMapFromComponents = ( components : MetadataTypeAndMetadataName [ ] ) : MetadataMap => {
342
+ const buildMapFromMetadata = ( mdOption : MetadataOption , registry : RegistryAccess ) : MetadataMap => {
317
343
const mdMap : MetadataMap = new Map < string , string [ ] > ( ) ;
318
- components . map ( ( cmp ) => {
319
- mdMap . set ( cmp . type . name , [ ...( mdMap . get ( cmp . type . name ) ?? [ ] ) , cmp . metadataName ] ) ;
320
- } ) ;
344
+
345
+ // Add metadata type entries we were told to include
346
+ if ( mdOption . metadataEntries ?. length ) {
347
+ mdOption . metadataEntries . map ( entryToTypeAndName ( registry ) ) . map ( ( cmp ) => {
348
+ mdMap . set ( cmp . type . name , [ ...( mdMap . get ( cmp . type . name ) ?? [ ] ) , cmp . metadataName ] ) ;
349
+ } ) ;
350
+ }
351
+
352
+ // Build an array of excluded types from the options
353
+ if ( mdOption . excludedEntries ?. length ) {
354
+ const excludedTypes : string [ ] = [ ] ;
355
+ mdOption . excludedEntries . map ( entryToTypeAndName ( registry ) ) . map ( ( cmp ) => {
356
+ if ( cmp . metadataName === '*' ) {
357
+ excludedTypes . push ( cmp . type . name ) ;
358
+ }
359
+ } ) ;
360
+ if ( mdMap . size === 0 ) {
361
+ // we are excluding specific metadata types from all supported types
362
+ Object . values ( registry . getRegistry ( ) . types ) . map ( ( t ) => {
363
+ if ( ! excludedTypes . includes ( t . name ) ) {
364
+ mdMap . set ( t . name , [ ] ) ;
365
+ }
366
+ } ) ;
367
+ }
368
+ }
369
+
321
370
return mdMap ;
322
371
} ;
0 commit comments