diff --git a/CHANGELOG.md b/CHANGELOG.md index 39fef55..6b0033a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [2.2.0] - 2026-04-20 - Added: support for modern twig layouts of contao 5.7 ([#34](https://github.com/heimrichhannot/contao-encore-bundle/pull/34)) - Added: EntrypointsBuilder concept for retriving current page entrypoints ([#34](https://github.com/heimrichhannot/contao-encore-bundle/pull/34)) +- Added: support for symfony debug bar ([#35](https://github.com/heimrichhannot/contao-encore-bundle/pull/35)) - Changed: add constant for default field name ([#34](https://github.com/heimrichhannot/contao-encore-bundle/pull/34)) - Fixed: removed dead or unnecessary code and checks ([#34](https://github.com/heimrichhannot/contao-encore-bundle/pull/34)) - Deprecated: `src/Asset/PageEntrypoints.php`, `src/Asset/TemplateAsset.php` and `src/Asset/TemplateAssetGenerator.php` ([#34](https://github.com/heimrichhannot/contao-encore-bundle/pull/34)) diff --git a/README.md b/README.md index c48f9d1..f33a743 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# Contao Encore Bundle +# ![waypoints.svg](docs/img/waypoints.svg) Contao Encore Bundle [![Latest Stable Version](https://img.shields.io/packagist/v/heimrichhannot/contao-encore-bundle.svg)](https://packagist.org/packages/heimrichhannot/contao-encore-bundle) [![Total Downloads](https://img.shields.io/packagist/dt/heimrichhannot/contao-encore-bundle.svg)](https://packagist.org/packages/heimrichhannot/contao-encore-bundle) ![CI](https://github.com/heimrichhannot/contao-encore-bundle/workflows/CI/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/heimrichhannot/contao-encore-bundle/badge.svg?branch=master)](https://coveralls.io/github/heimrichhannot/contao-encore-bundle?branch=master) -![](https://img.shields.io/badge/PHPStan-level%204-brightgreen.svg?style=flat) +![](https://img.shields.io/badge/PHPStan-level%205-brightgreen.svg?style=flat) Use the power and simplicity of symfony webpack encore in contao. This bundle let you decide on layout and page level, which encore entries should be loaded. If you want more, you can prepare your bundles define their own encore entries, so never need to manually add or remove encore entries again. diff --git a/config/services.php b/config/services.php index c3a3e72..d009c7f 100644 --- a/config/services.php +++ b/config/services.php @@ -10,6 +10,7 @@ use HeimrichHannot\EncoreBundle\Asset\FrontendAsset; use HeimrichHannot\EncoreBundle\Asset\TemplateAsset; +use HeimrichHannot\EncoreBundle\DataCollector\EncoreCollector; use HeimrichHannot\EncoreBundle\EntryPoint\EntryPointBuilderFactory; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -43,11 +44,15 @@ $services ->alias('huh.encore.asset.frontend', FrontendAsset::class) - ->public() + ->public() ; $services ->alias('huh.encore.asset.template', TemplateAsset::class) - ->public() + ->public() ; + + $services->set(EncoreCollector::class) + ->autoconfigure() + ->autowire(); }; diff --git a/contao/templates/twig/.twig-root b/contao/templates/twig/.twig-root new file mode 100644 index 0000000..e69de29 diff --git a/contao/templates/twig/data_collector/huh_encore.html.twig b/contao/templates/twig/data_collector/huh_encore.html.twig new file mode 100644 index 0000000..6798126 --- /dev/null +++ b/contao/templates/twig/data_collector/huh_encore.html.twig @@ -0,0 +1,133 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% block toolbar %} + + {% set icon %} + + {{ collector.entries|length }} + {% endset %} + + {% set text %} +
+
+ Enabled + {{ collector.enabled ? 'yes' : 'no' }} +
+ +
+ Encore Entries + {{ collector.entries|length }} +
+ +
+ Encore Extensions + {{ collector.extensions|length }} +
+
+
+
+ Resources + Read the encore bundle docs +
+
+ {% endset %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { + link: true, + additional_classes: (collector.enabled ? '' : 'sf-toolbar-status-red') } + ) }} + +{% endblock %} + +{% block menu %} + + + + + Encore + +{% endblock %} + +{% block panel %} +

Encore

+ +
+
+ {{ collector.enabled ? 'yes' : 'no' }} + Enabled +
+ +
+ {{ collector.version }} + Encore Bundle version +
+ +
+ {{ collector.extensions|length }} + Registered extensions +
+ +
+ + +

Page entry points

+ + + + + + + + + + + + + + + {% for entry in collector.entries %} + + + + + + + + + {% endfor %} + +
NameActiveHeadCSSOriginExtension
{{ entry.name }}{{ entry.active }}{{ entry.head }}{{ entry.requiresCss }}{{ entry.origin }}{{ entry.extension }}
+ +

Registered extensions

