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

Feat: additional --use-enums flag #78

Merged
merged 3 commits into from
Jul 9, 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
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public function getFirstNameAttribute(): string // <- this
--model= : Generate typescript interfaces for a specific model
--global : Generate typescript interfaces in a global namespace named models
--json : Output the result as json
--use-enums : Use typescript enums instead of object literals
--plurals : Output model plurals
--no-relations : Do not include relations
--optional-relations : Make relations optional fields on the model type
Expand Down Expand Up @@ -378,6 +379,6 @@ export interface User {
}
```

> ModelTyper uses Object Literals instead of TS Enums [for opinionated reasons](https://maxheiber.medium.com/alternatives-to-typescript-enums-50e4c16600b1)
> ModelTyper uses Object Literals by default instead of TS Enums [for opinionated reasons](https://maxheiber.medium.com/alternatives-to-typescript-enums-50e4c16600b1). But you can use `--use-enums` option to use TS Enums instead of Object Literals.

> Notice how the comments are found and parsed - they must follow the specified format
16 changes: 8 additions & 8 deletions src/Actions/GenerateCliOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GenerateCliOutput
* @param Collection<int, SplFileInfo> $models
* @param array<string, string> $mappings
*/
public function __invoke(Collection $models, array $mappings, bool $global = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable'): string
public function __invoke(Collection $models, array $mappings, bool $global = false, bool $useEnums = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable'): string
{
$modelBuilder = app(BuildModelDetails::class);
$colAttrWriter = app(WriteColumnAttribute::class);
Expand All @@ -45,7 +45,7 @@ public function __invoke(Collection $models, array $mappings, bool $global = fal
$this->indent = ' ';
}

$models->each(function (SplFileInfo $model) use ($mappings, $modelBuilder, $colAttrWriter, $relationWriter, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $optionalNullables, $resolveAbstract, $fillables, $fillableSuffix) {
$models->each(function (SplFileInfo $model) use ($mappings, $modelBuilder, $colAttrWriter, $relationWriter, $plurals, $apiResources, $optionalRelations, $noRelations, $noHidden, $optionalNullables, $resolveAbstract, $fillables, $fillableSuffix, $useEnums) {
$entry = '';
$modelDetails = $modelBuilder($model, $resolveAbstract);

Expand All @@ -70,8 +70,8 @@ public function __invoke(Collection $models, array $mappings, bool $global = fal

if ($columns->isNotEmpty()) {
$entry .= "{$this->indent} // columns\n";
$columns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $optionalNullables, $mappings) {
[$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, mappings: $mappings, indent: $this->indent, noHidden: $noHidden, optionalNullables: $optionalNullables);
$columns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $optionalNullables, $mappings, $useEnums) {
[$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, mappings: $mappings, indent: $this->indent, noHidden: $noHidden, optionalNullables: $optionalNullables, useEnums: $useEnums);
if (! empty($line)) {
$entry .= $line;
if ($enum) {
Expand All @@ -83,8 +83,8 @@ public function __invoke(Collection $models, array $mappings, bool $global = fal

if ($nonColumns->isNotEmpty()) {
$entry .= "{$this->indent} // mutators\n";
$nonColumns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $optionalNullables, $mappings) {
[$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, mappings: $mappings, indent: $this->indent, noHidden: $noHidden, optionalNullables: $optionalNullables);
$nonColumns->each(function ($att) use (&$entry, $reflectionModel, $colAttrWriter, $noHidden, $optionalNullables, $mappings, $useEnums) {
[$line, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, attribute: $att, mappings: $mappings, indent: $this->indent, noHidden: $noHidden, optionalNullables: $optionalNullables, useEnums: $useEnums);
if (! empty($line)) {
$entry .= $line;
if ($enum) {
Expand Down Expand Up @@ -139,8 +139,8 @@ public function __invoke(Collection $models, array $mappings, bool $global = fal

collect($this->enumReflectors)
->unique(fn (ReflectionClass $reflector) => $reflector->getName())
->each(function (ReflectionClass $reflector) {
$this->output .= app(WriteEnumConst::class)($reflector, $this->indent);
->each(function (ReflectionClass $reflector) use ($useEnums) {
$this->output .= app(WriteEnumConst::class)($reflector, $this->indent, false, $useEnums);
});

collect($this->imports)
Expand Down
12 changes: 6 additions & 6 deletions src/Actions/GenerateJsonOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class GenerateJsonOutput
* @param Collection<int, \Symfony\Component\Finder\SplFileInfo> $models
* @param array<string, string> $mappings
*/
public function __invoke(Collection $models, array $mappings, bool $resolveAbstract = false): string
public function __invoke(Collection $models, array $mappings, bool $resolveAbstract = false, bool $useEnums = false): string
{
$modelBuilder = app(BuildModelDetails::class);
$colAttrWriter = app(WriteColumnAttribute::class);
$relationWriter = app(WriteRelationship::class);
$enumWriter = app(WriteEnumConst::class);

$models->each(function (SplFileInfo $model) use ($modelBuilder, $colAttrWriter, $relationWriter, $resolveAbstract, $mappings) {
$models->each(function (SplFileInfo $model) use ($modelBuilder, $colAttrWriter, $relationWriter, $resolveAbstract, $mappings, $useEnums) {
$modelDetails = $modelBuilder($model, $resolveAbstract);

if ($modelDetails === null) {
Expand All @@ -56,8 +56,8 @@ public function __invoke(Collection $models, array $mappings, bool $resolveAbstr
$this->output['interfaces'][$name] = $columns
->merge($nonColumns)
->merge($interfaces)
->map(function ($att) use ($reflectionModel, $colAttrWriter, $mappings) {
[$property, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, mappings: $mappings, attribute: $att, jsonOutput: true);
->map(function ($att) use ($reflectionModel, $colAttrWriter, $mappings, $useEnums) {
[$property, $enum] = $colAttrWriter(reflectionModel: $reflectionModel, mappings: $mappings, attribute: $att, jsonOutput: true, useEnums: $useEnums);
if ($enum) {
$this->enumReflectors[] = $enum;
}
Expand All @@ -77,8 +77,8 @@ public function __invoke(Collection $models, array $mappings, bool $resolveAbstr
});
});

$this->output['enums'] = collect($this->enumReflectors)->map(function ($enum) use ($enumWriter) {
$enumConst = $enumWriter(reflection: $enum, jsonOutput: true);
$this->output['enums'] = collect($this->enumReflectors)->map(function ($enum) use ($enumWriter, $useEnums) {
$enumConst = $enumWriter(reflection: $enum, jsonOutput: true, useEnums: $useEnums);

return [
$enumConst['name'] => [
Expand Down
8 changes: 5 additions & 3 deletions src/Actions/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Generator
*
* @return string
*/
public function __invoke(?string $specificModel = null, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable')
public function __invoke(?string $specificModel = null, bool $global = false, bool $json = false, bool $useEnums = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable')
{
$models = app(GetModels::class)($specificModel);

Expand All @@ -33,6 +33,7 @@ public function __invoke(?string $specificModel = null, bool $global = false, bo
timestampsDate: $timestampsDate,
optionalNullables: $optionalNullables,
resolveAbstract: $resolveAbstract,
useEnums: $useEnums,
fillables: $fillables,
fillableSuffix: $fillableSuffix
);
Expand All @@ -43,18 +44,19 @@ public function __invoke(?string $specificModel = null, bool $global = false, bo
*
* @param Collection<int, \Symfony\Component\Finder\SplFileInfo> $models
*/
protected function display(Collection $models, bool $global = false, bool $json = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable'): string
protected function display(Collection $models, bool $global = false, bool $json = false, bool $useEnums = false, bool $plurals = false, bool $apiResources = false, bool $optionalRelations = false, bool $noRelations = false, bool $noHidden = false, bool $timestampsDate = false, bool $optionalNullables = false, bool $resolveAbstract = false, bool $fillables = false, string $fillableSuffix = 'Fillable'): string
{
$mappings = app(GetMappings::class)(setTimestampsToDate: $timestampsDate);

if ($json) {
return app(GenerateJsonOutput::class)(models: $models, mappings: $mappings, resolveAbstract: $resolveAbstract);
return app(GenerateJsonOutput::class)(models: $models, mappings: $mappings, resolveAbstract: $resolveAbstract, useEnums: $useEnums);
}

return app(GenerateCliOutput::class)(
models: $models,
mappings: $mappings,
global: $global,
useEnums: $useEnums,
plurals: $plurals,
apiResources: $apiResources,
optionalRelations: $optionalRelations,
Expand Down
8 changes: 6 additions & 2 deletions src/Actions/WriteColumnAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class WriteColumnAttribute
* @param array<string, string> $mappings
* @return array{array{name: string, type: string}, ReflectionClass|null}|array{string, ReflectionClass|null}|array{null, null}
*/
public function __invoke(ReflectionClass $reflectionModel, array $attribute, array $mappings, string $indent = '', bool $jsonOutput = false, bool $noHidden = false, bool $optionalNullables = false): array
public function __invoke(ReflectionClass $reflectionModel, array $attribute, array $mappings, string $indent = '', bool $jsonOutput = false, bool $noHidden = false, bool $optionalNullables = false, bool $useEnums = false): array
{
$enumRef = null;
$returnType = app(MapReturnType::class);
Expand Down Expand Up @@ -98,6 +98,10 @@ public function __invoke(ReflectionClass $reflectionModel, array $attribute, arr
}
}

if ($useEnums) {
$type = $enumRef && $type ? ($type . 'Enum') : $type;
}

if ($attribute['nullable']) {
$type .= '|null';
}
Expand Down Expand Up @@ -139,7 +143,7 @@ protected function resolveEnum(string $returnTypeName): ?ReflectionClass
private function ensurePropertyIsValid(string $identifier): string
{
$firstCharacter = substr($identifier, 0, 1);

if (! ctype_digit($identifier) && ctype_digit($firstCharacter)) {
return "'$identifier'";
}
Expand Down
26 changes: 20 additions & 6 deletions src/Actions/WriteEnumConst.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class WriteEnumConst
*
* @return array{type: string, name: string}|string
*/
public function __invoke(ReflectionClass $reflection, string $indent = '', bool $jsonOutput = false): array|string
public function __invoke(ReflectionClass $reflection, string $indent = '', bool $jsonOutput = false, bool $useEnums = false): array|string
{
$entry = '';

Expand All @@ -28,9 +28,13 @@ public function __invoke(ReflectionClass $reflection, string $indent = '', bool
$cases = collect($reflection->getConstants());

if ($cases->isNotEmpty()) {
$entry .= "{$indent}const {$reflection->getShortName()} = {\n";
if ($useEnums) {
$entry .= "{$indent}export const enum {$reflection->getShortName()} {\n";
} else {
$entry .= "{$indent}const {$reflection->getShortName()} = {\n";
}

$cases->each(function ($case) use ($indent, &$entry, $comments) {
$cases->each(function ($case) use ($indent, &$entry, $comments, $useEnums) {
$name = $case->name;
$value = is_string($case->value) ? "'{$case->value}'" : $case->value;

Expand All @@ -47,11 +51,21 @@ public function __invoke(ReflectionClass $reflection, string $indent = '', bool
}
}

$entry .= "{$indent} {$name}: {$value},\n";
if ($useEnums) {
$entry .= "{$indent} {$name} = {$value},\n";
} else {
$entry .= "{$indent} {$name}: {$value},\n";
}
});

$entry .= "{$indent}} as const;\n\n";
$entry .= "{$indent}export type {$reflection->getShortName()} = typeof {$reflection->getShortName()}[keyof typeof {$reflection->getShortName()}]\n\n";
if ($useEnums) {
$entry .= "{$indent}}\n\n";
$entry .= "{$indent}export type {$reflection->getShortName()}Enum = `\${{$reflection->getShortName()}}`\n\n";
} else {
$entry .= "{$indent}} as const;\n\n";
$entry .= "{$indent}export type {$reflection->getShortName()} = typeof {$reflection->getShortName()}[keyof typeof {$reflection->getShortName()}]\n\n";
}

}

if ($jsonOutput) {
Expand Down
2 changes: 2 additions & 0 deletions src/Commands/ModelTyperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ModelTyperCommand extends Command
{--model= : Generate typescript interfaces for a specific model}
{--global : Generate typescript interfaces in a global namespace named models}
{--json : Output the result as json}
{--use-enums : Use typescript enums instead of object literals}
{--plurals : Output model plurals}
{--no-relations : Do not include relations}
{--optional-relations : Make relations optional fields on the model type}
Expand Down Expand Up @@ -65,6 +66,7 @@ public function handle(Generator $generator): int
$this->option('model'),
$this->option('global'),
$this->option('json'),
$this->option('use-enums'),
$this->option('plurals') || $this->option('all'),
$this->option('api-resources') || $this->option('all'),
$this->option('optional-relations'),
Expand Down