Skip to content

Commit

Permalink
Switch to auto-generated enums from PHP in TypeScript.
Browse files Browse the repository at this point in the history
  • Loading branch information
BusterNeece committed Feb 26, 2025
1 parent 1370da5 commit 33a7968
Show file tree
Hide file tree
Showing 36 changed files with 525 additions and 155 deletions.
1 change: 1 addition & 0 deletions backend/config/cli.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
'azuracast:api:docs' => Command\Dev\GenerateApiDocsCommand::class,
'azuracast:new-version' => Command\Dev\NewVersionCommand::class,
'azuracast:dev:generate-db-fixture' => Command\Dev\GenerateDbFixtureCommand::class,
'azuracast:dev:ts' => Command\Dev\GenerateTypescriptClassesCommand::class,
'azuracast:locale:generate' => Command\Locale\GenerateCommand::class,
'azuracast:locale:import' => Command\Locale\ImportCommand::class,
'locale:generate' => Command\Locale\GenerateCommand::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace App\Console\Command\Dev;

use App\Console\Command\CommandAbstract;
use App\Container\EnvironmentAwareTrait;
use App\Container\LoggerAwareTrait;
use App\TypeScript\NativeJsEnumTransformer;
use App\TypeScript\NativeJsWriter;
use Spatie\TypeScriptTransformer\Formatters\PrettierFormatter;
use Spatie\TypeScriptTransformer\TypeScriptTransformer;
use Spatie\TypeScriptTransformer\TypeScriptTransformerConfig;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

#[AsCommand(
name: 'azuracast:dev:ts',
description: 'Trigger generation of TypeScript equivalents of PHP classes.',
)]
final class GenerateTypescriptClassesCommand extends CommandAbstract
{
use LoggerAwareTrait;
use EnvironmentAwareTrait;

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$backendBaseDir = $this->environment->getBackendDirectory();
$outputFile = $this->environment->getBaseDirectory() . '/frontend/entities/PhpClasses.ts';

$config = TypeScriptTransformerConfig::create()
->autoDiscoverTypes($backendBaseDir)
->transformers([
NativeJsEnumTransformer::class,
])
->transformToNativeEnums()
->writer(NativeJsWriter::class)
->formatter(PrettierFormatter::class)
->outputFile($outputFile);

TypeScriptTransformer::create($config)->transform();

$io->writeln('TypeScript classes generated!');
return 0;
}
}
3 changes: 3 additions & 0 deletions backend/src/Radio/Enums/AudioProcessingMethods.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace App\Radio\Enums;

