Skip to content

Commit ad1c0ec

Browse files
Copilotdermatz
andcommitted
Implement hyva-tokens command with token processing services
Co-authored-by: dermatz <[email protected]>
1 parent 94cfa3b commit ad1c0ec

File tree

8 files changed

+685
-1
lines changed

8 files changed

+685
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Please ensure that your Magento installation meets this requirement before insta
4040
| `mageforge:theme:list` | Lists all available themes | `m:t:l` |
4141
| `mageforge:theme:build` | Builds selected themes (CSS/TailwindCSS) | `m:t:b`, `frontend:build` |
4242
| `mageforge:theme:watch` | Starts watch mode for theme development | `m:t:w`, `frontend:watch` |
43+
| `mageforge:hyva:tokens` | Generates Hyvä design tokens CSS from token definitions | `m:h:t` |
4344

4445

4546
---

docs/commands.md

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,81 @@ bin/magento mageforge:system:check
111111

112112
---
113113

114-
### 5. VersionCommand (`mageforge:version`)
114+
### 5. HyvaTokensCommand (`mageforge:hyva:tokens`)
115+
116+
**Purpose**: Generates Hyvä design tokens CSS from token definitions.
117+
118+
**File**: `/src/Console/Command/Hyva/TokensCommand.php`
119+
120+
**Dependencies**:
121+
- `ThemePath` - Service to resolve theme paths
122+
- `ThemeList` - Service to retrieve theme information
123+
- `TokenProcessor` - Service to process and generate token CSS
124+
- `HyvaBuilder` - Service to detect Hyvä themes
125+
126+
**Usage**:
127+
```bash
128+
bin/magento mageforge:hyva:tokens [<theme-code>]
129+
```
130+
131+
**Implementation Details**:
132+
- If no theme code is provided, displays an interactive prompt to select from available Hyvä themes
133+
- Verifies that the selected theme is a Hyvä theme
134+
- Reads configuration from `hyva.config.json` or uses defaults
135+
- Supports multiple token sources:
136+
- `design.tokens.json` file (default)
137+
- Custom token file specified in configuration
138+
- Inline token values in `hyva.config.json`
139+
- Figma tokens format
140+
- Generates `generated/hyva-tokens.css` in the theme's `web/tailwind` directory
141+
- Supports customization via `hyva.config.json`:
142+
- `tokens.src`: Source file path (default: `design.tokens.json`)
143+
- `tokens.format`: Token format - `default` or `figma` (default: `default`)
144+
- `tokens.cssSelector`: CSS selector for generated tokens (default: `@theme` for Tailwind v4, use `:root` for v3)
145+
- `tokens.values`: Inline token definitions
146+
147+
**Configuration Examples**:
148+
149+
Using a Figma tokens file:
150+
```json
151+
{
152+
"tokens": {
153+
"src": "acme.figma-tokens.json",
154+
"format": "figma"
155+
}
156+
}
157+
```
158+
159+
Using inline token values:
160+
```json
161+
{
162+
"tokens": {
163+
"values": {
164+
"colors": {
165+
"primary": {
166+
"lighter": "oklch(62.3% 0.214 259.815)",
167+
"DEFAULT": "oklch(54.6% 0.245 262.881)",
168+
"darker": "oklch(37.9% 0.146 265.522)"
169+
}
170+
}
171+
}
172+
}
173+
}
174+
```
175+
176+
Using custom CSS selector for Tailwind v3:
177+
```json
178+
{
179+
"tokens": {
180+
"src": "design.tokens.json",
181+
"cssSelector": ":root"
182+
}
183+
}
184+
```
185+
186+
---
187+
188+
### 6. VersionCommand (`mageforge:version`)
115189

