Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate As feature #20

Merged
merged 6 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: run-tests
name: CI

on:
push:
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
run: |
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
composer require "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev
composer require "pestphp/pest:${{ matrix.pest }}" "pestphp/pest-plugin-laravel:${{ matrix.pest }}" --no-interaction --no-update --dev
composer require "pestphp/pest:${{ matrix.pest }}" "pestphp/pest-plugin-laravel:${{ matrix.pest }}" "pestphp/pest-plugin-type-coverage:${{ matrix.pest }}" --no-interaction --no-update --dev
composer update --prefer-dist --no-interaction --no-suggest --dev
composer dump

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"laravel/pint": "^1.18",
"orchestra/testbench": "^9.5",
"pestphp/pest": "^3.2",
"pestphp/pest-plugin-laravel": "^3.0"
"pestphp/pest-plugin-laravel": "^3.0",
"pestphp/pest-plugin-type-coverage": "^3.1"
},
"autoload": {
"psr-4": {
Expand Down
28 changes: 28 additions & 0 deletions config/paragon.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
<?php

return [
/*
|--------------------------------------------------------------------------
| Default Generation Language
|--------------------------------------------------------------------------
|
| Here you may specify the language that Paragon will use when generating files. The default
| is TypeScript which allows for type hinting in IDE's while also providing strong typing.
| If your project doesn't support TypeScript you may instead change this to Javascript.
|
| Please note you may use the `--javascript` or `-j` flag as well if you need to generate Javascript.
|
| Supported: "typescript", "javascript"
|
*/

'generate-as' => 'typescript',

/*
|--------------------------------------------------------------------------
| Paragon Enums
|--------------------------------------------------------------------------
|
| Here you may specify the settings for enum code generation. You have the ability to change
| file paths for locating php enums, what to ignore, and where the generated files should
| be placed. By default, Paragon will look in your entire app/ directory for all enums.
|
*/

'enums' => [
'abstract-class' => 'Enum',

Expand Down
26 changes: 20 additions & 6 deletions src/Commands/GenerateEnumsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;
use Kirschbaum\Paragon\Concerns\DiscoverEnums;
use Kirschbaum\Paragon\Concerns\GenerateAs;
use Kirschbaum\Paragon\Concerns\IgnoreParagon;
use Kirschbaum\Paragon\Generators\AbstractEnumGenerator;
use Kirschbaum\Paragon\Generators\EnumGenerator;
use ReflectionEnum;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;
use UnitEnum;

#[AsCommand(name: 'paragon:generate-enums', description: 'Generate Typescript versions of existing PHP enums')]
class GenerateEnumsCommand extends Command
Expand All @@ -28,7 +30,7 @@ public function handle(): int
$builder = $this->builder();

$generatedEnums = $this->enums()
->map(fn ($enum) => app(EnumGenerator::class, ['enum' => $enum, 'builder' => $builder])())
->map(fn (string $enum) => app(EnumGenerator::class, ['enum' => $enum, 'builder' => $builder])())
->filter();

$this->components->info("{$generatedEnums->count()} enums have been (re)generated.");
Expand All @@ -43,15 +45,15 @@ public function handle(): int
/**
* Gather all enum namespaces for searching.
*
* @return Collection<int,class-string<\UnitEnum>>
* @return Collection<int,class-string<UnitEnum>>
*/
protected function enums(): Collection
{
/** @var string */
$phpPath = config('paragon.enums.paths.php');

return DiscoverEnums::within(app_path($phpPath))
->reject(function ($enum) {
->reject(function (string $enum) {
if (! enum_exists($enum)) {
return true;
}
Expand All @@ -71,10 +73,16 @@ protected function enums(): Collection

protected function builder(): EnumBuilder
{
return $this->option('javascript')
? app(EnumJsBuilder::class)
: app(EnumTsBuilder::class);
/** @var string */
$generateAs = config('paragon.generate-as');

$builder = match (true) {
$this->option('javascript') => EnumJsBuilder::class,
$this->option('typescript') => EnumTsBuilder::class,
default => GenerateAs::from($generateAs)->builder()
};

return app($builder);
}

/**
Expand All @@ -91,6 +99,12 @@ protected function getOptions(): array
mode: InputOption::VALUE_NONE,
description: 'Output Javascript files',
),
new InputOption(
name: 'typescript',
shortcut: 't',
mode: InputOption::VALUE_NONE,
description: 'Output TypeScript files',
),
];
}
}
2 changes: 1 addition & 1 deletion src/Commands/MakeEnumMethodCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class MakeEnumMethodCommand extends GeneratorCommand
/**
* Execute the console command.
*
* @throws FileNotFoundException
* @throws Exception
* @throws FileNotFoundException
*/
public function handle(): ?bool
{
Expand Down
5 changes: 3 additions & 2 deletions src/Concerns/Builders/EnumJsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Kirschbaum\Paragon\Concerns\Builders;

use BackedEnum;
use ReflectionEnumBackedCase;
use ReflectionEnumUnitCase;
use ReflectionMethod;

Expand Down Expand Up @@ -35,7 +36,7 @@ public function fileExtension(): string
/**
* Prepare the method and its respective values so it can get injected into the case object.
*/
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $case): string
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase|ReflectionEnumBackedCase $case): string
{
$value = $case->getValue()->{$method->getName()}();
$class = class_basename($method->getDeclaringClass()->getName());
Expand All @@ -53,7 +54,7 @@ public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $cas
/**
* Assemble the static getter method code for the enum case object.
*/
public function assembleCaseGetter(ReflectionEnumUnitCase $case): string
public function assembleCaseGetter(ReflectionEnumUnitCase|ReflectionEnumBackedCase $case): string
{
return <<<JS
static get {$case->name}() {
Expand Down
5 changes: 3 additions & 2 deletions src/Concerns/Builders/EnumTsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Kirschbaum\Paragon\Concerns\Builders;

use BackedEnum;
use ReflectionEnumBackedCase;
use ReflectionEnumUnitCase;
use ReflectionMethod;

Expand Down Expand Up @@ -35,7 +36,7 @@ public function fileExtension(): string
/**
* Prepare the method and its respective values so it can get injected into the case object.
*/
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $case): string
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase|ReflectionEnumBackedCase $case): string
{
$value = $case->getValue()->{$method->getName()}();
$class = class_basename($method->getDeclaringClass()->getName());
Expand All @@ -53,7 +54,7 @@ public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $cas
/**
* Assemble the static getter method code for the enum case object.
*/
public function assembleCaseGetter(ReflectionEnumUnitCase $case): string
public function assembleCaseGetter(ReflectionEnumUnitCase|ReflectionEnumBackedCase $case): string
{
$class = class_basename($case->getDeclaringClass()->name);

Expand Down
19 changes: 12 additions & 7 deletions src/Concerns/DiscoverEnums.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
use ReflectionException;
use SplFileInfo;
use Symfony\Component\Finder\Finder;
use UnitEnum;

class DiscoverEnums
{
/**
* Get all the enums by searching the given directory.
*
* @param array<int,string>|string $path
* @param array<int, string>|string $path
*
* @return Collection<class-string<\UnitEnum>,class-string<\UnitEnum>>
* @return Collection<class-string<UnitEnum>, class-string<UnitEnum>>
*/
public static function within(array|string $path): Collection
{
Expand All @@ -25,13 +26,15 @@ public static function within(array|string $path): Collection
/**
* Filter the files down to only enums.
*
* @param Finder<string,SplFileInfo> $files
* @param Finder<string, SplFileInfo> $files
*
* @return Collection<class-string<\UnitEnum>,class-string<\UnitEnum>>
* @return Collection<class-string<UnitEnum>, class-string<UnitEnum>>
*/
protected static function getEnums(Finder $files): Collection
{
/** @var Collection<int, SplFileInfo> $fileCollection */
/**
* @var Collection<int, SplFileInfo> $fileCollection
*/
$fileCollection = collect($files);

return $fileCollection
Expand All @@ -56,11 +59,13 @@ protected static function getEnums(Finder $files): Collection
/**
* Extract the class name from the given file path.
*
* @return class-string<\UnitEnum>
* @return class-string<UnitEnum>
*/
protected static function classFromFile(SplFileInfo $file): string
{
/** @var class-string<\UnitEnum> */
/**
* @var class-string<UnitEnum>
*/
return str($file->getRealPath())
->replaceFirst(base_path(), '')
->trim(DIRECTORY_SEPARATOR)
Expand Down
25 changes: 25 additions & 0 deletions src/Concerns/GenerateAs.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Kirschbaum\Paragon\Concerns;

use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;

enum GenerateAs: string
{
case Javascript = 'javascript';

case TypeScript = 'typescript';

/**
* @return class-string<EnumBuilder>
*/
public function builder(): string
{
return match ($this) {
self::Javascript => EnumJsBuilder::class,
self::TypeScript => EnumTsBuilder::class,
};
}
}
4 changes: 3 additions & 1 deletion src/Generators/AbstractEnumGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ protected function imports(): Collection
return collect();
}

/** @var Collection<int, SplFileInfo> $fileCollection */
/**
* @var Collection<int, SplFileInfo> $fileCollection
*/
$fileCollection = collect($files);

return $fileCollection
Expand Down
22 changes: 12 additions & 10 deletions src/Generators/EnumGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Fluent;
use Illuminate\Support\Stringable;
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use Kirschbaum\Paragon\Concerns\IgnoreParagon;
use ReflectionEnum;
use ReflectionEnumBackedCase;
use ReflectionEnumUnitCase;
use ReflectionMethod;
use UnitEnum;

class EnumGenerator
{
Expand All @@ -25,7 +27,7 @@ class EnumGenerator
/**
* Create new EnumGenerator instance.
*
* @param class-string<\UnitEnum> $enum
* @param class-string<UnitEnum> $enum
*/
public function __construct(protected string $enum, protected EnumBuilder $builder)
{
Expand Down Expand Up @@ -112,11 +114,11 @@ protected function relativePath(): string
protected function buildTypeDefinition(): string
{
return $this->methods()
->map(fn ($method) => PHP_EOL . " {$method->getName()}();")
->map(fn (ReflectionMethod $method) => PHP_EOL . " {$method->getName()}();")
->sortDesc()
->when(
$this->reflector->isBacked(),
fn ($collection) => $collection->push(PHP_EOL . " value: {$this->valueReturnType()};")
fn (Collection $collection) => $collection->push(PHP_EOL . " value: {$this->valueReturnType()};")
)
->reverse()
->join('');
Expand Down Expand Up @@ -147,12 +149,12 @@ protected function valueReturnType(): string
/**
* Build all the case objects.
*
* @param Collection<int,ReflectionEnumUnitCase|ReflectionEnumBackedCase> $cases
* @param Collection<int, ReflectionEnumUnitCase|ReflectionEnumBackedCase> $cases
*/
protected function buildCases(Collection $cases): string
{
return $cases
->map(function (ReflectionEnumUnitCase $case) {
->map(function (ReflectionEnumUnitCase|ReflectionEnumBackedCase $case) {
$value = $this->caseValueProperty($case);

$methodValues = $this->methods()
Expand All @@ -173,10 +175,10 @@ protected function caseValueProperty(ReflectionEnumUnitCase|ReflectionEnumBacked
->prepend(PHP_EOL . ' ')
->when(
$this->reflector->getBackingType()->getName() === 'int',
fn ($string) => $case->getValue() instanceof BackedEnum
fn (Stringable $string) => $case->getValue() instanceof BackedEnum
? $string->append("{$case->getValue()->value}")
: $string,
fn ($string) => $case->getValue() instanceof BackedEnum
fn (Stringable $string) => $case->getValue() instanceof BackedEnum
? $string->append("'{$case->getValue()->value}'")
: $string,
)
Expand All @@ -189,7 +191,7 @@ protected function caseValueProperty(ReflectionEnumUnitCase|ReflectionEnumBacked
/**
* Assemble the actual enum case object code including the name, value if needed, and any public methods.
*
* @param Collection<int,string> $methodValues
* @param Collection<int, string> $methodValues
*/
protected function assembleCaseObject(
ReflectionEnumUnitCase|ReflectionEnumBackedCase $case,
Expand All @@ -208,12 +210,12 @@ protected function assembleCaseObject(
/**
* Build all case object getter methods.
*
* @param Collection<int,ReflectionEnumUnitCase|ReflectionEnumBackedCase> $cases
* @param Collection<int, ReflectionEnumUnitCase|ReflectionEnumBackedCase> $cases
*/
protected function buildGetters(Collection $cases): string
{
return $cases
->map(fn ($case) => $this->builder->assembleCaseGetter($case))
->map(fn (ReflectionEnumUnitCase|ReflectionEnumBackedCase $case) => $this->builder->assembleCaseGetter($case))
->join(PHP_EOL . PHP_EOL);
}

Expand Down
Loading
Loading