Feature: #90899 - Introduce AssetRenderer pre-rendering events

See forge#90899

Description

AssetRenderer is amended by two events which allow post-processing of AssetCollector assets.

These new PSR-14 events are introduced:

Both stem fom the abstract base class \TYPO3\CMS\Core\Page\Event\AbstractBeforeAssetRenderingEvent and provide these public methods:

  • getAssetCollector(): AssetCollector
  • isInline(): bool
  • isPriority(): bool

inline and priority refer to how the asset was registered with AssetCollector.

The events are fired exactly once for every combination of inline/priority before the corresponding section of JS/CSS assets is rendered by the AssetRenderer.

To make the events easier to use, the AssetCollector::get*() methods have gotten an optional parameter ?bool $priority = null which when given a boolean only returns assets of the given priority.

Example

As an example let's make sure jQuery is included in a specific version and from a CDN.

  1. Register our listeners

    Configuration/Services.yaml

    services:
       MyVendor\MyExt\EventListener\AssetRenderer\LibraryVersion:
        tags:
          - name: event.listener
            identifier: 'myExt/LibraryVersion'
            event: TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent
    Copied!
  2. Implement Listener to enforce a library version or CDN URI

    namespace MyVendor\MyExt\EventListener\AssetRenderer;
    
    use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent;
    
    /**
     * If a library has been registered, it is made sure that it is loaded
     * from the given URI
     */
    class LibraryVersion
    {
        protected $libraries = [
            'jquery' => 'https://code.jquery.com/jquery-3.4.1.min.js',
        ];
    
        public function __invoke(BeforeJavaScriptsRenderingEvent $event): void
        {
            if ($event->isInline()) {
                return;
            }
    
            foreach ($this->libraries as $library => $source) {
                $asset = $event->getAssetCollector()->getJavaScripts($event->isPriority())
                // if it was already registered
                if ($asset[$library] ?? false) {
                    // we set our authoritative version
                    $event->getAssetCollector()->addJavaScript($library, $source);
                }
            }
        }
    }
    Copied!

Impact

Existing installations are not affected.

If using the AssetCollector API, these new events should be used for asset postprocessing.