+ + + + + + + + + + {% for extension in collector.extensions %} + + + + + {% endfor %} + +
Extension classEntry Points
{{ extension.name }} +
    + {% for entry in extension.entries %} +
  • {{ entry.name }}
  • + {% endfor %} +
+
+{% endblock %} \ No newline at end of file diff --git a/docs/img/waypoints.svg b/docs/img/waypoints.svg new file mode 100644 index 0000000..2519610 --- /dev/null +++ b/docs/img/waypoints.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/DataCollector/EncoreCollector.php b/src/DataCollector/EncoreCollector.php new file mode 100644 index 0000000..3d30173 --- /dev/null +++ b/src/DataCollector/EncoreCollector.php @@ -0,0 +1,70 @@ +attributes->has('encore_entries')) { + $entryPoints = $request->attributes->get('encore_entries'); + if (!($entryPoints instanceof EntryPoints)) { + $this->data['enabled'] = false; + } else { + $this->data['entries'] = $entryPoints->all(); + $this->data['enabled'] = true; + } + } else { + $this->data['enabled'] = false; + } + + $extensions = $this->extensionCollection->getExtensions(); + $extensionEntries = []; + foreach ($extensions as $extension) { + $reflection = new \ReflectionClass($extension->getBundle()); + $extensionEntries[] = [ + 'name' => $reflection->getShortName(), + 'entries' => $extension->getEntries(), + ]; + } + + $this->data['extensions'] = $extensionEntries; + } + + public static function getTemplate(): ?string + { + return '@Contao/data_collector/huh_encore.html.twig'; + } + + public function isEnabled(): bool + { + return $this->data['enabled'] ?? false; + } + + public function getEntries(): array + { + return $this->data['entries'] ?? []; + } + + public function getVersion(): string + { + return InstalledVersions::getPrettyVersion('heimrichhannot/contao-encore-bundle') ?? 'unknown'; + } + + public function getExtensions(): array + { + return $this->data['extensions'] ?? []; + } +} diff --git a/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php b/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php index 4f49303..eaa69bc 100644 --- a/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php +++ b/src/EventListener/Contao/ReplaceDynamicScriptTagsListener.php @@ -15,6 +15,7 @@ use HeimrichHannot\EncoreBundle\EntryPoint\EntryPointBuilderFactory; use HeimrichHannot\EncoreBundle\Helper\ConfigurationHelper; use HeimrichHannot\UtilsBundle\Util\Utils; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\WebpackEncoreBundle\Asset\TagRenderer; #[AsHook('replaceDynamicScriptTags')] @@ -27,6 +28,7 @@ public function __construct( private readonly EntryPointBuilderFactory $entryPointBuilderFactory, private readonly FrontendAsset $frontendAsset, private readonly TagRenderer $tagRenderer, + private readonly RequestStack $requestStack, ) { } @@ -47,6 +49,12 @@ public function __invoke(string $buffer): string ->setPage($pageModel) ->build(); + if ($request = $this->requestStack->getCurrentRequest()) { + $request->attributes->add([ + 'encore_entries' => $entryPoints, + ]); + } + $css = ''; $headJs = ''; $bodyJs = ''; diff --git a/src/EventListener/InjectPageEntriesListener.php b/src/EventListener/InjectPageEntriesListener.php index 526db6f..d6d6bb7 100644 --- a/src/EventListener/InjectPageEntriesListener.php +++ b/src/EventListener/InjectPageEntriesListener.php @@ -8,6 +8,7 @@ use HeimrichHannot\EncoreBundle\EntryPoint\EntryPointBuilderFactory; use HeimrichHannot\EncoreBundle\Helper\ConfigurationHelper; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\WebpackEncoreBundle\Asset\TagRenderer; class InjectPageEntriesListener @@ -18,6 +19,7 @@ public function __construct( private readonly FrontendAsset $frontendAsset, private readonly GlobalContaoAsset $globalContaoAsset, private readonly ConfigurationHelper $configurationHelper, + private readonly RequestStack $requestStack, ) { } @@ -36,6 +38,12 @@ public function onLayoutEvent(LayoutEvent $event): void ->setFrontendAsset($this->frontendAsset) ->build(); + if ($request = $this->requestStack->getCurrentRequest()) { + $request->attributes->add([ + 'encore_entries' => $entryPoints, + ]); + } + $this->tagRenderer->reset(); foreach ($entryPoints->allActive() as $entrypoint) { diff --git a/tests/DataCollector/EncoreCollectorTest.php b/tests/DataCollector/EncoreCollectorTest.php new file mode 100644 index 0000000..9309467 --- /dev/null +++ b/tests/DataCollector/EncoreCollectorTest.php @@ -0,0 +1,87 @@ +add(new EntryPoint('app', head: true, requiresCss: true)); + $entryPoints->add(new EntryPoint('deferred', active: false)); + + $extensionEntry = EncoreEntry::create('bundle-entry', '/build/bundle-entry.js'); + $extension = $this->createMock(EncoreExtensionInterface::class); + $extension->expects($this->once())->method('getBundle')->willReturn(HeimrichHannotEncoreBundle::class); + $extension->expects($this->once())->method('getEntries')->willReturn([$extensionEntry]); + + $extensionCollection = $this->createMock(ExtensionCollection::class); + $extensionCollection->expects($this->once())->method('getExtensions')->willReturn([$extension]); + + $collector = new EncoreCollector($extensionCollection); + + $request = new Request(); + $request->attributes->set('encore_entries', $entryPoints); + + $collector->collect($request, new Response()); + + $this->assertTrue($collector->isEnabled()); + $this->assertSame($entryPoints->all(), $collector->getEntries()); + $this->assertSame( + [ + [ + 'name' => 'HeimrichHannotEncoreBundle', + 'entries' => [$extensionEntry], + ], + ], + $collector->getExtensions() + ); + $this->assertSame('@Contao/data_collector/huh_encore.html.twig', EncoreCollector::getTemplate()); + $this->assertSame( + InstalledVersions::getPrettyVersion('heimrichhannot/contao-encore-bundle') ?? 'unknown', + $collector->getVersion() + ); + } + + public function testCollectWithoutEntryPointsDisablesCollector(): void + { + $extensionCollection = $this->createMock(ExtensionCollection::class); + $extensionCollection->expects($this->once())->method('getExtensions')->willReturn([]); + + $collector = new EncoreCollector($extensionCollection); + $collector->collect(new Request(), new Response()); + + $this->assertFalse($collector->isEnabled()); + $this->assertSame([], $collector->getEntries()); + $this->assertSame([], $collector->getExtensions()); + } + + public function testCollectWithInvalidEntryPointsAttributeDisablesCollector(): void + { + $extensionCollection = $this->createMock(ExtensionCollection::class); + $extensionCollection->expects($this->once())->method('getExtensions')->willReturn([]); + + $collector = new EncoreCollector($extensionCollection); + + $request = new Request(); + $request->attributes->set('encore_entries', 'invalid'); + + $collector->collect($request, new Response()); + + $this->assertFalse($collector->isEnabled()); + $this->assertSame([], $collector->getEntries()); + $this->assertSame([], $collector->getExtensions()); + } +} diff --git a/tests/EventListener/Contao/ReplaceDynamicScriptTagsListenerTest.php b/tests/EventListener/Contao/ReplaceDynamicScriptTagsListenerTest.php index 0400668..725643b 100644 --- a/tests/EventListener/Contao/ReplaceDynamicScriptTagsListenerTest.php +++ b/tests/EventListener/Contao/ReplaceDynamicScriptTagsListenerTest.php @@ -22,6 +22,8 @@ use HeimrichHannot\TestUtilitiesBundle\Mock\ModelMockTrait; use HeimrichHannot\UtilsBundle\Util\RequestUtil; use HeimrichHannot\UtilsBundle\Util\Utils; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\WebpackEncoreBundle\Asset\TagRenderer; class ReplaceDynamicScriptTagsListenerTest extends ContaoTestCase @@ -36,6 +38,7 @@ public function createTestInstance(array $parameter = []): ReplaceDynamicScriptT $parameter['entryPointBuilderFactory'] = $parameter['entryPointBuilderFactory'] ?? $this->createMock(EntryPointBuilderFactory::class); $parameter['frontendAsset'] = $parameter['frontendAsset'] ?? $this->createMock(FrontendAsset::class); $parameter['tagRenderer'] = $parameter['tagRenderer'] ?? $this->createMock(TagRenderer::class); + $parameter['requestStack'] = $parameter['requestStack'] ?? $this->createMock(RequestStack::class); return new ReplaceDynamicScriptTagsListener( $parameter['utils'], @@ -44,6 +47,7 @@ public function createTestInstance(array $parameter = []): ReplaceDynamicScriptT entryPointBuilderFactory: $parameter['entryPointBuilderFactory'], frontendAsset: $parameter['frontendAsset'], tagRenderer: $parameter['tagRenderer'], + requestStack: $parameter['requestStack'] ); } @@ -139,12 +143,19 @@ public function testInvoke() default => throw new \InvalidArgumentException(sprintf('Unexpected entry "%s".', $entryName)), }); + $request = new Request(); + $requestStack = $this->createMock(RequestStack::class); + $requestStack->expects($this->once()) + ->method('getCurrentRequest') + ->willReturn($request); + $instance = $this->createTestInstance([ 'utils' => $utils, 'configurationHelper' => $configurationHelper, 'globalContaoAsset' => $globalContaoAsset, 'entryPointBuilderFactory' => $entryPointBuilderFactory, 'tagRenderer' => $tagRenderer, + 'requestStack' => $requestStack, ]); $nonce = '_' . ContaoFramework::getNonce(); @@ -152,5 +163,6 @@ public function testInvoke() $expected = "[[TL_CSS$nonce]] [[TL_HEAD$nonce]] [[TL_BODY$nonce]]"; $this->assertSame($expected, $instance->__invoke($buffer)); + $this->assertSame($entryPoints, $request->attributes->get('encore_entries')); } }