Repository: symfony/ux
Pull request #2357 (2024-11-10 04:39:28)

[TwigComponent] Store mount methods in compiler pass 
| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no 
| Issues        | Fix #...
| License       | MIT

(internal optimization)

* collection mount metadata (premount, mount, postmount) in  compiler pass
* store in ComponentMetadata object
* start removing methods from AsTwigComponent

Note: i had to update an internal test method in LiveComponent 

smnandre: Fabbot false positive, let's irgnore

Patch:
diff --git a/src/Map/CHANGELOG.md b/src/Map/CHANGELOG.md
index ef718fcb..3883fe05 100644
--- a/src/Map/CHANGELOG.md
+++ b/src/Map/CHANGELOG.md
@@ -1,9 +1,4 @@
 # CHANGELOG
- 
-## 2.22
-
--   Add method `Symfony\UX\Map\Renderer\AbstractRenderer::tapOptions()`, to allow Renderer to modify options before rendering a Map.
--   Add `ux_map.google_maps.default_map_id` configuration to set the Google ``Map ID``
 
 ## 2.20
 
diff --git a/src/Map/config/twig_component.php b/src/Map/config/twig_component.php
index 0a3ad404..096eec4a 100644
--- a/src/Map/config/twig_component.php
+++ b/src/Map/config/twig_component.php
@@ -12,6 +12,8 @@
 namespace Symfony\Component\DependencyInjection\Loader\Configurator;
 
 use Symfony\UX\Map\Twig\UXMapComponent;
+use Symfony\UX\Map\Twig\UXMapComponentListener;
+use Symfony\UX\TwigComponent\Event\PreCreateForRenderEvent;
 
 return static function (ContainerConfigurator $container): void {
     $container->services()
diff --git a/src/Map/src/Bridge/Google/CHANGELOG.md b/src/Map/src/Bridge/Google/CHANGELOG.md
index 48478b98..46f7d49c 100644
--- a/src/Map/src/Bridge/Google/CHANGELOG.md
+++ b/src/Map/src/Bridge/Google/CHANGELOG.md
@@ -1,11 +1,5 @@
 # CHANGELOG
 
-## 2.22
-
--   Add support for configuring a default Map ID
--   Add argument `$defaultMapId` to `Symfony\UX\Map\Bridge\Google\Renderer\GoogleRendererFactory` constructor
--   Add argument `$defaultMapId` to `Symfony\UX\Map\Bridge\Google\Renderer\GoogleRenderer` constructor
-
 ## 2.20
 
 ### BC Breaks
diff --git a/src/Map/src/Bridge/Google/src/GoogleOptions.php b/src/Map/src/Bridge/Google/src/GoogleOptions.php
index a241daa8..8b26efcf 100644
--- a/src/Map/src/Bridge/Google/src/GoogleOptions.php
+++ b/src/Map/src/Bridge/Google/src/GoogleOptions.php
@@ -46,11 +46,6 @@ final class GoogleOptions implements MapOptionsInterface
         return $this;
     }
 
-    public function hasMapId(): bool
-    {
-        return null !== $this->mapId;
-    }
-
     public function gestureHandling(GestureHandling $gestureHandling): self
     {
         $this->gestureHandling = $gestureHandling;
diff --git a/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php b/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php
index cb325ba0..808278ab 100644
--- a/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php
+++ b/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php
@@ -41,7 +41,6 @@ final readonly class GoogleRenderer extends AbstractRenderer
          * @var array<'core'|'maps'|'places'|'geocoding'|'routes'|'marker'|'geometry'|'elevation'|'streetView'|'journeySharing'|'drawing'|'visualization'>
          */
         private array $libraries = [],
-        private ?string $defaultMapId = null,
     ) {
         parent::__construct($stimulusHelper);
     }
@@ -67,20 +66,7 @@ final readonly class GoogleRenderer extends AbstractRenderer
 
     protected function getDefaultMapOptions(): MapOptionsInterface
     {
-        return new GoogleOptions(mapId: $this->defaultMapId);
-    }
-
-    protected function tapOptions(MapOptionsInterface $options): MapOptionsInterface
-    {
-        if (!$options instanceof GoogleOptions) {
-            throw new \InvalidArgumentException(\sprintf('The options must be an instance of "%s", got "%s" instead.', GoogleOptions::class, get_debug_type($options)));
-        }
-
-        if (!$options->hasMapId()) {
-            $options->mapId($this->defaultMapId);
-        }
-
-        return $options;
+        return new GoogleOptions();
     }
 
     public function __toString(): string
