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

Feature/javascript #17

Merged
merged 4 commits into from
Oct 23, 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
9 changes: 5 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/.idea
/.phpunit.cache
/build
/vendor
composer.phar
.DS_Store
.phpunit.result.cache
composer.lock
composer.phar
phpunit.xml
.phpunit.result.cache
.phpunit.cache
.DS_Store
Thumbs.db
35 changes: 33 additions & 2 deletions src/Commands/GenerateEnumsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;
use Kirschbaum\Paragon\Concerns\DiscoverEnums;
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;

#[AsCommand(name: 'paragon:generate-enums', description: 'Generate Typescript versions of existing PHP enums')]
class GenerateEnumsCommand extends Command
Expand All @@ -21,13 +25,15 @@ class GenerateEnumsCommand extends Command
*/
public function handle(): int
{
$builder = $this->builder();

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

$this->components->info("{$generatedEnums->count()} enums have been (re)generated.");

app(AbstractEnumGenerator::class)();
app(AbstractEnumGenerator::class, ['builder' => $builder])();

$this->components->info('Abstract enum class has been (re)generated.');

Expand Down Expand Up @@ -59,4 +65,29 @@ protected function enums(): Collection
})
->values();
}

protected function builder(): EnumBuilder
{
return $this->option('javascript')
? app(EnumJsBuilder::class)
: app(EnumTsBuilder::class);

}

/**
* Get the console command options.
*
* @return array<int, InputOption>
*/
protected function getOptions(): array
{
return [
new InputOption(
name: 'javascript',
shortcut: 'j',
mode: InputOption::VALUE_NONE,
description: 'Output Javascript files',
),
];
}
}
36 changes: 33 additions & 3 deletions src/Commands/MakeEnumMethodCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace Kirschbaum\Paragon\Commands;

use Exception;
use Illuminate\Console\Command;
use Illuminate\Console\GeneratorCommand;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumJsBuilder;
use Kirschbaum\Paragon\Concerns\Builders\EnumTsBuilder;
use Kirschbaum\Paragon\Generators\AbstractEnumGenerator;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;

use function Laravel\Prompts\text;

Expand All @@ -25,7 +28,7 @@ public function handle(): ?bool
{
parent::handle();

app(AbstractEnumGenerator::class)();
app(AbstractEnumGenerator::class, ['builder' => $this->builder()])();

$this->components->info("Abstract enum class has been rebuilt to include new [{$this->name()}] method.");

Expand Down Expand Up @@ -89,7 +92,9 @@ protected function buildClass($name): string
*/
protected function getPath($name): string
{
return resource_path(config()->string('paragon.enums.paths.methods')) . "/{$this->name()}.ts";
$extension = $this->option('javascript') ? 'js' : 'ts';

return resource_path(config()->string('paragon.enums.paths.methods')) . "/{$this->name()}.{$extension}";
}

/**
Expand All @@ -107,4 +112,29 @@ protected function name(): string

throw new Exception('[name] argument is not a string.');
}

protected function builder(): EnumBuilder
{
return $this->option('javascript')
? app(EnumJsBuilder::class)
: app(EnumTsBuilder::class);

}

/**
* Get the console command options.
*
* @return array<int, InputOption>
*/
protected function getOptions(): array
{
return [
new InputOption(
name: 'javascript',
shortcut: 'j',
mode: InputOption::VALUE_NONE,
description: 'Output Javascript files',
),
];
}
}
35 changes: 35 additions & 0 deletions src/Concerns/Builders/EnumBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Kirschbaum\Paragon\Concerns\Builders;

use ReflectionEnumBackedCase;
use ReflectionEnumUnitCase;
use ReflectionMethod;

interface EnumBuilder
{
/**
* Get the path to the stub.
*/
public function stubPath(): string;

/**
* Get the path to the abstract stub.
*/
public function abstractStubPath(): string;

/**
* File extension.
*/
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|ReflectionEnumBackedCase $case): string;

/**
* Assemble the static getter method code for the enum case object.
*/
public function assembleCaseGetter(ReflectionEnumUnitCase|ReflectionEnumBackedCase $case): string;
}
64 changes: 64 additions & 0 deletions src/Concerns/Builders/EnumJsBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

namespace Kirschbaum\Paragon\Concerns\Builders;

use BackedEnum;
use ReflectionEnumUnitCase;
use ReflectionMethod;

