@@ -418,16 +418,20 @@ export async function promptAddonQuestions({
418418
419419 // run setup if we have access to workspace
420420 // prepare addons (both official and non-official)
421- // When no addons are selected, use official addons for setup
422- const officialAddonsList = Array . from ( allAddons . values ( ) ) . filter ( ( addon ) =>
423- officialAddons . some ( ( o ) => o . id === addon . id )
424- ) ;
425- let setups = selectedAddons . length ? selectedAddons : officialAddonsList ;
426- let addonSetupResults = setupAddons ( setups , workspace ) ;
421+ let addonSetupResults : Record < string , AddonSetupResult > = { } ;
422+
423+ // If we have selected addons, run setup on them (regardless of official status )
424+ if ( selectedAddons . length > 0 ) {
425+ addonSetupResults = setupAddons ( selectedAddons , workspace ) ;
426+ }
427427
428428 // prompt which addons to apply (only when no addons were specified)
429429 // Only show selection prompt if no addons were specified at all
430430 if ( selectedAddonIds . length === 0 ) {
431+ // For the prompt, we only show official addons
432+ const officialAddonsList = Array . from ( allAddons . values ( ) ) . filter ( ( addon ) =>
433+ officialAddons . some ( ( o ) => o . id === addon . id )
434+ ) ;
431435 const allSetupResults = setupAddons ( officialAddonsList , workspace ) ;
432436 const addonOptions = officialAddonsList
433437 // only display supported addons relative to the current environment
@@ -456,45 +460,70 @@ export async function promptAddonQuestions({
456460 }
457461 }
458462
459- // Re-run setup for the newly selected addons
460- setups = selectedAddons ;
461- addonSetupResults = setupAddons ( setups , workspace ) ;
463+ // Re-run setup for all selected addons (including any that were added via CLI options)
464+ addonSetupResults = setupAddons ( selectedAddons , workspace ) ;
465+ }
466+
467+ // Ensure all selected addons have setup results
468+ // This should always be the case, but we add a safeguard
469+ const missingSetupResults = selectedAddons . filter ( ( addon ) => ! addonSetupResults [ addon . id ] ) ;
470+ if ( missingSetupResults . length > 0 ) {
471+ const additionalSetupResults = setupAddons ( missingSetupResults , workspace ) ;
472+ Object . assign ( addonSetupResults , additionalSetupResults ) ;
462473 }
463474
464475 // add inter-addon dependencies
465- for ( const addon of selectedAddons ) {
466- const setupResult = addonSetupResults [ addon . id ] ;
467- const missingDependencies = setupResult . dependsOn . filter (
468- ( depId ) => ! selectedAddons . some ( ( a ) => a . id === depId )
469- ) ;
476+ // We need to iterate until no new dependencies are added (to handle transitive dependencies)
477+ let hasNewDependencies = true ;
478+ while ( hasNewDependencies ) {
479+ hasNewDependencies = false ;
480+ const addonsToProcess = [ ...selectedAddons ] ; // Work with a snapshot to avoid infinite loops
481+
482+ for ( const addon of addonsToProcess ) {
483+ const setupResult = addonSetupResults [ addon . id ] ;
484+ if ( ! setupResult ) {
485+ common . errorAndExit ( `Setup result missing for addon: ${ addon . id } ` ) ;
486+ }
487+ const missingDependencies = setupResult . dependsOn . filter (
488+ ( depId ) => ! selectedAddons . some ( ( a ) => a . id === depId )
489+ ) ;
470490
471- for ( const depId of missingDependencies ) {
472- // Dependencies are always official addons
473- const depAddon = allAddons . get ( depId ) ;
474- if ( ! depAddon ) {
475- // If not in resolved addons, try to get it (dependencies are always official)
476- const officialDep = officialAddons . find ( ( a ) => a . id === depId ) ;
477- if ( ! officialDep ) {
478- throw new Error ( `'${ addon . id } ' depends on an invalid add-on: '${ depId } '` ) ;
491+ for ( const depId of missingDependencies ) {
492+ hasNewDependencies = true ;
493+ // Dependencies are always official addons
494+ const depAddon = allAddons . get ( depId ) ;
495+ if ( ! depAddon ) {
496+ // If not in resolved addons, try to get it (dependencies are always official)
497+ const officialDep = officialAddons . find ( ( a ) => a . id === depId ) ;
498+ if ( ! officialDep ) {
499+ throw new Error ( `'${ addon . id } ' depends on an invalid add-on: '${ depId } '` ) ;
500+ }
501+ // Add official dependency to the map and use it
502+ const officialAddonDetails = getAddonDetails ( depId ) ;
503+ allAddons . set ( depId , officialAddonDetails ) ;
504+ selectedAddons . push ( officialAddonDetails ) ;
505+ answers [ depId ] = { } ;
506+ continue ;
479507 }
480- // Add official dependency to the map and use it
481- const officialAddonDetails = getAddonDetails ( depId ) ;
482- allAddons . set ( depId , officialAddonDetails ) ;
483- selectedAddons . push ( officialAddonDetails ) ;
508+
509+ // prompt to install the dependent
510+ const install = await p . confirm ( {
511+ message : `The ${ pc . bold ( pc . cyan ( addon . id ) ) } add-on requires ${ pc . bold ( pc . cyan ( depId ) ) } to also be setup. ${ pc . green ( 'Include it?' ) } `
512+ } ) ;
513+ if ( install !== true ) {
514+ p . cancel ( 'Operation cancelled.' ) ;
515+ process . exit ( 1 ) ;
516+ }
517+ selectedAddons . push ( depAddon ) ;
484518 answers [ depId ] = { } ;
485- continue ;
486519 }
520+ }
487521
488- // prompt to install the dependent
489- const install = await p . confirm ( {
490- message : `The ${ pc . bold ( pc . cyan ( addon . id ) ) } add-on requires ${ pc . bold ( pc . cyan ( depId ) ) } to also be setup. ${ pc . green ( 'Include it?' ) } `
491- } ) ;
492- if ( install !== true ) {
493- p . cancel ( 'Operation cancelled.' ) ;
494- process . exit ( 1 ) ;
495- }
496- selectedAddons . push ( depAddon ) ;
497- answers [ depId ] = { } ;
522+ // Run setup for any newly added dependencies
523+ const newlyAddedAddons = selectedAddons . filter ( ( addon ) => ! addonSetupResults [ addon . id ] ) ;
524+ if ( newlyAddedAddons . length > 0 ) {
525+ const newSetupResults = setupAddons ( newlyAddedAddons , workspace ) ;
526+ Object . assign ( addonSetupResults , newSetupResults ) ;
498527 }
499528 }
500529
0 commit comments