116190
**Purpose**: Displays the current and latest version of the MageForge module.
117191

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenForgeProject\MageForge\Console\Command\Hyva;
6+
7+
use Laravel\Prompts\SelectPrompt;
8+
use OpenForgeProject\MageForge\Console\Command\AbstractCommand;
9+
use OpenForgeProject\MageForge\Model\ThemeList;
10+
use OpenForgeProject\MageForge\Model\ThemePath;
11+
use OpenForgeProject\MageForge\Service\HyvaTokens\TokenProcessor;
12+
use OpenForgeProject\MageForge\Service\ThemeBuilder\HyvaThemes\Builder as HyvaBuilder;
13+
use Symfony\Component\Console\Command\Command;
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
18+
/**
19+
* Command for generating Hyva design tokens CSS
20+
*/
21+
class TokensCommand extends AbstractCommand
22+
{
23+
/**
24+
* @param ThemePath $themePath
25+
* @param ThemeList $themeList
26+
* @param TokenProcessor $tokenProcessor
27+
* @param HyvaBuilder $hyvaBuilder
28+
*/
29+
public function __construct(
30+
private readonly ThemePath $themePath,
31+
private readonly ThemeList $themeList,
32+
private readonly TokenProcessor $tokenProcessor,
33+
private readonly HyvaBuilder $hyvaBuilder
34+
) {
35+
parent::__construct();
36+
}
37+
38+
/**
39+
* {@inheritdoc}
40+
*/
41+
protected function configure(): void
42+
{
43+
$this->setName($this->getCommandName('hyva', 'tokens'))
44+
->setDescription('Generate Hyva design tokens CSS from token definitions')
45+
->addArgument(
46+
'themeCode',
47+
InputArgument::OPTIONAL,
48+
'Theme code to generate tokens for (format: Vendor/theme)'
49+
);
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
protected function executeCommand(InputInterface $input, OutputInterface $output): int
56+
{
57+
$themeCode = $input->getArgument('themeCode');
58+
59+
// If no theme code provided, show interactive prompt for Hyva themes only
60+
if (empty($themeCode)) {
61+
$hyvaThemes = $this->getHyvaThemes();
62+
63+
if (empty($hyvaThemes)) {
64+
$this->io->error('No Hyvä themes found in this installation.');
65+
return Command::FAILURE;
66+
}
67+
68+
// Check if we're in an interactive terminal environment
69+
if (!$this->isInteractiveTerminal($output)) {
70+
$this->io->info('Available Hyvä themes:');
71+
foreach ($hyvaThemes as $theme) {
72+
$this->io->writeln(' - ' . $theme->getCode());
73+
}
74+
$this->io->newLine();
75+
$this->io->info('Usage: bin/magento mageforge:hyva:tokens <theme-code>');
76+
return Command::SUCCESS;
77+
}
78+
79+
$options = array_map(fn($theme) => $theme->getCode(), $hyvaThemes);
80+
81+
$themeCodePrompt = new SelectPrompt(
82+
label: 'Select Hyvä theme to generate tokens for',
83+
options: $options,
84+
hint: 'Arrow keys to navigate, Enter to confirm'
85+
);
86+
87+
try {
88+
$themeCode = $themeCodePrompt->prompt();
89+
\Laravel\Prompts\Prompt::terminal()->restoreTty();
90+
} catch (\Exception $e) {
91+
$this->io->error('Interactive mode failed: ' . $e->getMessage());
92+
return Command::FAILURE;
93+
}
94+
}
95+
96+
// Get theme path
97+
$themePath = $this->themePath->getPath($themeCode);
98+
if ($themePath === null) {
99+
$this->io->error("Theme $themeCode is not installed.");
100+
return Command::FAILURE;
101+
}
102+
103+
// Verify this is a Hyva theme
104+
if (!$this->hyvaBuilder->detect($themePath)) {
105+
$this->io->error("Theme $themeCode is not a Hyvä theme. This command only works with Hyvä themes.");
106+
return Command::FAILURE;
107+
}
108+
109+
// Process tokens
110+
$this->io->text("Processing design tokens for theme: <fg=cyan>$themeCode</>");
111+
$result = $this->tokenProcessor->process($themePath);
112+
113+
if ($result['success']) {
114+
$this->io->newLine();
115+
$this->io->success($result['message']);
116+
$this->io->writeln("Output file: <fg=green>{$result['outputPath']}</>");
117+
$this->io->newLine();
118+
$this->io->text('ℹ️ Make sure to import this file in your Tailwind CSS configuration.');
119+
return Command::SUCCESS;
120+
} else {
121+
$this->io->error($result['message']);
122+
$this->io->newLine();
123+
$this->io->text('ℹ️ To use this command, you need one of the following:');
124+
$this->io->listing([
125+
'A design.tokens.json file in the theme\'s web/tailwind directory',
126+
'A custom token file specified in hyva.config.json',
127+
'Inline token values in hyva.config.json',
128+
]);
129+
$this->io->newLine();
130+
$this->io->text('Example hyva.config.json with inline tokens:');
131+
$this->io->text(<<<JSON
132+
{
133+
"tokens": {
134+
"values": {
135+
"colors": {
136+
"primary": {
137+
"lighter": "oklch(62.3% 0.214 259.815)",
138+
"DEFAULT": "oklch(54.6% 0.245 262.881)",
139+
"darker": "oklch(37.9% 0.146 265.522)"
140+
}
141+
}
142+
}
143+
}
144+
}
145+
JSON);
146+
return Command::FAILURE;
147+
}
148+
}
149+
150+
/**
151+
* Get list of Hyva themes
152+
*
153+
* @return array
154+
*/
155+
private function getHyvaThemes(): array
156+
{
157+
$allThemes = $this->themeList->getAllThemes();
158+
$hyvaThemes = [];
159+
160+
foreach ($allThemes as $theme) {
161+
$themePath = $this->themePath->getPath($theme->getCode());
162+
if ($themePath && $this->hyvaBuilder->detect($themePath)) {
163+
$hyvaThemes[] = $theme;
164+
}
165+
}
166+
167+
return $hyvaThemes;
168+
}
169+
170+
/**
171+
* Check if the current environment supports interactive terminal input
172+
*
173+
* @param OutputInterface $output
174+
* @return bool
175+
*/
176+
private function isInteractiveTerminal(OutputInterface $output): bool
177+
{
178+
// Check if output is decorated (supports ANSI codes)
179+
if (!$output->isDecorated()) {
180+
return false;
181+
}
182+
183+
// Check if STDIN is available and readable
184+
if (!defined('STDIN') || !is_resource(STDIN)) {
185+
return false;
186+
}
187+
188+
// Additional check: try to detect if running in a proper TTY
189+
$sttyOutput = shell_exec('stty -g 2>/dev/null');
190+
return !empty($sttyOutput);
191+
}
192+
}

0 commit comments

Comments
 (0)