Skip to content

Commit 38fdafc

Browse files
committed
chore: attended phpstan errors, deprectaed older enums
1 parent 73fa2ec commit 38fdafc

1 file changed

Lines changed: 132 additions & 59 deletions

File tree

src/TemplateGenerator/TypeScriptGenerator.php

Lines changed: 132 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22

33
namespace Html\TemplateGenerator;
44

5+
use BackedEnum;
56
use Html\Interface\HTMLElementDelegatorInterface;
67
use Html\Interface\TemplateGeneratorInterface;
78
use Html\Mapping\TemplateGenerator;
89
use ReflectionClass;
10+
use ReflectionMethod;
911
use ReflectionNamedType;
12+
use ReflectionProperty;
1013
use ReflectionUnionType;
14+
use Throwable;
15+
use UnitEnum;
1116

1217
/**
1318
* TypeScriptGenerator - Generates Pure TypeScript Classes
@@ -116,7 +121,7 @@ public function renderComposedElement(HTMLElementDelegatorInterface $element): ?
116121
$lines = explode("\n", $docComment);
117122
foreach ($lines as $line) {
118123
$line = trim($line, " \t/*");
119-
if (str_starts_with($line, '*') && !str_starts_with($line, '*/') && !str_starts_with($line, '* @')) {
124+
if (str_starts_with($line, '*') && ! str_starts_with($line, '*/') && ! str_starts_with($line, '* @')) {
120125
$desc = trim(substr($line, 1));
121126
break;
122127
}
@@ -143,7 +148,7 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
143148
$lines = explode("\n", $docComment);
144149
foreach ($lines as $line) {
145150
$line = trim($line, " \t/*");
146-
if (str_starts_with($line, '*') && !str_starts_with($line, '*/') && !str_starts_with($line, '* @')) {
151+
if (str_starts_with($line, '*') && ! str_starts_with($line, '*/') && ! str_starts_with($line, '* @')) {
147152
$desc = trim(substr($line, 1));
148153
break;
149154
}
@@ -157,7 +162,7 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
157162
$enumImports = [];
158163

159164
// Add children for non-self-closing elements
160-
if (!$isSelfClosing) {
165+
if (! $isSelfClosing) {
161166
$props['children'] = [
162167
'type' => 'string | HTMLElement | (string | HTMLElement)[]',
163168
'description' => 'Child content or elements',
@@ -170,7 +175,7 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
170175
$example = null;
171176
try {
172177
$example = $ref->newInstance();
173-
} catch (\Throwable $e) {
178+
} catch (Throwable $e) {
174179
// Ignore if we can't create an instance
175180
}
176181

@@ -215,7 +220,9 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
215220
foreach ($propType->getTypes() as $unionType) {
216221
if (str_ends_with($unionType->getName(), 'Enum')) {
217222
$isEnum = true;
218-
$enumClass = basename(str_replace('\\', '/', $unionType->getName())); // Extract just the enum name
223+
$enumClass = basename(
224+
str_replace('\\', '/', $unionType->getName())
225+
); // Extract just the enum name
219226
break;
220227
}
221228
}
@@ -242,8 +249,12 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
242249
$reserved = ['children'];
243250
$sortedKeys = array_keys($props);
244251
usort($sortedKeys, function ($a, $b) use ($reserved) {
245-
if (in_array($a, $reserved)) return -1;
246-
if (in_array($b, $reserved)) return 1;
252+
if (in_array($a, $reserved)) {
253+
return -1;
254+
}
255+
if (in_array($b, $reserved)) {
256+
return 1;
257+
}
247258
return strcasecmp($a, $b);
248259
});
249260

@@ -252,14 +263,7 @@ public function renderElement(HTMLElementDelegatorInterface $element): string
252263
$sortedProps[$key] = $props[$key];
253264
}
254265

255-
$ts = $this->buildTypeScriptClass(
256-
$elementName,
257-
$name,
258-
$desc,
259-
$sortedProps,
260-
$enumImports,
261-
$isSelfClosing
262-
);
266+
$ts = $this->buildTypeScriptClass($elementName, $name, $desc, $sortedProps, $enumImports, $isSelfClosing);
263267

264268
return $ts;
265269
}
@@ -269,13 +273,13 @@ private function camelToKebab(string $string): string
269273
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $string));
270274
}
271275