diff --git a/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php b/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php
index e3495634..a391f676 100644
--- a/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php
+++ b/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php
@@ -17,20 +17,12 @@ use Symfony\UX\Map\Renderer\AbstractRendererFactory;
 use Symfony\UX\Map\Renderer\Dsn;
 use Symfony\UX\Map\Renderer\RendererFactoryInterface;
 use Symfony\UX\Map\Renderer\RendererInterface;
-use Symfony\UX\StimulusBundle\Helper\StimulusHelper;
 
 /**
  * @author Hugo Alliaume <hugo@alliau.me>
  */
 final class GoogleRendererFactory extends AbstractRendererFactory implements RendererFactoryInterface
 {
-    public function __construct(
-        StimulusHelper $stimulus,
-        private ?string $defaultMapId = null,
-    ) {
-        parent::__construct($stimulus);
-    }
-
     public function create(Dsn $dsn): RendererInterface
     {
         if (!$this->supports($dsn)) {
@@ -50,7 +42,6 @@ final class GoogleRendererFactory extends AbstractRendererFactory implements Ren
             url: $dsn->getOption('url'),
             version: $dsn->getOption('version', 'weekly'),
             libraries: ['maps', 'marker', ...$dsn->getOption('libraries', [])],
-            defaultMapId: $this->defaultMapId,
         );
     }
 
diff --git a/src/Map/src/Renderer/AbstractRenderer.php b/src/Map/src/Renderer/AbstractRenderer.php
index 4471f9be..aa229009 100644
--- a/src/Map/src/Renderer/AbstractRenderer.php
+++ b/src/Map/src/Renderer/AbstractRenderer.php
@@ -31,18 +31,6 @@ abstract readonly class AbstractRenderer implements RendererInterface
 
     abstract protected function getDefaultMapOptions(): MapOptionsInterface;
 
-    /**
-     * @template T of MapOptionsInterface
-     *
-     * @param T $options
-     *
-     * @return T
-     */
-    protected function tapOptions(MapOptionsInterface $options): MapOptionsInterface
-    {
-        return $options;
-    }
-
     final public function renderMap(Map $map, array $attributes = []): string
     {
         if (!$map->hasOptions()) {
@@ -51,8 +39,6 @@ abstract readonly class AbstractRenderer implements RendererInterface
             $map->options($defaultMapOptions);
         }
 
-        $map->options($this->tapOptions($map->getOptions()));
-
         $controllers = [];
         if ($attributes['data-controller'] ?? null) {
             $controllers[$attributes['data-controller']] = [];
diff --git a/src/Map/src/UXMapBundle.php b/src/Map/src/UXMapBundle.php
index 2f4bb35a..13929261 100644
--- a/src/Map/src/UXMapBundle.php
+++ b/src/Map/src/UXMapBundle.php
@@ -44,12 +44,6 @@ final class UXMapBundle extends AbstractBundle
         $rootNode
             ->children()
                 ->scalarNode('renderer')->defaultNull()->end()
-                ->arrayNode('google_maps')
-                    ->addDefaultsIfNotSet()
-                    ->children()
-                        ->scalarNode('default_map_id')->defaultNull()->end()
-                    ->end()
-                ->end()
             ->end()
         ;
     }
@@ -81,17 +75,9 @@ final class UXMapBundle extends AbstractBundle
         foreach (self::$bridges as $name => $bridge) {
             if (ContainerBuilder::willBeAvailable('symfony/ux-'.$name.'-map', $bridge['renderer_factory'], ['symfony/ux-map'])) {
                 $container->services()
-                    ->set($rendererFactoryName = 'ux_map.renderer_factory.'.$name, $bridge['renderer_factory'])
+                    ->set('ux_map.renderer_factory.'.$name, $bridge['renderer_factory'])
                     ->parent('ux_map.renderer_factory.abstract')
                     ->tag('ux_map.renderer_factory');
-
-                if ('google' === $name) {
-                    $container->services()
-                        ->get($rendererFactoryName)
-                        ->args([
-                            '$defaultMapId' => $config['google_maps']['default_map_id'],
-                        ]);
-                }
             }
         }
     }
diff --git a/src/TwigComponent/src/Attribute/AsTwigComponent.php b/src/TwigComponent/src/Attribute/AsTwigComponent.php
index d58e5647..829fd5bf 100644
--- a/src/TwigComponent/src/Attribute/AsTwigComponent.php
+++ b/src/TwigComponent/src/Attribute/AsTwigComponent.php
@@ -75,46 +75,6 @@ class AsTwigComponent
         ];
     }
 
