From 630219e471a3315014aad998b9f754a1337de57b Mon Sep 17 00:00:00 2001 From: Andrey Helldar Date: Sat, 24 Aug 2024 18:42:39 +0300 Subject: [PATCH] Improved generation of helper files for IDE --- src/Commands/GenerateHelperCommand.php | 141 +++++++++++++++------ stubs/helper.stub | 2 +- tests/Datasets/types.php | 5 +- tests/Helpers/content.php | 2 +- tests/Snapshots/dynamic | 12 -- tests/Snapshots/request | 11 ++ tests/Snapshots/response | 15 +++ tests/Snapshots/static | 12 -- tests/Unit/Commands/GenerateHelperTest.php | 12 +- 9 files changed, 135 insertions(+), 77 deletions(-) delete mode 100644 tests/Snapshots/dynamic create mode 100644 tests/Snapshots/request create mode 100644 tests/Snapshots/response delete mode 100644 tests/Snapshots/static diff --git a/src/Commands/GenerateHelperCommand.php b/src/Commands/GenerateHelperCommand.php index 22abd61..ec9770a 100644 --- a/src/Commands/GenerateHelperCommand.php +++ b/src/Commands/GenerateHelperCommand.php @@ -4,22 +4,28 @@ namespace DragonCode\LaravelHttpMacros\Commands; +use Closure; use DragonCode\LaravelHttpMacros\Macros\Macro; use DragonCode\Support\Facades\Filesystem\Directory; use DragonCode\Support\Facades\Filesystem\File; use Illuminate\Console\Command; +use Illuminate\Support\Collection; use Illuminate\Support\Str; +use ReflectionFunction; +use ReflectionNamedType; +use ReflectionParameter; +use ReflectionUnionType; -use function array_map; use function base_path; +use function class_exists; use function collect; use function config; use function file_get_contents; -use function implode; -use function is_string; +use function is_numeric; +use function Laravel\Prompts\intro; +use function Laravel\Prompts\outro; use function sprintf; - -use const PHP_EOL; +use function var_export; class GenerateHelperCommand extends Command { @@ -29,71 +35,124 @@ class GenerateHelperCommand extends Command public function handle(): void { - $names = $this->names(); + $this->sections()->map(function (array $macros, string $section) { + intro($section); + + return $this->macros($macros); + }) + ->tap(fn () => outro('storing')) + ->tap(fn () => $this->cleanUp()) + ->each(fn (Collection $blocks, string $section) => $this->store( + $section, + $this->compileBlocks($section, $blocks->flatten()) + )); + } + + protected function cleanUp(): void + { + $this->components->task('clean up', fn () => Directory::ensureDelete($this->directory())); + } + + protected function macros(array $macros): Collection + { + return collect($macros)->map(function (Macro|string $macro, int|string $name) { + $name = $this->resolveName($macro, $name); - $static = $this->make($names, true); - $dynamic = $this->make($names); + $this->components->task($name, function () use ($macro, $name, &$result) { + $result = $this->prepare($name, $this->reflectionCallback($macro::callback())->getParameters()); + }); - $this->cleanUp(); - $this->store($static, true); - $this->store($dynamic); + return $result; + }); } - protected function make(array $names, bool $isStatic = false): array + protected function store(string $section, string $content): void { - return array_map( - fn (string $name) => sprintf( - ' * @method %s $this %s(\Closure|string $class, int|string|null $key = null)', - $isStatic ? 'static' : '', - $name - ), - $names - ); + $this->components->task($section, fn () => File::store($this->helperPath($section), $content)); } - protected function store(array $methods, bool $isStatic = false): void + protected function compileBlocks(string $section, Collection $blocks): string { - File::store( - $this->path($this->filename($isStatic)), - $this->makeDocBlock($methods) + return Str::replace( + ['{class}', '{methods}'], + [ + Str::studly($section), + $blocks->implode("\n"), + ], + $this->stub() ); } - protected function makeDocBlock(array $methods): string + protected function prepare(string $name, array $functions): array { - return Str::replace('{methods}', implode(PHP_EOL, $methods), $this->template()); + return $this->docBlock($name, $this->docBlockParameters($functions)); } - protected function names(): array + protected function docBlock(string $name, string $parameters): array { - return collect($this->macros())->map( - fn (Macro|string $macro, int|string $name) => is_string($name) ? $name : $macro::name() - )->all(); + return [ + sprintf(' * @method $this %s(%s)', $name, $parameters), + sprintf(' * @method static $this %s(%s)', $name, $parameters), + ]; } - protected function path(?string $filename = null): string + /** + * @param array $functions + * + * @return Collection + */ + protected function docBlockParameters(array $functions): string { - return base_path('vendor/_http_macros/' . $filename); + return collect($functions)->map(function (ReflectionParameter $parameter) { + $result = $parameter->hasType() ? $this->compactTypes($parameter->getType()) : 'mixed'; + + $result .= ' $' . $parameter->getName(); + + if ($parameter->isDefaultValueAvailable()) { + $result .= ' = ' . var_export($parameter->getDefaultValue(), true); + } + + return $result; + })->implode(', '); } - protected function filename(bool $isStatic): string + protected function compactTypes(ReflectionNamedType|ReflectionUnionType $type): string { - return $isStatic - ? '_ide_helper_macro_static.php' - : '_ide_helper_macro.php'; + if ($type instanceof ReflectionNamedType) { + return class_exists($type->getName()) ? '\\' . $type->getName() : $type->getName(); + } + + return collect($type->getTypes())->map( + fn (ReflectionNamedType $type) => $this->compactTypes($type) + )->implode('|'); } - protected function cleanUp(): void + protected function reflectionCallback(Closure $callback): ReflectionFunction + { + return new ReflectionFunction($callback); + } + + protected function resolveName(Macro|string $macro, int|string $name): string + { + return is_numeric($name) ? $macro::name() : $name; + } + + protected function sections(): Collection + { + return collect(config('http.macros', [])); + } + + protected function helperPath(string $name): string { - Directory::ensureDelete($this->path()); + return $this->directory() . "/_ide_helper_macro_$name.php"; } - protected function macros(): array + protected function directory(): string { - return config('http.macros.response', []); + return base_path('vendor/_http_macros'); } - protected function template(): string + protected function stub(): string { return file_get_contents(__DIR__ . '/../../stubs/helper.stub'); } diff --git a/stubs/helper.stub b/stubs/helper.stub index ddb1ea0..9177382 100644 --- a/stubs/helper.stub +++ b/stubs/helper.stub @@ -6,5 +6,5 @@ namespace Illuminate\Http\Client { /** {methods} */ - class Response {} + class {class} {} } diff --git a/tests/Datasets/types.php b/tests/Datasets/types.php index 16e2b00..882e628 100644 --- a/tests/Datasets/types.php +++ b/tests/Datasets/types.php @@ -2,7 +2,4 @@ declare(strict_types=1); -dataset('types', [ - ['static', '_static'], - ['dynamic', ''], -]); +dataset('types', ['request', 'response']); diff --git a/tests/Helpers/content.php b/tests/Helpers/content.php index c5b00c9..2c78df5 100644 --- a/tests/Helpers/content.php +++ b/tests/Helpers/content.php @@ -4,5 +4,5 @@ function content(string $filename): string { - return trim(file_get_contents($filename)); + return file_get_contents($filename); } diff --git a/tests/Snapshots/dynamic b/tests/Snapshots/dynamic deleted file mode 100644 index f41a11d..0000000 --- a/tests/Snapshots/dynamic +++ /dev/null @@ -1,12 +0,0 @@ - Directory::ensureDelete(base_path('vendor/_http_macros')) ); -test('new generation', closure: function (string $type, string $name) { +test('new generation', function (string $type) { $directory = base_path('vendor/_http_macros'); - $filename = $directory . "/_ide_helper_macro$name.php"; + $filename = $directory . "/_ide_helper_macro_$type.php"; $snapshot = __DIR__ . "/../../Snapshots/$type"; expect($directory)->not->toBeDirectory(); @@ -25,9 +25,9 @@ expect(content($filename))->toBe(content($snapshot)); })->with('types'); -test('replace', closure: function (string $type, string $name) { +test('replace', function (string $type) { $directory = base_path('vendor/_http_macros'); - $filename = $directory . "/_ide_helper_macro$name.php"; + $filename = $directory . "/_ide_helper_macro_$type.php"; $snapshot = __DIR__ . "/../../Snapshots/$type"; expect($directory)->not->toBeDirectory(); @@ -40,9 +40,9 @@ expect(content($filename))->toBe(content($snapshot)); })->with('types'); -test('clean up', closure: function (string $type, string $name) { +test('clean up', function (string $type) { $directory = base_path('vendor/_http_macros'); - $filename = $directory . "/_ide_helper_macro$name.php"; + $filename = $directory . "/_ide_helper_macro_$type.php"; $snapshot = __DIR__ . "/../../Snapshots/$type"; expect($directory)->not->toBeDirectory();