@@ -176,10 +176,16 @@ export default class RetrieveMetadata extends SfCommand<RetrieveResultJson> {
176176 // eslint-disable-next-line complexity
177177 public async run ( ) : Promise < RetrieveResultJson > {
178178 const { flags } = await this . parse ( RetrieveMetadata ) ;
179+
179180 let resolvedTargetDir : string | undefined ;
180181 if ( flags [ 'output-dir' ] ) {
181182 resolvedTargetDir = resolve ( flags [ 'output-dir' ] ) ;
182- if ( SfProject . getInstance ( ) ?. getPackageNameFromPath ( resolvedTargetDir ) ) {
183+
184+ await fs . promises . mkdir ( resolvedTargetDir , { recursive : true } ) ;
185+
186+ const packageName = SfProject . getInstance ( ) ?. getPackageNameFromPath ( resolvedTargetDir ) ;
187+
188+ if ( packageName && ! flags [ 'output-dir' ] . startsWith ( '.' ) ) {
183189 throw messages . createError ( 'retrieveTargetDirOverlapsPackage' , [ flags [ 'output-dir' ] ] ) ;
184190 }
185191 }
@@ -271,9 +277,42 @@ export default class RetrieveMetadata extends SfCommand<RetrieveResultJson> {
271277
272278 this . ms . stop ( ) ;
273279
274- // flags['output-dir'] will set resolvedTargetDir var, so this check is redundant, but allows for nice typings in the moveResultsForRetrieveTargetDir method
275280 if ( flags [ 'output-dir' ] && resolvedTargetDir ) {
276- await this . moveResultsForRetrieveTargetDir ( flags [ 'output-dir' ] , resolvedTargetDir ) ;
281+ // Determine the actual source directory where files were retrieved
282+ const actualSourceDir = flags [ 'output-dir' ] . startsWith ( '.' )
283+ ? join ( ( await SfProject . resolve ( ) ) . getPath ( ) , 'tempRetrieve' )
284+ : resolvedTargetDir ;
285+
286+ const mainDefaultPath = join ( actualSourceDir , 'main' , 'default' ) ;
287+ const hasMainDefaultStructure = await fs . promises
288+ . access ( mainDefaultPath )
289+ . then ( ( ) => true )
290+ . catch ( ( ) => false ) ;
291+
292+ if ( hasMainDefaultStructure ) {
293+ await this . moveResultsForRetrieveTargetDir ( flags [ 'output-dir' ] , actualSourceDir ) ;
294+
295+ // Update file response paths to reflect the actual destination for hidden directories
296+ if ( flags [ 'output-dir' ] . startsWith ( '.' ) ) {
297+ const outputDir = flags [ 'output-dir' ] ;
298+ this . retrieveResult . getFileResponses ( ) . forEach ( ( fileResponse ) => {
299+ if ( fileResponse . filePath ?. includes ( 'tempRetrieve/' ) ) {
300+ fileResponse . filePath = fileResponse . filePath . replace ( 'tempRetrieve/' , `${ outputDir } /` ) ;
301+ }
302+ } ) ;
303+ }
304+
305+ if ( flags [ 'output-dir' ] . startsWith ( '.' ) ) {
306+ await fs . promises . rm ( actualSourceDir , { recursive : true } ) ;
307+ } else {
308+ this . retrieveResult . getFileResponses ( ) . forEach ( ( fileResponse ) => {
309+ // Update the path to reflect the actual location
310+ if ( fileResponse . filePath ?. includes ( 'main/default/' ) ) {
311+ fileResponse . filePath = fileResponse . filePath . replace ( / .* m a i n \/ d e f a u l t \/ / , '' ) ;
312+ }
313+ } ) ;
314+ }
315+ }
277316 }
278317
279318 // reference the flag instead of `format` so we get correct type
@@ -362,7 +401,8 @@ export default class RetrieveMetadata extends SfCommand<RetrieveResultJson> {
362401 await promisesQueue (
363402 files ,
364403 async ( file : string ) : Promise < string > => {
365- const dest = join ( src . replace ( join ( 'main' , 'default' ) , '' ) , file ) ;
404+ const relativePath = src . replace ( join ( resolvedTargetDir , 'main' , 'default' ) , '' ) ;
405+ const dest = join ( targetDir , relativePath , file ) ;
366406 const destDir = dirname ( dest ) ;
367407 await fs . promises . mkdir ( destDir , { recursive : true } ) ;
368408 await fs . promises . rename ( join ( src , file ) , dest ) ;
@@ -387,8 +427,16 @@ export default class RetrieveMetadata extends SfCommand<RetrieveResultJson> {
387427 } ) ;
388428 // move contents of 'main/default' to 'retrievetargetdir'
389429 await promisesQueue ( [ join ( resolvedTargetDir , 'main' , 'default' ) ] , mv , 5 , true ) ;
390- // remove 'main/default'
391- await fs . promises . rm ( join ( targetDir , 'main' ) , { recursive : true } ) ;
430+
431+ try {
432+ await fs . promises . access ( join ( targetDir , 'main' ) ) ;
433+ // remove 'main/default'
434+ await fs . promises . rm ( join ( targetDir , 'main' ) , { recursive : true } ) ;
435+ } catch ( e ) {
436+ const err = SfError . wrap ( e ) ;
437+ getLogger ( ) . debug ( `Error directory does not exist: ${ err . message }
438+ Due to: ${ err . stack ?? 'unknown (no error stack)' } ` ) ;
439+ }
392440 }
393441}
394442
@@ -518,6 +566,18 @@ const buildRetrieveOptions = async (
518566 output : string | undefined
519567) : Promise < RetrieveSetOptions > => {
520568 const apiVersion = await resolveApiVersion ( flags ) ;
569+
570+ let retrieveOutput : string ;
571+
572+ if ( flags [ 'output-dir' ] && format === 'source' && flags [ 'output-dir' ] . startsWith ( '.' ) ) {
573+ // Use a temporary regular directory for hidden directories
574+ const projectRoot = await SfProject . resolve ( ) ;
575+ retrieveOutput = join ( projectRoot . getPath ( ) , 'tempRetrieve' ) ;
576+ await fs . promises . mkdir ( retrieveOutput , { recursive : true } ) ;
577+ } else {
578+ retrieveOutput = output ?? ( await SfProject . resolve ( ) ) . getDefaultPackage ( ) . fullPath ;
579+ }
580+
521581 return {
522582 usernameOrConnection : flags [ 'target-org' ] . getUsername ( ) ?? flags [ 'target-org' ] . getConnection ( flags [ 'api-version' ] ) ,
523583 merge : true ,
@@ -533,7 +593,7 @@ const buildRetrieveOptions = async (
533593 output : flags [ 'target-metadata-dir' ] as string ,
534594 }
535595 : {
536- output : output ?? ( await SfProject . resolve ( ) ) . getDefaultPackage ( ) . fullPath ,
596+ output : retrieveOutput ,
537597 } ) ,
538598 } ;
539599} ;
0 commit comments