diff --git a/packages/documentator/docs/Commands/requirements.md b/packages/documentator/docs/Commands/requirements.md index 75fc7a1c6..0eec80f7e 100644 --- a/packages/documentator/docs/Commands/requirements.md +++ b/packages/documentator/docs/Commands/requirements.md @@ -22,6 +22,18 @@ PHP only: } ``` +You can also merge multiple requirements into one. For example, the +following will merge all `illuminate` into `laravel/framework` (the +package will be ignored if not listed in `require`): + +```json +{ + "merge": { + "illuminate/*": "laravel/framework" + } +} +``` + ## Arguments ### `cwd?` diff --git a/packages/documentator/src/Commands/Requirements.php b/packages/documentator/src/Commands/Requirements.php index 6d66d7a3f..d4e70fa74 100644 --- a/packages/documentator/src/Commands/Requirements.php +++ b/packages/documentator/src/Commands/Requirements.php @@ -19,6 +19,7 @@ use function array_diff_key; use function array_filter; use function array_keys; +use function array_map; use function array_merge; use function array_search; use function array_unique; @@ -33,9 +34,13 @@ use function json_decode; use function mb_strlen; use function mb_substr; +use function natsort; +use function preg_match; +use function preg_quote; use function range; use function reset; use function str_starts_with; +use function strtr; use function trim; use function uksort; @@ -73,6 +78,18 @@ class Requirements extends Command { } } ``` + + You can also merge multiple requirements into one. For example, the + following will merge all `illuminate` into `laravel/framework` (the + package will be ignored if not listed in `require`): + + ```json + { + "merge": { + "illuminate/*": "laravel/framework" + } + } + ``` HELP; public function __invoke(PackageViewer $viewer, Git $git, Serializer $serializer): void { @@ -87,6 +104,9 @@ public function __invoke(PackageViewer $viewer, Git $git, Serializer $serializer 'php' => 'PHP', 'laravel/framework' => 'Laravel', ]; + $merge = $metadata->merge ?? [ + 'illuminate/*' => 'laravel/framework', + ]; foreach ($tags as $tag) { // Cached? @@ -106,13 +126,7 @@ public function __invoke(PackageViewer $viewer, Git $git, Serializer $serializer } // Update - $metadata->requirements[$tag] = []; - - foreach ($packages as $key => $title) { - $required = explode('|', Cast::toString($package['require'][$key] ?? '')); - $required = array_values(array_filter($required)); - $metadata->requirements[$tag][$key] = $required; - } + $metadata->requirements[$tag] = $this->getPackageRequirements($packages, $merge, $package); } // Cleanup @@ -192,6 +206,64 @@ protected function getPackageInfo(Git $git, string $tag, string $cwd): ?array { return $package; } + /** + * @param array $require + * @param array $merge + * @param array $package + * + * @return array> + */ + protected function getPackageRequirements(array $require, array $merge, array $package): array { + // Prepare + $regexps = []; + + foreach ($require as $key => $title) { + $regexp = '#^'.preg_quote($key).'$#u'; + $regexps[$regexp] = $key; + } + + foreach ($merge as $pattern => $key) { + $regexp = '#^'.strtr(preg_quote($pattern), ['\\*' => '.+?']).'$#u'; + $regexps[$regexp] = $key; + } + + // Requirements + $requirements = []; + + foreach ($package['require'] ?? [] as $requirement => $constraint) { + // Match? + $match = false; + + foreach ($regexps as $regexp => $key) { + if (preg_match($regexp, $requirement)) { + $requirement = $key; + $match = true; + break; + } + } + + if (!$match) { + continue; + } + + // Wanted? + if (!isset($require[$requirement])) { + continue; + } + + // Add + $required = explode('|', Cast::toString($constraint)); + $required = array_values(array_filter(array_map(trim(...), $required))); + $requirement = Cast::toString($requirement); + $requirements[$requirement] = array_merge($requirements[$requirement] ?? [], $required); + $requirements[$requirement] = array_values(array_unique($requirements[$requirement])); + + natsort($requirements[$requirement]); + } + + return $requirements; + } + /** * @param array $packages * diff --git a/packages/documentator/src/Commands/RequirementsTest.php b/packages/documentator/src/Commands/RequirementsTest.php index 034a74d03..f47211338 100644 --- a/packages/documentator/src/Commands/RequirementsTest.php +++ b/packages/documentator/src/Commands/RequirementsTest.php @@ -103,4 +103,58 @@ public function getRequirements(array $packages, Metadata $metadata): array { $command->getRequirements($packages, $metadata), ); } + + public function testGetPackageRequirements(): void { + $merge = [ + 'illuminate/*' => 'laravel/framework', + 'example/package' => 'another/package', + ]; + $packages = [ + 'laravel/framework' => 'Laravel', + 'php' => 'PHP', + ]; + $package = [ + 'require' => [ + 'php' => '^8.1|^8.2|^8.3', + 'ext-mbstring' => '*', + 'composer/semver' => '^3.2', + 'laravel/framework' => '^10.34.0', + 'league/commonmark' => '^2.4', + 'illuminate/auth' => '^10 || ^9', + 'illuminate/bus' => '^9 || ^10', + 'illuminate/contracts' => '^9 || ^10 || ^11', + 'example/package' => '~1.0.0', + ], + ]; + $command = new class() extends Requirements { + /** + * @inheritDoc + */ + #[Override] + public function getPackageRequirements(array $require, array $merge, array $package): array { + return parent::getPackageRequirements( + $require, + $merge, + $package, + ); + } + }; + + self::assertEquals( + [ + 'laravel/framework' => [ + '^9', + '^10', + '^10.34.0', + '^11', + ], + 'php' => [ + '^8.1', + '^8.2', + '^8.3', + ], + ], + $command->getPackageRequirements($packages, $merge, $package), + ); + } } diff --git a/packages/documentator/src/Metadata/Metadata.php b/packages/documentator/src/Metadata/Metadata.php index 5e6def917..ca10d4f95 100644 --- a/packages/documentator/src/Metadata/Metadata.php +++ b/packages/documentator/src/Metadata/Metadata.php @@ -8,13 +8,28 @@ * @internal */ class Metadata implements Serializable { - /** - * @param array $require - * @param array>> $requirements - */ public function __construct( + /** + * Package version. + */ public ?string $version = null, + /** + * Requirements to show. + * + * @var array + */ public array $require = [], + /** + * Defines how to merge packages (`illuminate/*`, `symfony/*`, etc) + * + * @var array|null + */ + public ?array $merge = null, + /** + * Cached requirements. + * + * @var array>> + */ public array $requirements = [], ) { // empty