use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum AudioProcessingMethods: string
{
case None = 'none';
Expand Down
2 changes: 2 additions & 0 deletions backend/src/Radio/Enums/BackendAdapters.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace App\Radio\Enums;

use App\Radio\Backend\Liquidsoap;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum BackendAdapters: string implements AdapterTypeInterface
{
case Liquidsoap = 'liquidsoap';
Expand Down
2 changes: 2 additions & 0 deletions backend/src/Radio/Enums/FrontendAdapters.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
use App\Radio\Frontend\Icecast;
use App\Radio\Frontend\Rsas;
use App\Radio\Frontend\Shoutcast;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum FrontendAdapters: string implements AdapterTypeInterface
{
case Icecast = 'icecast';
Expand Down
2 changes: 2 additions & 0 deletions backend/src/Radio/Enums/RemoteAdapters.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
use App\Radio\Remote\Icecast;
use App\Radio\Remote\Shoutcast1;
use App\Radio\Remote\Shoutcast2;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum RemoteAdapters: string implements AdapterTypeInterface
{
case Shoutcast1 = 'shoutcast1';
Expand Down
15 changes: 15 additions & 0 deletions backend/src/TypeScript/IgnoreCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace App\TypeScript;

use Attribute;

/**
* Ignore a specific enum case.
*/
#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
final class IgnoreCase
{
}
38 changes: 38 additions & 0 deletions backend/src/TypeScript/NativeJsEnumTransformer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace App\TypeScript;

use ReflectionEnum;
use ReflectionEnumBackedCase;
use Spatie\TypeScriptTransformer\Structures\TransformedType;
use Spatie\TypeScriptTransformer\Transformers\EnumTransformer;

final class NativeJsEnumTransformer extends EnumTransformer
{
protected function toEnum(ReflectionEnum $enum, string $name): TransformedType
{
/** @var ReflectionEnumBackedCase[] $enumCases */
$enumCases = $enum->getCases();

$options = array_filter(
array_map(
function (ReflectionEnumBackedCase $case): ?string {
$hiddenAttributes = $case->getAttributes(IgnoreCase::class);
return (empty($hiddenAttributes))
? "'{$case->getName()}': {$this->toEnumValue($case)}"
: null;
},
$enumCases
)
);

return TransformedType::create(
$enum,
$name,
'Object.freeze({ ' . implode(', ', $options) . ' } as const)',
keyword: 'const'
);
}
}
60 changes: 60 additions & 0 deletions backend/src/TypeScript/NativeJsWriter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace App\TypeScript;

use Spatie\TypeScriptTransformer\Structures\TransformedType;
use Spatie\TypeScriptTransformer\Structures\TypesCollection;
use Spatie\TypeScriptTransformer\Writers\ModuleWriter;

final class NativeJsWriter extends ModuleWriter
{
public function format(TypesCollection $collection): string
{
$output = <<<'TS'
/*
* This file is automatically generated by AzuraCast.
* To update it, run:
* `backend/bin/console azuracast:dev:ts`
*/


TS;

$iterator = $collection->getIterator();
$iterator->uasort(function (TransformedType $a, TransformedType $b) {
return strcmp($a->name ?? '', $b->name ?? '');
});

/** @var TransformedType $type */
foreach ($iterator as $type) {
if ($type->isInline) {
continue;
}

if ($type->keyword === 'const') {
$this->handleNativeJsEnum($type, $output);
} else {
$output .= "export {$type->toString()}" . PHP_EOL . PHP_EOL;
}
}

return $output;
}

protected function handleNativeJsEnum(TransformedType $type, string &$output): void
{
$output .= "export const {$type->name} = {$type->transformed}";
if ($type->trailingSemicolon) {
$output .= ';';
}
$output .= PHP_EOL;

$output .= "export type {$type->name}Enum = typeof {$type->name}[keyof typeof {$type->name}]";
if ($type->trailingSemicolon) {
$output .= ';';
}
$output .= PHP_EOL . PHP_EOL;
}
}
3 changes: 3 additions & 0 deletions backend/src/Webhook/Enums/WebhookTriggers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace App\Webhook\Enums;

use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum WebhookTriggers: string
{
case SongChanged = 'song_changed';
Expand Down
6 changes: 6 additions & 0 deletions backend/src/Webhook/Enums/WebhookTypes.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Webhook\Enums;

use App\TypeScript\IgnoreCase;
use App\Webhook\Connector\Bluesky;
use App\Webhook\Connector\Discord;
use App\Webhook\Connector\Email;
Expand All @@ -17,7 +18,9 @@
use App\Webhook\Connector\RadioReg;
use App\Webhook\Connector\Telegram;
use App\Webhook\Connector\TuneIn;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
enum WebhookTypes: string
{
case Generic = 'generic';
Expand All @@ -38,7 +41,10 @@ enum WebhookTypes: string
case MatomoAnalytics = 'matomo_analytics';

// Retired connectors
#[IgnoreCase]
case Twitter = 'twitter';

#[IgnoreCase]
case GoogleAnalyticsV3 = 'google_analytics';

/**
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"slim/http": "^1.1",
"slim/slim": "^4.2",
"spatie/flysystem-dropbox": "^3",
"spatie/typescript-transformer": "*",
"spomky-labs/otphp": "^11",
"supervisorphp/supervisor": "dev-main",
"symfony/cache": "^7",
Expand Down
Loading

0 comments on commit 33a7968

Please sign in to comment.