Skip to content

Commit abaa764

Browse files
authored
Merge pull request #1833 from hydephp/experimental-yaml-config-refactor-command
Internal: Adding an experimental config converter command to the monorepo
2 parents d99378a + 92a64b3 commit abaa764

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

monorepo/DevTools/src/MonorepoDevToolsServiceProvider.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function boot(): void
1717
{
1818
$this->commands([
1919
MonorepoReleaseCommand::class,
20+
RefactorConfigCommand::class,
2021
]);
2122
}
2223
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyde\MonorepoDevTools;
6+
7+
use Hyde\Hyde;
8+
use RuntimeException;
9+
use Hyde\Enums\Feature;
10+
use Illuminate\Support\Str;
11+
use Symfony\Component\Yaml\Yaml;
12+
use Hyde\Console\Concerns\Command;
13+
use Hyde\Framework\Features\Blogging\Models\PostAuthor;
14+
use Hyde\Framework\Features\Metadata\MetadataElementContract;
15+
16+
use function config;
17+
use function substr;
18+
use function collect;
19+
use function implode;
20+
use function in_array;
21+
use function is_array;
22+
use function is_string;
23+
use function file_exists;
24+
use function str_starts_with;
25+
use function file_put_contents;
26+
27+
/**
28+
* @internal This class is internal to the hydephp/develop monorepo.
29+
*
30+
* @experimental https://github.com/hydephp/develop/pull/1833
31+
*/
32+
class RefactorConfigCommand extends Command
33+
{
34+
protected const SUPPORTED_FORMATS = ['yaml'];
35+
36+
/** @var string */
37+
protected $signature = 'refactor:config {format : The new configuration format}';
38+
39+
/** @var string */
40+
protected $description = 'Migrate the configuration to a different format.';
41+
42+
public function handle(): int
43+
{
44+
$format = $this->argument('format');
45+
if (! in_array($format, self::SUPPORTED_FORMATS)) {
46+
$this->error('Invalid format. Supported formats: '.implode(', ', self::SUPPORTED_FORMATS));
47+
48+
return 1;
49+
}
50+
51+
$this->gray(" > Migrating configuration to $format");
52+
53+
return match ($format) {
54+
'yaml' => $this->migrateToYaml(),
55+
};
56+
}
57+
58+
protected function migrateToYaml(): int
59+
{
60+
$this->ensureYamlConfigDoesNotExist();
61+
62+
$config = $this->getConfigDiff();
63+
64+
if (empty($config)) {
65+
$this->warn("You don't seem to have any configuration to migrate.");
66+
67+
return 0;
68+
}
69+
70+
$serializedConfig = $this->serializePhpData($config);
71+
$yaml = $this->dumpConfigToYaml($serializedConfig);
72+
73+
file_put_contents(Hyde::path('hyde.yml'), $yaml);
74+
75+
$this->info('All done!');
76+
77+
return 0;
78+
}
79+
80+
protected function ensureYamlConfigDoesNotExist(): void
81+
{
82+
if (file_exists(Hyde::path('hyde.yml')) || file_exists(Hyde::path('hyde.yaml'))) {
83+
throw new RuntimeException('Configuration already exists in YAML format.');
84+
}
85+
}
86+
87+
protected function getConfigDiff(): array
88+
{
89+
$config = config('hyde');
90+
$default = require Hyde::vendorPath('config/hyde.php');
91+
92+
return $this->diffConfig($config, $default);
93+
}
94+
95+
protected function dumpConfigToYaml(array $config): string
96+
{
97+
return Yaml::dump($config, 16, 4, Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK | Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
98+
}
99+
100+
/**
101+
* @param array<string|int, mixed> $config
102+
* @return array<string|int, mixed>
103+
*/
104+
protected function serializePhpData(array $config): array
105+
{
106+
return collect($config)->mapWithKeys(function ($value, $key) {
107+
if (is_array($value)) {
108+
return [$key => $this->serializePhpData($value)];
109+
}
110+
111+
return $this->serializePhpValue($value, $key);
112+
})->toArray();
113+
}
114+
115+
/**
116+
* @param mixed $value
117+
* @param string|int $key
118+
* @return array<string|int, mixed>
119+
*/
120+
protected function serializePhpValue(mixed $value, string|int $key): array
121+
{
122+
if ($value instanceof Feature) {
123+
return [$key => Str::kebab($value->name)];
124+
}
125+
126+
if (is_string($key) && str_starts_with($key, 'Hyde\Pages\\')) {
127+
return [Str::kebab(substr($key, 11)) => $value];
128+
}
129+
130+
if ($value instanceof MetadataElementContract) {
131+
return [$key => $value->__toString()];
132+
}
133+
134+
if ($value instanceof PostAuthor) {
135+
return [$key => $this->serializePostAuthor($value)];
136+
}
137+
138+
return [$key => $value];
139+
}
140+
141+
protected function serializePostAuthor(PostAuthor $author): array
142+
{
143+
return [
144+
'username' => $author->username,
145+
'name' => $author->name,
146+
'website' => $author->website,
147+
];
148+
}
149+
150+
/**
151+
* @param array<string, mixed> $config
152+
* @param array<string, mixed> $default
153+
* @return array<string, mixed>
154+
*/
155+
protected function diffConfig(array $config, array $default): array
156+
{
157+
$diff = [];
158+
159+
foreach ($config as $key => $value) {
160+
if (! isset($default[$key]) || $value != $default[$key]) {
161+
$diff[$key] = $value;
162+
}
163+
}
164+
165+
return $this->arrayFilterRecurse($diff);
166+
}
167+
168+
/**
169+
* @param array<string|int, mixed> $input
170+
* @return array<string|int, mixed>
171+
*/
172+
protected function arrayFilterRecurse(array $input): array
173+
{
174+
foreach ($input as $key => &$value) {
175+
if (is_array($value)) {
176+
$value = $this->arrayFilterRecurse($value);
177+
if (empty($value)) {
178+
unset($input[$key]);
179+
}
180+
} elseif (blank($value)) {
181+
unset($input[$key]);
182+
}
183+
}
184+
185+
return $input;
186+
}
187+
}

0 commit comments

Comments
 (0)