Skip to content

Commit ec4316d

Browse files
dermatzCopilot
andauthored
feat: add wildcard theme resolution to build and clean commands (#150)
* feat: add wildcard theme resolution to build and clean commands * fix: improve theme resolution and output formatting in build command * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * feat: support multiple theme codes for build and clean commands --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 414a312 commit ec4316d

4 files changed

Lines changed: 110 additions & 8 deletions

File tree

docs/commands.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ bin/magento mageforge:theme:build [<themeCodes>...]
5656

5757
**Implementation Details**:
5858

59+
- `themeCodes` accepts single themes (`Vendor/theme`) or just the vendor name (`Vendor`) to target all themes of a specific vendor.
5960
- If no theme codes are provided, displays an interactive prompt to select themes
6061
- For each selected theme:
6162
1. Resolves the theme path
@@ -109,11 +110,13 @@ bin/magento mageforge:theme:watch [--theme=THEME]
109110
**Usage**:
110111

111112
```bash
112-
bin/magento mageforge:theme:clean [<themename>]
113+
bin/magento mageforge:theme:clean [<themename>...]
113114
```
114115

115116
**Implementation Details**:
116117

118+
- Can accept multiple themes like `Vendor/theme1 Vendor/theme2`.
119+
- Accepts simply the vendor name `Vendor` to clean all registered themes for a vendor.
117120
- If no theme name is provided:
118121
- In interactive terminals, displays an interactive prompt to select the theme to clean
119122
- In non-interactive environments, prints the list of available themes and exits, requiring an explicit theme name

src/Console/Command/AbstractCommand.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Laravel\Prompts\SelectPrompt;
88
use Magento\Framework\Console\Cli;
99
use OpenForgeProject\MageForge\Service\ThemeSuggester;
10+
use OpenForgeProject\MageForge\Model\ThemeList;
1011
use Symfony\Component\Console\Command\Command;
1112
use Symfony\Component\Console\Input\InputInterface;
1213
use Symfony\Component\Console\Output\OutputInterface;
@@ -478,4 +479,72 @@ private function removeSecureEnvironmentValue(string $name): void
478479
unset($this->secureEnvStorage[$name]);
479480
$this->clearEnvironmentCache();
480481
}
482+
483+
/**
484+
* Resolve vendor theme codes (e.g., Vendor to all underlying vendor themes)
485+
*
486+
* @param array<string> $themeCodes
487+
* @param ThemeList $themeList
488+
* @return array<string>
489+
*/
490+
protected function resolveVendorThemes(
491+
array $themeCodes,
492+
ThemeList $themeList
493+
): array {
494+
$resolved = [];
495+
$availableThemes = null;
496+
497+
foreach ($themeCodes as $code) {
498+
// Check if it's explicitly a wildcard OR just a vendor name without a slash
499+
$isExplicitWildcard = \str_ends_with($code, '/*');
500+
$isVendorOnly = !\str_contains($code, '/');
501+
502+
if ($isExplicitWildcard || $isVendorOnly) {
503+
// Lazy-load themes only when needed
504+
if ($availableThemes === null) {
505+
$availableThemes = array_map(
506+
fn($theme) => $theme->getCode(),
507+
$themeList->getAllThemes()
508+
);
509+
}
510+
511+
if ($isExplicitWildcard) {
512+
$prefix = substr($code, 0, -1); // Keeps the trailing slash, e.g. "Vendor/"
513+
} else {
514+
$prefix = $code . '/'; // e.g. "Vendor" -> "Vendor/"
515+
}
516+
517+
$matched = array_filter(
518+
$availableThemes,
519+
fn(string $availableCode) => \str_starts_with($availableCode, $prefix)
520+
);
521+
522+
if (empty($matched)) {
523+
$this->io->warning(sprintf("No themes found for vendor/prefix '%s'", $prefix));
524+
525+
// If they typed just a word and it wasn't a vendor,
526+
// we still add it so standard Magento validation kicks in later.
527+
if ($isVendorOnly) {
528+
$resolved[] = $code;
529+
}
530+
} else {
531+
$this->io->note(sprintf(
532+
"Resolved vendor '%s' to %d theme(s): %s",
533+
$code,
534+
count($matched),
535+
implode(', ', $matched)
536+
));
537+
538+
foreach ($matched as $match) {
539+
$resolved[] = $match;
540+
}
541+
}
542+
} else {
543+
$resolved[] = $code;
544+
}
545+
}
546+
547+
// Return a fresh list without duplicates
548+
return array_values(array_unique($resolved));
549+
}
481550
}