272-
private function getPropertyType(\ReflectionProperty $prop, \ReflectionMethod $getter): string
276+
private function getPropertyType(ReflectionProperty $prop, ReflectionMethod $getter): string
273277
{
274278
$type = $prop->getType();
275279
if ($type instanceof ReflectionNamedType) {
276280
$typeName = $type->getName();
277281
$allowsNull = $type->allowsNull();
278-
282+
279283
if ($typeName === 'string') {
280284
return $allowsNull ? 'string | null | undefined' : 'string | undefined';
281285
} elseif ($typeName === 'int') {
@@ -290,11 +294,14 @@ private function getPropertyType(\ReflectionProperty $prop, \ReflectionMethod $g
290294
if (enum_exists($fullClassName)) {
291295
try {
292296
$cases = $fullClassName::cases();
293-
$values = array_map(fn(\UnitEnum $case) => $case instanceof \BackedEnum ? $case->value : $case->name, $cases);
297+
$values = array_map(
298+
fn (UnitEnum $case) => $case instanceof BackedEnum ? $case->value : $case->name,
299+
$cases
300+
);
294301
if (in_array('true', $values) && in_array('false', $values)) {
295302
$enumType .= ' | boolean';
296303
}
297-
} catch (\Throwable $e) {
304+
} catch (Throwable $e) {
298305
// Ignore
299306
}
300307
}
@@ -317,11 +324,14 @@ private function getPropertyType(\ReflectionProperty $prop, \ReflectionMethod $g
317324
if (enum_exists($fullClassName)) {
318325
try {
319326
$cases = $fullClassName::cases();
320-
$values = array_map(fn(\UnitEnum $case) => $case instanceof \BackedEnum ? $case->value : $case->name, $cases);
327+
$values = array_map(
328+
fn (UnitEnum $case) => $case instanceof BackedEnum ? $case->value : $case->name,
329+
$cases
330+
);
321331
if (in_array('true', $values) && in_array('false', $values)) {
322332
$enumType .= ' | boolean';
323333
}
324-
} catch (\Throwable $e) {
334+
} catch (Throwable $e) {
325335
// Ignore
326336
}
327337
}
@@ -342,7 +352,9 @@ private function getTypeName(string $phpType): string
342352
'string' => 'string',
343353
'int' => 'number',
344354
'bool' => 'boolean',
345-
default => str_ends_with($phpType, 'Enum') ? $this->getEnumType(basename(str_replace('\\', '/', $phpType))) : 'any',
355+
default => str_ends_with($phpType, 'Enum') ? $this->getEnumType(
356+
basename(str_replace('\\', '/', $phpType))
357+
) : 'any',
346358
};
347359
}
348360

@@ -353,16 +365,19 @@ private function getEnumType(string $enumClass): string
353365
if (enum_exists($fullClassName)) {
354366
try {
355367
$cases = $fullClassName::cases();
356-
$values = array_map(fn(\UnitEnum $case) => $case instanceof \BackedEnum ? "'{$case->value}'" : "'{$case->name}'", $cases);
368+
$values = array_map(
369+
fn (UnitEnum $case) => $case instanceof BackedEnum ? "'{$case->value}'" : "'{$case->name}'",
370+
$cases
371+
);
357372
return implode(' | ', $values);
358-
} catch (\Throwable $e) {
373+
} catch (Throwable $e) {
359374
// Fallback
360375
}
361376
}
362377
return 'string'; // Fallback to string if enum not found
363378
}
364379

365-
private function getPropertyDescription(\ReflectionProperty $prop): string
380+
private function getPropertyDescription(ReflectionProperty $prop): string
366381
{
367382
$docComment = $prop->getDocComment();
368383
if ($docComment !== false) {
@@ -375,7 +390,7 @@ private function getPropertyDescription(\ReflectionProperty $prop): string
375390
$line = trim($line);
376391
if (str_starts_with($line, '*')) {
377392
$line = trim(substr($line, 1));
378-
if (!str_starts_with($line, '@') && !empty($line)) {
393+
if (! str_starts_with($line, '@') && ! empty($line)) {
379394
$description .= $line . ' ';
380395
}
381396
}
@@ -392,25 +407,82 @@ private function getGlobalAttributeTraits(object $element): array
392407

393408
// Check which global attribute traits are used
394409
$globalTraitClasses = [
395-
'AccesskeyTrait' => ['type' => 'string', 'description' => 'Keyboard shortcut to activate or focus the element'],
396-
'AutocapitalizeTrait' => ['type' => 'string', 'description' => 'Controls automatic capitalization'],
397-
'AutofocusTrait' => ['type' => 'boolean', 'description' => 'Automatically focus the element when the page loads'],
398-
'ClassTrait' => ['type' => 'string', 'description' => 'CSS class names'],
399-
'ContenteditableTrait' => ['type' => 'boolean | "true" | "false" | "inherit"', 'description' => 'Whether the element is editable'],
400-
'DataTrait' => ['type' => 'Record<string, string>', 'description' => 'Custom data attributes'],
401-
'DirTrait' => ['type' => '"ltr" | "rtl" | "auto"', 'description' => 'Text direction'],
402-
'DraggableTrait' => ['type' => 'boolean', 'description' => 'Whether the element can be dragged'],
403-
'HiddenTrait' => ['type' => 'boolean', 'description' => 'Whether the element is hidden'],
404-
'IdTrait' => ['type' => 'string', 'description' => 'Unique identifier'],
405-
'InputmodeTrait' => ['type' => 'string', 'description' => 'Type of virtual keyboard to show'],
406-
'LangTrait' => ['type' => 'string', 'description' => 'Language of the element'],
407-
'PopoverTrait' => ['type' => 'string', 'description' => 'Controls popover behavior'],
408-
'SlotTrait' => ['type' => 'string', 'description' => 'Shadow DOM slot name'],
409-
'SpellcheckTrait' => ['type' => 'boolean', 'description' => 'Whether to check spelling'],
410-
'StyleTrait' => ['type' => 'string | Partial<CSSStyleDeclaration>', 'description' => 'Inline CSS styles'],
411-
'TabindexTrait' => ['type' => 'number', 'description' => 'Tab order position'],
412-
'TitleTrait' => ['type' => 'string', 'description' => 'Tooltip text'],
413-
'TranslateTrait' => ['type' => 'boolean', 'description' => 'Whether the element should be translated'],
410+
'AccesskeyTrait' => [
411+
'type' => 'string',
412+
'description' => 'Keyboard shortcut to activate or focus the element',
413+
],
414+
'AutocapitalizeTrait' => [
415+
'type' => 'string',
416+
'description' => 'Controls automatic capitalization',
417+
],
418+
'AutofocusTrait' => [
419+
'type' => 'boolean',
420+
'description' => 'Automatically focus the element when the page loads',
421+
],
422+
'ClassTrait' => [
423+
'type' => 'string',
424+
'description' => 'CSS class names',
425+
],
426+
'ContenteditableTrait' => [
427+
'type' => 'boolean | "true" | "false" | "inherit"',
428+
'description' => 'Whether the element is editable',
429+
],
430+
'DataTrait' => [
431+
'type' => 'Record<string, string>',
432+
'description' => 'Custom data attributes',
433+
],
434+
'DirTrait' => [
435+
'type' => '"ltr" | "rtl" | "auto"',
436+
'description' => 'Text direction',
437+
],
438+
'DraggableTrait' => [
439+
'type' => 'boolean',
440+
'description' => 'Whether the element can be dragged',
441+
],
442+
'HiddenTrait' => [
443+
'type' => 'boolean',
444+
'description' => 'Whether the element is hidden',
445+
],
446+
'IdTrait' => [
447+
'type' => 'string',
448+
'description' => 'Unique identifier',
449+
],
450+
'InputmodeTrait' => [
451+
'type' => 'string',
452+
'description' => 'Type of virtual keyboard to show',
453+
],
454+
'LangTrait' => [
455+
'type' => 'string',
456+
'description' => 'Language of the element',
457+
],
458+
'PopoverTrait' => [
459+
'type' => 'string',
460+
'description' => 'Controls popover behavior',
461+
],
462+
'SlotTrait' => [
463+
'type' => 'string',
464+
'description' => 'Shadow DOM slot name',
465+
],
466+
'SpellcheckTrait' => [
467+
'type' => 'boolean',
468+
'description' => 'Whether to check spelling',
469+
],
470+
'StyleTrait' => [
471+
'type' => 'string | Partial<CSSStyleDeclaration>',
472+
'description' => 'Inline CSS styles',
473+
],
474+
'TabindexTrait' => [
475+
'type' => 'number',
476+
'description' => 'Tab order position',
477+
],
478+
'TitleTrait' => [
479+
'type' => 'string',
480+
'description' => 'Tooltip text',
481+
],
482+
'TranslateTrait' => [
483+
'type' => 'boolean',
484+
'description' => 'Whether the element should be translated',
485+
],
414486
];
415487

416488
foreach ($globalTraitClasses as $traitName => $traitInfo) {
@@ -475,12 +547,7 @@ private function buildTypeScriptClass(
475547
$ts .= " * @description {$description}\n";
476548
$ts .= " */\n\n";
477549

478-
// Import statements - none needed for now since we use inline union types
479-
// if (!empty($enumImports)) {
480-
// $ts .= "import { " . implode(', ', array_unique($enumImports)) . " } from './enums';\n\n";
481-
// }
482550

483-
// Interface definition
484551
$ts .= "export interface {$className}Props {\n";
485552
foreach ($props as $propName => $propData) {
486553
$optional = $propData['required'] ? '' : '?';
@@ -492,7 +559,7 @@ private function buildTypeScriptClass(
492559
}
493560
$ts .= "}\n\n";
494561

495-
// Class definition
562+
496563
$ts .= "/**\n";
497564
$ts .= " * {$className} - {$description}\n";
498565
$ts .= " */\n";
@@ -511,7 +578,7 @@ private function buildTypeScriptClass(
511578
$quotedPropName = $this->quotePropertyName($propName);
512579
$isQuoted = $quotedPropName !== $propName;
513580
$propAccess = $isQuoted ? "['{$propName}']" : ".{$propName}";
514-
581+
515582
if ($propName === 'children') {
516583
$ts .= " if (props{$propAccess} !== undefined) {\n";
517584
$ts .= " this.setChildren(props{$propAccess});\n";
@@ -564,7 +631,7 @@ private function buildTypeScriptClass(
564631
}
565632

566633
// setChildren method
567-
if (!$isSelfClosing) {
634+
if (! $isSelfClosing) {
568635
$ts .= " setChildren(children: string | HTMLElement | (string | HTMLElement)[]): this {\n";
569636
$ts .= " // Clear existing children\n";
570637
$ts .= " while (this.element.firstChild) {\n";
@@ -623,7 +690,7 @@ private function buildComposedTypeScript(
623690
$ts = "/**\n";
624691
$ts .= " * THIS FILE IS AUTOGENERATED. DO NOT EDIT IT.\n";
625692
$ts .= " *\n";
626-
$ts .= " * @generated " . date('F j, Y H:i:s') . "\n";
693+
$ts .= ' * @generated ' . date('F j, Y H:i:s') . "\n";
627694
$ts .= " * @component {$name}Example\n";
628695
$ts .= " * @description {$description} - Composed Example\n";
629696
$ts .= " */\n\n";
@@ -634,11 +701,17 @@ private function buildComposedTypeScript(
634701
$ts .= " * {$name}Example - Demonstrates valid parent-child relationships\n";
635702
$ts .= " *\n";
636703
$ts .= " * CONTENT MODEL:\n";
637-
if (!empty($childOf)) {
638-
$ts .= " * - Can be child of: " . implode(', ', array_map(function($c) { return basename(str_replace('\\', '/', $c)); }, $childOf)) . "\n";
704+
if (! empty($childOf)) {
705+
$ts .= ' * - Can be child of: ' . implode(
706+
', ',
707+
array_map(function ($c) { return basename(str_replace('\\', '/', $c)); }, $childOf)
708+
) . "\n";
639709
}
640-
if (!empty($parentOf)) {
641-
$ts .= " * - Can contain: " . implode(', ', array_map(function($c) { return basename(str_replace('\\', '/', $c)); }, $parentOf)) . "\n";
710+
if (! empty($parentOf)) {
711+
$ts .= ' * - Can contain: ' . implode(
712+
', ',
713+
array_map(function ($c) { return basename(str_replace('\\', '/', $c)); }, $parentOf)
714+
) . "\n";
642715
}
643716
$ts .= " */\n";
644717
$ts .= "export class {$name}Example {\n";
@@ -665,4 +738,4 @@ private function determineLevel(string $className): string
665738
}
666739
return 'block';
667740
}
668-
}
741+
}

0 commit comments

Comments
 (0)