|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace KaririCode\Devkit\Configuration; |
| 6 | + |
| 7 | +use KaririCode\Devkit\Contract\ConfigGenerator; |
| 8 | +use KaririCode\Devkit\Core\ProjectContext; |
| 9 | + |
| 10 | +/** |
| 11 | + * Generates `.kcode/composer.json` — the self-contained dev-toolchain manifest. |
| 12 | + * |
| 13 | + * When `kcode init` runs, this file is written to `.kcode/` and then |
| 14 | + * `composer install --working-dir=.kcode/ --no-interaction` is executed |
| 15 | + * by the InitCommand. Tools are installed into `.kcode/vendor/bin/`, |
| 16 | + * keeping the target project's own composer.json free of dev-tool deps. |
| 17 | + * |
| 18 | + * Version constraints come from `devkit.php` → `tools` key (optional). |
| 19 | + * Falls back to KaririCode-certified defaults when not specified. |
| 20 | + * |
| 21 | + * @since 1.0.0 |
| 22 | + */ |
| 23 | +final class KcodeComposerGenerator implements ConfigGenerator |
| 24 | +{ |
| 25 | + private const array DEFAULT_TOOL_VERSIONS = [ |
| 26 | + 'phpunit/phpunit' => '^12.5', |
| 27 | + 'phpstan/phpstan' => '^2.0', |
| 28 | + 'friendsofphp/php-cs-fixer' => '^3.64', |
| 29 | + 'rector/rector' => '^2.0', |
| 30 | + 'vimeo/psalm' => '^6.0', |
| 31 | + ]; |
| 32 | + |
| 33 | + /** @var array<string, string> Maps devkit.php tool short-names → Composer package names */ |
| 34 | + private const array TOOL_SHORT_NAME_MAP = [ |
| 35 | + 'phpunit' => 'phpunit/phpunit', |
| 36 | + 'phpstan' => 'phpstan/phpstan', |
| 37 | + 'php-cs-fixer' => 'friendsofphp/php-cs-fixer', |
| 38 | + 'rector' => 'rector/rector', |
| 39 | + 'psalm' => 'vimeo/psalm', |
| 40 | + ]; |
| 41 | + |
| 42 | + #[\Override] |
| 43 | + public function toolName(): string |
| 44 | + { |
| 45 | + return 'kcode-composer'; |
| 46 | + } |
| 47 | + |
| 48 | + #[\Override] |
| 49 | + public function outputPath(): string |
| 50 | + { |
| 51 | + return 'composer.json'; |
| 52 | + } |
| 53 | + |
| 54 | + #[\Override] |
| 55 | + public function generate(ProjectContext $context): string |
| 56 | + { |
| 57 | + $require = $this->resolveVersions($context->toolVersions); |
| 58 | + |
| 59 | + $manifest = [ |
| 60 | + 'name' => 'kariricode/devkit-tools', |
| 61 | + 'description' => 'Dev toolchain managed by kcode — do not edit manually.', |
| 62 | + 'require' => $require, |
| 63 | + 'config' => [ |
| 64 | + 'bin-compat' => 'full', |
| 65 | + 'optimize-autoloader' => true, |
| 66 | + 'sort-packages' => true, |
| 67 | + 'preferred-install' => 'dist', |
| 68 | + 'allow-plugins' => [ |
| 69 | + 'infection/extension-installer' => false, |
| 70 | + ], |
| 71 | + ], |
| 72 | + 'minimum-stability' => 'stable', |
| 73 | + 'prefer-stable' => true, |
| 74 | + ]; |
| 75 | + |
| 76 | + return json_encode($manifest, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES) . \PHP_EOL; |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + * Merge user-supplied version constraints with defaults. |
| 81 | + * User constraints win on conflict; short-names are resolved to package names. |
| 82 | + * |
| 83 | + * @param array<string, string> $userVersions From devkit.php → tools |
| 84 | + * @return array<string, string> |
| 85 | + */ |
| 86 | + private function resolveVersions(array $userVersions): array |
| 87 | + { |
| 88 | + $resolved = self::DEFAULT_TOOL_VERSIONS; |
| 89 | + |
| 90 | + foreach ($userVersions as $shortName => $constraint) { |
| 91 | + $package = self::TOOL_SHORT_NAME_MAP[$shortName] ?? $shortName; |
| 92 | + $resolved[$package] = $constraint; |
| 93 | + } |
| 94 | + |
| 95 | + ksort($resolved); |
| 96 | + |
| 97 | + return $resolved; |
| 98 | + } |
| 99 | +} |
0 commit comments