src/Console/Command/Theme/BuildCommand.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ protected function configure(): void
5151
->addArgument(
5252
'themeCodes',
5353
InputArgument::IS_ARRAY,
54-
'Theme codes to build (format: Vendor/theme, Vendor/theme 2, ...)',
54+
'Theme codes to build (format: Vendor/theme, Vendor, ...)',
5555
)
5656
->setAliases(['frontend:build']);
5757
}
@@ -66,6 +66,17 @@ protected function configure(): void
6666
protected function executeCommand(InputInterface $input, OutputInterface $output): int
6767
{
6868
$themeCodes = $input->getArgument('themeCodes');
69+
70+
// Allow wildcards using the AbstractCommand helper
71+
if (!empty($themeCodes)) {
72+
$themeCodes = $this->resolveVendorThemes($themeCodes, $this->themeList);
73+
74+
// If wildcards matched nothing and no other explicit themes remain
75+
if (empty($themeCodes)) {
76+
return Command::SUCCESS;
77+
}
78+
}
79+
6980
$isVerbose = $this->isVerbose($output);
7081

7182
if (empty($themeCodes)) {
@@ -337,12 +348,22 @@ private function processTheme(
337348
private function displayBuildSummary(SymfonyStyle $io, array $successList, float $duration): void
338349
{
339350
$io->newLine();
340-
$io->success(sprintf('🚀 Build process completed in %.2f seconds with the following results:', $duration));
341-
$io->writeln('Summary:');
342-
$io->newLine();
343351

344-
if (empty($successList)) {
345-
$io->warning('No themes were built successfully.');
352+
$successCount = count($successList);
353+
354+
if ($successCount > 0) {
355+
$io->success(sprintf(
356+
'🚀 Successfully built %d theme(s). Build process completed in %.2f seconds.',
357+
$successCount,
358+
$duration
359+
));
360+
$io->writeln('Summary:');
361+
$io->newLine();
362+
} else {
363+
$io->warning(sprintf(
364+
'Build process completed in %.2f seconds, but no themes were built successfully.',
365+
$duration
366+
));
346367
return;
347368
}
348369

src/Console/Command/Theme/CleanCommand.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ protected function configure(): void
4949
->addArgument(
5050
'themeCodes',
5151
InputArgument::IS_ARRAY,
52-
'Theme codes to clean (format: Vendor/theme, Vendor/theme 2, ...)',
52+
'Theme codes to clean (format: Vendor/theme, Vendor, ...)',
5353
)
5454
->addOption('all', 'a', InputOption::VALUE_NONE, 'Clean all themes')
5555
->addOption(
@@ -105,6 +105,15 @@ private function resolveThemeCodes(InputInterface $input, OutputInterface $outpu
105105
return $this->getAllThemeCodes();
106106
}
107107

108+
if (!empty($themeCodes)) {
109+
$themeCodes = $this->resolveVendorThemes($themeCodes, $this->themeList);
110+
111+
// If wildcards matched nothing and no other explicit themes remain
112+
if (empty($themeCodes)) {
113+
return null;
114+
}
115+
}
116+
108117
if (empty($themeCodes)) {
109118
return $this->selectThemesInteractively($output);
110119
}

0 commit comments

Comments
 (0)