class EnumJsBuilder implements EnumBuilder
{
/**
* Get the path to the stub.
*/
public function stubPath(): string
{
return __DIR__ . '/../../../stubs/enum-js.stub';
}

/**
* Get the path to the abstract stub.
*/
public function abstractStubPath(): string
{
return __DIR__ . '/../../../stubs/abstract-enum-js.stub';
}

/**
* File extension.
*/
public function fileExtension(): string
{
return '.js';
}

/**
* Prepare the method and its respective values so it can get injected into the case object.
*/
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $case): string
{
$value = $case->getValue()->{$method->getName()}();
$class = class_basename($method->getDeclaringClass()->getName());

return str(PHP_EOL . " {$method->getName()}: () ")
->append(match (true) {
$value instanceof BackedEnum => "=> {$class}.{$value->name}",
is_numeric($value) => "=> {$value}",
is_null($value) => '=> null',
default => "=> '{$value}'"
})
->append(',');
}

/**
* Assemble the static getter method code for the enum case object.
*/
public function assembleCaseGetter(ReflectionEnumUnitCase $case): string
{
return <<<JS
static get {$case->name}() {
return this.items['{$case->name}'];
}
JS;
}
}
66 changes: 66 additions & 0 deletions src/Concerns/Builders/EnumTsBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace Kirschbaum\Paragon\Concerns\Builders;

use BackedEnum;
use ReflectionEnumUnitCase;
use ReflectionMethod;

class EnumTsBuilder implements EnumBuilder
{
/**
* Get the path to the stub.
*/
public function stubPath(): string
{
return __DIR__ . '/../../../stubs/enum.stub';
}

/**
* Get the path to the stub.
*/
public function abstractStubPath(): string
{
return __DIR__ . '/../../../stubs/abstract-enum.stub';
}

/**
* File extension.
*/
public function fileExtension(): string
{
return '.ts';
}

/**
* Prepare the method and its respective values so it can get injected into the case object.
*/
public function caseMethod(ReflectionMethod $method, ReflectionEnumUnitCase $case): string
{
$value = $case->getValue()->{$method->getName()}();
$class = class_basename($method->getDeclaringClass()->getName());

return str(PHP_EOL . " {$method->getName()}: (): ")
->append(match (true) {
$value instanceof BackedEnum => "object => {$class}.{$value->name}",
is_numeric($value) => "number => {$value}",
is_null($value) => 'null => null',
default => "string => '{$value}'"
})
->append(',');
}

/**
* Assemble the static getter method code for the enum case object.
*/
public function assembleCaseGetter(ReflectionEnumUnitCase $case): string
{
$class = class_basename($case->getDeclaringClass()->name);

return <<<JS
public static get {$case->name}(): {$class}Definition {
return this.items['{$case->name}'];
}
JS;
}
}
15 changes: 4 additions & 11 deletions src/Generators/AbstractEnumGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Kirschbaum\Paragon\Concerns\Builders\EnumBuilder;
use SplFileInfo;
use Symfony\Component\Filesystem\Filesystem as FileUtility;
use Symfony\Component\Finder\Exception\DirectoryNotFoundException;
Expand All @@ -14,7 +15,7 @@ class AbstractEnumGenerator
{
protected Filesystem $files;

public function __construct()
public function __construct(protected EnumBuilder $builder)
{
$this->files = Storage::createLocalDriver([
'root' => resource_path(config()->string('paragon.enums.paths.generated')),
Expand All @@ -34,20 +35,12 @@ protected function contents(): string
$imports = $this->imports();
$suffix = $imports->count() ? PHP_EOL : '';

return str((string) file_get_contents($this->stubPath()))
return str((string) file_get_contents($this->builder->abstractStubPath()))
->replace('{{ Abstract }}', config()->string('paragon.enums.abstract-class'))
->replace('{{ Imports }}', "{$imports->join('')}{$suffix}")
->replace('{{ Methods }}', "{$this->methods($imports->keys())}{$suffix}");
}

/**
* Get the path to the stubs.
*/
protected function stubPath(): string
{
return __DIR__ . '/../../stubs/abstract-enum.stub';
}

/**
* Build out the actual enum case object including the name, value if needed, and any public methods.
*
Expand Down Expand Up @@ -100,6 +93,6 @@ protected function methods(Collection $methods): string
*/
protected function path(): string
{
return config('paragon.enums.abstract-class') . '.ts';
return config('paragon.enums.abstract-class') . $this->builder->fileExtension();
}
}
Loading
Loading