-    /**
-     * @param object|class-string $component
-     *
-     * @internal
-     */
-    public static function mountMethod(object|string $component): ?\ReflectionMethod
-    {
-        foreach ((new \ReflectionClass($component))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
-            if ('mount' === $method->getName()) {
-                return $method;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * @param object|class-string $component
-     *
-     * @return \ReflectionMethod[]
-     *
-     * @internal
-     */
-    public static function postMountMethods(object|string $component): array
-    {
-        return self::attributeMethodsByPriorityFor($component, PostMount::class);
-    }
-
-    /**
-     * @param object|class-string $component
-     *
-     * @return \ReflectionMethod[]
-     *
-     * @internal
-     */
-    public static function preMountMethods(object|string $component): array
-    {
-        return self::attributeMethodsByPriorityFor($component, PreMount::class);
-    }
-
     /**
      * @param object|class-string $component
      * @param class-string        $attributeClass
diff --git a/src/TwigComponent/src/Command/TwigComponentDebugCommand.php b/src/TwigComponent/src/Command/TwigComponentDebugCommand.php
index 84313802..f883f59e 100644
--- a/src/TwigComponent/src/Command/TwigComponentDebugCommand.php
+++ b/src/TwigComponent/src/Command/TwigComponentDebugCommand.php
@@ -21,7 +21,6 @@ use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Style\SymfonyStyle;
 use Symfony\Component\Finder\Finder;
-use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate;
 use Symfony\UX\TwigComponent\ComponentFactory;
 use Symfony\UX\TwigComponent\ComponentMetadata;
@@ -214,7 +213,7 @@ EOF
         ]);
 
         // Anonymous Component
-        if (null === $metadata->get('class')) {
+        if ($metadata->isAnonymous()) {
             $table->addRows([
                 ['Type', '<comment>Anonymous</comment>'],
                 new TableSeparator(),
@@ -229,7 +228,7 @@ EOF
             ['Type', $metadata->get('live') ? '<info>Live</info>' : ''],
             new TableSeparator(),
             // ['Attributes Var', $metadata->get('attributes_var')],
-            ['Public Props', $metadata->get('expose_public_props') ? 'Yes' : 'No'],
+            ['Public Props', $metadata->isPublicPropsExposed() ? 'Yes' : 'No'],
             ['Properties', implode("\n", $this->getComponentProperties($metadata))],
         ]);
 
@@ -242,14 +241,15 @@ EOF
             return \sprintf('%s(%s)', $m->getName(), implode(', ', $params));
         };
         $hooks = [];
-        if ($method = AsTwigComponent::mountMethod($metadata->getClass())) {
-            $hooks[] = ['Mount', $logMethod($method)];
+        $reflector = new \ReflectionClass($metadata->getClass());
+        foreach ($metadata->getPreMounts() as $method) {
+            $hooks[] = ['PreMount', $logMethod($reflector->getMethod($method))];
         }
-        foreach (AsTwigComponent::preMountMethods($metadata->getClass()) as $method) {
-            $hooks[] = ['PreMount', $logMethod($method)];
+        foreach ($metadata->getMounts() as $method) {
+            $hooks[] = ['Mount', $logMethod($reflector->getMethod($method))];
         }
-        foreach (AsTwigComponent::postMountMethods($metadata->getClass()) as $method) {
-            $hooks[] = ['PostMount', $logMethod($method)];
+        foreach ($metadata->getPostMounts() as $method) {
+            $hooks[] = ['PostMount', $logMethod($reflector->getMethod($method))];
         }
         if ($hooks) {
             $table->addRows([
diff --git a/src/TwigComponent/src/ComponentFactory.php b/src/TwigComponent/src/ComponentFactory.php
index c191bc1b..8699fb10 100644
--- a/src/TwigComponent/src/ComponentFactory.php
+++ b/src/TwigComponent/src/ComponentFactory.php
@@ -15,7 +15,6 @@ use Psr\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\DependencyInjection\ServiceLocator;
 use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
 use Symfony\Contracts\Service\ResetInterface;
-use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 use Symfony\UX\TwigComponent\Event\PostMountEvent;
 use Symfony\UX\TwigComponent\Event\PreMountEvent;
 
@@ -26,13 +25,12 @@ use Symfony\UX\TwigComponent\Event\PreMountEvent;
  */
 final class ComponentFactory implements ResetInterface
 {
-    private static $mountMethods = [];
-    private static $preMountMethods = [];
-    private static $postMountMethods = [];
+    private static array $mountMethods = [];
 
     /**
      * @param array<string, array>        $config
      * @param array<class-string, string> $classMap
+     * @param array<class-string, array<string, string[]> $classMounts
      */
     public function __construct(
         private ComponentTemplateFinderInterface $componentTemplateFinder,
@@ -92,7 +90,7 @@ final class ComponentFactory implements ResetInterface
         $originalData = $data;
         $data = $this->preMount($component, $data, $componentMetadata);
 
-        $this->mount($component, $data);
+        $this->mount($component, $data, $componentMetadata);
 
         // set data that wasn't set in mount on the component directly
         foreach ($data as $property => $value) {
@@ -144,7 +142,7 @@ final class ComponentFactory implements ResetInterface
         return $this->components->get($metadata->getName());
     }
 
-    private function mount(object $component, array &$data): void
+    private function mount(object $component, array &$data, ComponentMetadata $componentMetadata): void
     {
         if ($component instanceof AnonymousComponent) {
             $component->mount($data);
@@ -152,22 +150,14 @@ final class ComponentFactory implements ResetInterface
             return;
         }
 
-        if (null === (self::$mountMethods[$component::class] ?? null)) {
-            try {
-                $mountMethod = self::$mountMethods[$component::class] = (new \ReflectionClass($component))->getMethod('mount');
-            } catch (\ReflectionException) {
-                self::$mountMethods[$component::class] = false;
-
-                return;
-            }
-        }
-
-        if (false === $mountMethod ??= self::$mountMethods[$component::class]) {
+        if (!$componentMetadata->getMounts()) {
             return;
         }
 
+        $mount = self::$mountMethods[$component::class] ??= (new \ReflectionClass($component))->getMethod('mount');
+
         $parameters = [];
-        foreach ($mountMethod->getParameters() as $refParameter) {
+        foreach ($mount->getParameters() as $refParameter) {
             if (\array_key_exists($name = $refParameter->getName(), $data)) {
                 $parameters[] = $data[$name];
                 // remove the data element so it isn't used to set the property directly.
@@ -175,11 +165,11 @@ final class ComponentFactory implements ResetInterface
             } elseif ($refParameter->isDefaultValueAvailable()) {
                 $parameters[] = $refParameter->getDefaultValue();
             } else {
-                throw new \LogicException(\sprintf('%s::mount() has a required $%s parameter. Make sure to pass it or give it a default value.', $component::class, $name));
+                throw new \LogicException(\sprintf('%s has a required $%s parameter. Make sure to pass it or give it a default value.', $component::class.'::mount()', $name));
             }
         }
 
-        $mountMethod->invoke($component, ...$parameters);
+        $mount->invoke($component, ...$parameters);
     }
 
     private function preMount(object $component, array $data, ComponentMetadata $componentMetadata): array
@@ -188,9 +178,8 @@ final class ComponentFactory implements ResetInterface
         $this->eventDispatcher->dispatch($event);
         $data = $event->getData();
 
-        $methods = self::$preMountMethods[$component::class] ??= AsTwigComponent::preMountMethods($component::class);
-        foreach ($methods as $method) {
-            if (null !== $newData = $method->invoke($component, $data)) {
+        foreach ($componentMetadata->getPreMounts() as $preMount) {
+            if (null !== $newData = $component->$preMount($data)) {
                 $data = $newData;
             }
         }
@@ -207,9 +196,8 @@ final class ComponentFactory implements ResetInterface
         $this->eventDispatcher->dispatch($event);
         $data = $event->getData();
 
-        $methods = self::$postMountMethods[$component::class] ??= AsTwigComponent::postMountMethods($component::class);
-        foreach ($methods as $method) {
-            if (null !== $newData = $method->invoke($component, $data)) {
+        foreach ($componentMetadata->getPostMounts() as $postMount) {
+            if (null !== $newData = $component->$postMount($data)) {
                 $data = $newData;
             }
         }
@@ -257,7 +245,5 @@ final class ComponentFactory implements ResetInterface
     public function reset(): void
     {
         self::$mountMethods = [];
-        self::$preMountMethods = [];
-        self::$postMountMethods = [];
     }
 }
diff --git a/src/TwigComponent/src/ComponentMetadata.php b/src/TwigComponent/src/ComponentMetadata.php
index b71e3491..4eb30327 100644
--- a/src/TwigComponent/src/ComponentMetadata.php
+++ b/src/TwigComponent/src/ComponentMetadata.php
@@ -67,6 +67,36 @@ final class ComponentMetadata
         return $this->get('attributes_var', 'attributes');
     }
 
+    /**
+     * @return list<string>
+     *
+     * @internal
+     */
+    public function getPreMounts(): array
+    {
+        return $this->get('pre_mount', []);
+    }
+
+    /**
+     * @return list<string>
+     *
+     * @internal
+     */
+    public function getMounts(): array
+    {
+        return $this->get('mount', []);
+    }
+
+    /**
+     * @return list<string>
+     *
+     * @internal
+     */
+    public function getPostMounts(): array
+    {
+        return $this->get('post_mount', []);
+    }
+
     public function get(string $key, mixed $default = null): mixed
     {
         return $this->config[$key] ?? $default;
diff --git a/src/TwigComponent/src/DependencyInjection/Compiler/TwigComponentPass.php b/src/TwigComponent/src/DependencyInjection/Compiler/TwigComponentPass.php
index faf86afe..b39ae453 100644
--- a/src/TwigComponent/src/DependencyInjection/Compiler/TwigComponentPass.php
+++ b/src/TwigComponent/src/DependencyInjection/Compiler/TwigComponentPass.php
@@ -16,6 +16,8 @@ use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Exception\LogicException;
 use Symfony\Component\DependencyInjection\Reference;
+use Symfony\UX\TwigComponent\Attribute\PostMount;
+use Symfony\UX\TwigComponent\Attribute\PreMount;
 
 /**
  * @author Kevin Bond <kevinbond@gmail.com>
@@ -68,7 +70,7 @@ final class TwigComponentPass implements CompilerPassInterface
                 $tag['service_id'] = $id;
                 $tag['class'] = $definition->getClass();
                 $tag['template'] = $tag['template'] ?? $this->calculateTemplate($tag['key'], $defaults);
-                $componentConfig[$tag['key']] = $tag;
+                $componentConfig[$tag['key']] = [...$tag, ...$this->getMountMethods($tag['class'])];
                 $componentReferences[$tag['key']] = new Reference($id);
                 $componentNames[] = $tag['key'];
                 $componentClassMap[$tag['class']] = $tag['key'];
@@ -109,4 +111,35 @@ final class TwigComponentPass implements CompilerPassInterface
 
         return \sprintf('%s/%s.html.twig', rtrim($directory, '/'), str_replace(':', '/', $componentName));
     }
+
+    /**
+     * @param class-string $component
+     *
+     * @return array{preMount: string[], mount: string[], postMount: string[]}
+     */
+    private function getMountMethods(string $component): array
+    {
+        $preMount = $mount = $postMount = [];
+        foreach ((new \ReflectionClass($component))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
+            foreach ($method->getAttributes(PreMount::class) as $attribute) {
+                $preMount[$method->getName()] = $attribute->newInstance()->priority;
+            }
+            foreach ($method->getAttributes(PostMount::class) as $attribute) {
+                $postMount[$method->getName()] = $attribute->newInstance()->priority;
+            }
+            if ('mount' === $method->getName()) {
+                $mount['mount'] = 0;
+            }
+        }
+
+        arsort($preMount, \SORT_NUMERIC);
+        arsort($mount, \SORT_NUMERIC);
+        arsort($postMount, \SORT_NUMERIC);
+
+        return [
+            'pre_mount' => array_keys($preMount),
+            'mount' => array_keys($mount),
+            'post_mount' => array_keys($postMount),
+        ];
+    }
 }


Test patch:
diff --git a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
index 511a5580..66ea8cd2 100644
--- a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
+++ b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php
@@ -1872,7 +1872,10 @@ class HydrationTest
         return new HydrationTestCase(
             $this->component,
             new LiveComponentMetadata(
-                new ComponentMetadata(['key' => '__testing']),
+                new ComponentMetadata([
+                    'key' => '__testing',
+                    'mount' => $reflectionClass->hasMethod('mount') ? ['mount'] : [],
+                ]),
                 $metadataFactory->createPropMetadatas($reflectionClass),
             ),
             $this->inputProps,
diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
index 358eb2fa..e688d0c8 100644
--- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
+++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php
@@ -78,23 +78,5 @@ class GoogleRendererTest extends RendererTestCase
                     fullscreenControl: false,
                 )),
         ];
-
-        yield 'with default map id' => [
-            'expected_renderer' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="{&quot;apiKey&quot;:&quot;my_api_key&quot;}" data-symfony--ux-google-map--map-view-value="{&quot;center&quot;:{&quot;lat&quot;:48.8566,&quot;lng&quot;:2.3522},&quot;zoom&quot;:12,&quot;fitBoundsToMarkers&quot;:false,&quot;options&quot;:{&quot;mapId&quot;:&quot;DefaultMapId&quot;,&quot;gestureHandling&quot;:&quot;auto&quot;,&quot;backgroundColor&quot;:null,&quot;disableDoubleClickZoom&quot;:false,&quot;zoomControlOptions&quot;:{&quot;position&quot;:22},&quot;mapTypeControlOptions&quot;:{&quot;mapTypeIds&quot;:[],&quot;position&quot;:14,&quot;style&quot;:0},&quot;streetViewControlOptions&quot;:{&quot;position&quot;:22},&quot;fullscreenControlOptions&quot;:{&quot;position&quot;:20}},&quot;markers&quot;:[],&quot;polygons&quot;:[]}"></div>',
-            'renderer' => new GoogleRenderer(new StimulusHelper(null), 'my_api_key', defaultMapId: 'DefaultMapId'),
-            'map' => (clone $map),
-        ];
-        yield 'with default map id, when passing options (except the "mapId")' => [
-            'expected_renderer' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="{&quot;apiKey&quot;:&quot;my_api_key&quot;}" data-symfony--ux-google-map--map-view-value="{&quot;center&quot;:{&quot;lat&quot;:48.8566,&quot;lng&quot;:2.3522},&quot;zoom&quot;:12,&quot;fitBoundsToMarkers&quot;:false,&quot;options&quot;:{&quot;mapId&quot;:&quot;DefaultMapId&quot;,&quot;gestureHandling&quot;:&quot;auto&quot;,&quot;backgroundColor&quot;:null,&quot;disableDoubleClickZoom&quot;:false,&quot;zoomControlOptions&quot;:{&quot;position&quot;:22},&quot;mapTypeControlOptions&quot;:{&quot;mapTypeIds&quot;:[],&quot;position&quot;:14,&quot;style&quot;:0},&quot;streetViewControlOptions&quot;:{&quot;position&quot;:22},&quot;fullscreenControlOptions&quot;:{&quot;position&quot;:20}},&quot;markers&quot;:[],&quot;polygons&quot;:[]}"></div>',
-            'renderer' => new GoogleRenderer(new StimulusHelper(null), 'my_api_key', defaultMapId: 'DefaultMapId'),
-            'map' => (clone $map)
-                ->options(new GoogleOptions()),
-        ];
-        yield 'with default map id overridden by option "mapId"' => [
-            'expected_renderer' => '<div data-controller="symfony--ux-google-map--map" data-symfony--ux-google-map--map-provider-options-value="{&quot;apiKey&quot;:&quot;my_api_key&quot;}" data-symfony--ux-google-map--map-view-value="{&quot;center&quot;:{&quot;lat&quot;:48.8566,&quot;lng&quot;:2.3522},&quot;zoom&quot;:12,&quot;fitBoundsToMarkers&quot;:false,&quot;options&quot;:{&quot;mapId&quot;:&quot;CustomMapId&quot;,&quot;gestureHandling&quot;:&quot;auto&quot;,&quot;backgroundColor&quot;:null,&quot;disableDoubleClickZoom&quot;:false,&quot;zoomControlOptions&quot;:{&quot;position&quot;:22},&quot;mapTypeControlOptions&quot;:{&quot;mapTypeIds&quot;:[],&quot;position&quot;:14,&quot;style&quot;:0},&quot;streetViewControlOptions&quot;:{&quot;position&quot;:22},&quot;fullscreenControlOptions&quot;:{&quot;position&quot;:20}},&quot;markers&quot;:[],&quot;polygons&quot;:[]}"></div>',
-            'renderer' => new GoogleRenderer(new StimulusHelper(null), 'my_api_key', defaultMapId: 'DefaultMapId'),
-            'map' => (clone $map)
-                ->options(new GoogleOptions(mapId: 'CustomMapId')),
-        ];
     }
 }
diff --git a/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeRootComponent.php b/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeRootComponent.php
index 47fba424..5783818d 100644
--- a/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeRootComponent.php
+++ b/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeRootComponent.php
@@ -7,5 +7,4 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 #[AsTwigComponent]
 class AcmeRootComponent
 {
-
 }
diff --git a/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeSubDir/AcmeOtherComponent.php b/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeSubDir/AcmeOtherComponent.php
index d42ad45b..fa31f3ea 100644
--- a/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeSubDir/AcmeOtherComponent.php
+++ b/src/TwigComponent/tests/Fixtures/AcmeComponent/AcmeSubDir/AcmeOtherComponent.php
@@ -7,5 +7,4 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 #[AsTwigComponent]
 class AcmeOtherComponent
 {
-
 }
diff --git a/src/TwigComponent/tests/Fixtures/Component/Conflict.php b/src/TwigComponent/tests/Fixtures/Component/Conflict.php
index 0ffd16a8..c3b7960b 100644
--- a/src/TwigComponent/tests/Fixtures/Component/Conflict.php
+++ b/src/TwigComponent/tests/Fixtures/Component/Conflict.php
@@ -8,4 +8,4 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 class Conflict
 {
     public string $name;
-}
\ No newline at end of file
+}
diff --git a/src/TwigComponent/tests/Fixtures/Component/SubDirectory/ComponentInSubDirectory.php b/src/TwigComponent/tests/Fixtures/Component/SubDirectory/ComponentInSubDirectory.php
index 723eafbc..a7a53df4 100644
--- a/src/TwigComponent/tests/Fixtures/Component/SubDirectory/ComponentInSubDirectory.php
+++ b/src/TwigComponent/tests/Fixtures/Component/SubDirectory/ComponentInSubDirectory.php
@@ -7,5 +7,4 @@ use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
 #[AsTwigComponent]
 class ComponentInSubDirectory
 {
-
 }
diff --git a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
index 8b2b0e09..4f113262 100644
--- a/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
+++ b/src/TwigComponent/tests/Integration/ComponentExtensionTest.php
@@ -283,7 +283,8 @@ final class ComponentExtensionTest extends KernelTestCase
     {
         $output = $this->renderComponent('NestedAttributes');
 
-        $this->assertSame(<<<HTML
+        $this->assertSame(
+            <<<HTML
             <main>
                 <div>
                     <span>
@@ -302,7 +303,8 @@ final class ComponentExtensionTest extends KernelTestCase
             'title:span:class' => 'baz',
         ]);
 
-        $this->assertSame(<<<HTML
+        $this->assertSame(
+            <<<HTML
             <main class="foo">
                 <div class="bar">
                     <span class="baz">
@@ -324,7 +326,8 @@ final class ComponentExtensionTest extends KernelTestCase
             ->render()
         ;
 
-        $this->assertSame(<<<HTML
+        $this->assertSame(
+            <<<HTML
             <main>
                 <div>
                     <span>
@@ -343,7 +346,8 @@ final class ComponentExtensionTest extends KernelTestCase
             ->render()
         ;
 
-        $this->assertSame(<<<HTML
+        $this->assertSame(
+            <<<HTML
             <main class="foo" @class="vex">
                 <div class="bar">
                     <span class="baz">
diff --git a/src/TwigComponent/tests/Unit/Attribute/AsTwigComponentTest.php b/src/TwigComponent/tests/Unit/Attribute/AsTwigComponentTest.php
deleted file mode 100644
index 1606792c..00000000
--- a/src/TwigComponent/tests/Unit/Attribute/AsTwigComponentTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/*
- * This file is part of the Symfony package.
- *
- * (c) Fabien Potencier <fabien@symfony.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\UX\TwigComponent\Tests\Unit;
-
-use PHPUnit\Framework\TestCase;
-use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
-use Symfony\UX\TwigComponent\Attribute\PostMount;
-use Symfony\UX\TwigComponent\Attribute\PreMount;
-
-/**
- * @author Kevin Bond <kevinbond@gmail.com>
- */
-final class AsTwigComponentTest extends TestCase
-{
-    public function testPreMountHooksAreOrderedByPriority(): void
-    {
-        $hooks = AsTwigComponent::preMountMethods(
-            new class {
-                #[PreMount(priority: -10)]
-                public function hook1()
-                {
-                }
-
-                #[PreMount(priority: 10)]
-                public function hook2()
-                {
-                }
-
-                #[PreMount]
-                public function hook3()
-                {
-                }
-            }
-        );
-
-        $this->assertCount(3, $hooks);
-        $this->assertSame('hook2', $hooks[0]->name);
-        $this->assertSame('hook3', $hooks[1]->name);
-        $this->assertSame('hook1', $hooks[2]->name);
-    }
-
-    public function testPostMountHooksAreOrderedByPriority(): void
-    {
-        $hooks = AsTwigComponent::postMountMethods(
-            new class {
-                #[PostMount(priority: -10)]
-                public function hook1()
-                {
-                }
-
-                #[PostMount(priority: 10)]
-                public function hook2()
-                {
-                }
-
-                #[PostMount]
-                public function hook3()
-                {
-                }
-            }
-        );
-
-        $this->assertCount(3, $hooks);
-        $this->assertSame('hook2', $hooks[0]->name);
-        $this->assertSame('hook3', $hooks[1]->name);
-        $this->assertSame('hook1', $hooks[2]->name);
-    }
-}
diff --git a/src/TwigComponent/tests/Unit/ComponentFactoryTest.php b/src/TwigComponent/tests/Unit/ComponentFactoryTest.php
index fd1fd337..ccbf85f5 100644
--- a/src/TwigComponent/tests/Unit/ComponentFactoryTest.php
+++ b/src/TwigComponent/tests/Unit/ComponentFactoryTest.php
@@ -31,7 +31,8 @@ class ComponentFactoryTest extends TestCase
             $this->createMock(PropertyAccessorInterface::class),
             $this->createMock(EventDispatcherInterface::class),
             ['foo' => ['key' => 'foo', 'template' => 'bar.html.twig']],
-            []
+            [],
+            [],
         );
 
         $metadata = $factory->metadataFor('foo');
@@ -52,6 +53,7 @@ class ComponentFactoryTest extends TestCase
                 'foo' => ['key' => 'foo', 'template' => 'foo.html.twig'],
             ],
             ['Foo\\Bar' => 'bar'],
+            [],
         );
 
         $metadata = $factory->metadataFor('Foo\\Bar');
@@ -74,7 +76,8 @@ class ComponentFactoryTest extends TestCase
             $this->createMock(PropertyAccessorInterface::class),
             $this->createMock(EventDispatcherInterface::class),
             [],
-            []
+            [],
+            [],
         );
 
         $metadata = $factory->metadataFor('foo');