uxf/gen

Maintainers

Details

gitlab.com/uxf/gen

Source

Issues

Installs: 13 569

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Forks: 0


README

Install

$ composer req uxf/gen

Run

$ bin/console uxf:gen

Config

// config/routes/uxf.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return static function (RoutingConfigurator $routingConfigurator): void {
    $routingConfigurator->import('@UXFGenBundle/config/routes.php');
};
// config/packages/uxf.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $containerConfigurator->extension('uxf_gen', [
        'config' => [
            'prefix' => '/wow', // default '/api'
            // default config -> values from UXF bridge
            'disable_uxf_bridge' => false,
            'route_body_attribut' => FromBody::class,
            'route_query_attribute' => FromQuery::class,
            'route_header_attribute' => FromHeader::class,
            'route_entity_attribute' => Entity::class,
            'ignored_types' => [NotSet::class],
            'enum_as_union' => true, // default false
            'typescript_types' => [
                'DateTime' => '`${number}-${number}-${number}T${number}:${number}:${number}+${number}:${number}`',
                'Date' => '`${number}-${number}-${number}`',
                'Time' => '`${number}:${number}:${number}`',
            ],
        ],
        'open_api' => [
            'areas' => [
                'app' => [
                    'path_pattern' => '/^\/api\/article/',
                ],
            ],
        ],
        'hook' => [
            'areas' => [
                'admin' => [
                    'path_pattern' => '/^\/api\/article/',
                    'output' => 'swr', // default legacy (options: swr, legacy, empty)
                    'with_enum_options' => true, // default false
                    'destination' => '%kernel.project_dir%/tests/generated/hook.ts', // or array
                    'prepends' => [
                        '/* eslint-disable */',
                        'import { useAxiosRequest, axiosRequest, RequestConfig } from "@lib/api";'
                    ],
                ],
            ],
        ],
        'apollo' => [
            'areas' => [
                'app' => [
                    'path_pattern' => '/^\/api\/article/',
                    'destination' => '%kernel.project_dir%/tests/generated/apollo.ts', // or array
                ],
            ],
        ],
    ]);
};

Example

PersonRequestBody.php

InspectorPlugin

use UXF\Gen\Inspector\Schema\AppSchema;
use UXF\Gen\Inspector\Schema\RouteSchema;
use UXF\Gen\Inspector\Schema\TypeSchema;
use UXF\Gen\Inspector\Schema\TypeVariant;
use UXF\Gen\Inspector\TypeMap;
use UXF\Gen\Plugin\InspectorPluginInterface;
use UXF\GenTests\Project\FunZone\Entity\EntityNames;

class TestPlugin implements InspectorPluginInterface
{
    public function pre(string $configName, TypeMap $typeMap): void
    {
        if ($configName === 'hook') {
            $typeMap[EntityNames::class] = new TypeSchema(EntityNames::class, TypeVariant::ENUM, EntityNames::class);
        }
    }

    public function post(string $configName, AppSchema $appSchema): void
    {
        if ($configName === 'apollo') {
            $appSchema->routes['plugin'] = new RouteSchema(
                name: 'plugin',
                path: '/plugin',
                controller: 'FunZone\Ok',
                requestBody: null,
                requestQuery: null,
                pathParams: [],
                response: null,
                description: '',
                methods: ['get' => false],
            );
        }
    }
}

ConverterPlugin

use UXF\Core\Type\Date;
use UXF\Core\Type\DateTime;
use UXF\Core\Type\Time;
use UXF\Gen\Inspector\Schema\TypeSchema;
use UXF\Gen\Inspector\Schema\TypeVariant;
use UXF\Gen\Plugin\TypeConverterPlugin;

class UxfCoreTypeConverterPlugin implements TypeConverterPlugin
{
    public function convertToTypescript(TypeSchema $typeSchema): ?string
    {
        if (
            $typeSchema->variant === TypeVariant::SIMPLE &&
            in_array($typeSchema->name, [Date::class, DateTime::class, Time::class], true)
        ) {
            return 'string';
        }

        return null;
    }

    /**
     * @inheritDoc
     */
    public function convertToOpenApi(TypeSchema $typeSchema): ?array
    {
        if ($typeSchema->variant !== TypeVariant::SIMPLE) {
            return null;
        }

        return match ($typeSchema->name) {
            Date::class => [
                'type' => 'string',
                'format' => 'date',
            ],
            DateTime::class => [
                'type' => 'string',
                'format' => 'date-time',
            ],
            Time::class => [
                'type' => 'string',
                'pattern' => '^\d{2}:\d{2}:\d{2}$',
            ],
            default => null,
        };
    }
}

Description support

class TestController
{
    /**
     * PRIVATE INTERNAL DESCRIPTION (MUST BE BEFORE @description)
     *
     * @description Public hello world description
     * with multiline support bro!
     */
    public function __invoke(): StatusResponse
    {
        return new StatusResponse();
    }
}

Generic support (only for main response)

/**
 * @template T
 */
class GenericResponseBody
{
    /**
     * @param T $data
     */
    public function __construct(
        public readonly bool $success,
        public readonly mixed $data,
    ) {
    }
}

class GenericController
{
    /**
     * @return GenericResponseBody<TagResponse>
     */
    public function simple(): GenericResponseBody
    {
        ...
    }

    /**
     * @return GenericResponseBody<TagResponse[]>
     */
    public function array(): GenericResponseBody
    {
        ...
    }
}