@@ -478,4 +478,54 @@ private function removeSecureEnvironmentValue(string $name): void
478478 unset($ this ->secureEnvStorage [$ name ]);
479479 $ this ->clearEnvironmentCache ();
480480 }
481+
482+ /**
483+ * Resolve wildcard theme codes (e.g., Vendor/* to all underlying vendor themes)
484+ *
485+ * @param string[] $themeCodes
486+ * @param \OpenForgeProject\MageForge\Model\ThemeList $themeList
487+ * @return string[]
488+ */
489+ protected function resolveWildcardThemes (array $ themeCodes , \OpenForgeProject \MageForge \Model \ThemeList $ themeList ): array
490+ {
491+ $ resolved = [];
492+ $ availableThemes = null ;
493+
494+ foreach ($ themeCodes as $ code ) {
495+ if (\str_ends_with ($ code , '/* ' )) {
496+ // Lazy-load themes only when needed
497+ if ($ availableThemes === null ) {
498+ $ availableThemes = array_map (
499+ fn ($ theme ) => $ theme ->getCode (),
500+ $ themeList ->getAllThemes ()
501+ );
502+ }
503+
504+ $ prefix = substr ($ code , 0 , -1 ); // Keeps the trailing slash, e.g. "Vendor/"
505+
506+ $ matched = array_filter (
507+ $ availableThemes ,
508+ fn (string $ availableCode ) => \str_starts_with ($ availableCode , $ prefix )
509+ );
510+
511+ if (empty ($ matched )) {
512+ $ this ->io ->warning (sprintf ("No themes found for prefix '%s' " , $ prefix ));
513+ } else {
514+ $ this ->io ->note (sprintf (
515+ "Resolved '%s' to %d theme(s): %s " ,
516+ $ code ,
517+ count ($ matched ),
518+ implode (', ' , $ matched )
519+ ));
520+ }
521+
522+ $ resolved = array_merge ($ resolved , $ matched );
523+ } else {
524+ $ resolved [] = $ code ;
525+ }
526+ }
527+
528+ // Return a fresh list without duplicates
529+ return array_values (array_unique ($ resolved ));
530+ }
481531}
0 commit comments