joomla / joomla-cms

Home of the Joomla! Content Management System
https://www.joomla.org
GNU General Public License v2.0
4.77k stars 3.65k forks source link

[4.X] Place certain webassets at the beginning of head's script tags #40502

Closed maggus closed 1 year ago

maggus commented 1 year ago

Is your feature request related to a problem? Please describe.

Currently, to my knowledge, it is not possible to place a new webasset at the beginning of head's script tags

But there might be some situations in which this is necessary (i.e. privacy/consent tools).

Describe the solution you'd like

Currently I'm using a workaround to place Joomla! 4 integrated scripts after my own scripts that have been "registered" and "used" before this code.

$app = Factory::getApplication();
$document = Factory::getDocument();
$webassets = $document->getWebAssetManager();

// Places 'media/system' and 'media/vendor' scripts after consent tools
$assets = [
    'diff',
    'accessibility',
    'short-and-sweet',
    'awesomplete',
    'cropperjs',
    'choicesjs',
    'focus-visible',
    'dragula',
    'hotkeysjs',
    'jquery',
    'metismenujs',
    'skipto',
    'qrcode',
    'joomla.batch-copymove',
    'core',
    'field.color-slider'
];

foreach($assets as $asset) {
    if ($webassets->getAssetState('script', $asset) !== 0) {
        $webassets
            ->disableAsset('script', $asset)
            ->useAsset('script', $asset);
    }
}
unset($asset);

What does this script do? It disables and re-enables certain scripts so that they are placed later in the WebAssetManager. "Certain scripts" means that only scripts WITHOUT any dependency get reordered. All other dependent assets seem to be attached after these scripts even when asset state changes from '2' to '1' by this method.

Additional context

I would like to get feedback if

dgrammatiko commented 1 year ago

Currently, to my knowledge, it is not possible to place a new webasset at the beginning of head's script tags

How are you registering your assets? Using $document->addScript() or similar, will always append the asset AFTER the ones registered with WebAssetManager Using HTMLHelper::script() or similar will always append the asset AFTER the ones registered with WebAssetManager

So, to register something at the very beginning you have to:

In sort, the old functions will ALWAYS append the assets after the ones from the WAM...

Lastly, I guess this is for v5 (@HLeithner), all the old APIs for the scripts/stylesheets should be deprecated but in the mean time they should proxy to the respected WAM ones

maggus commented 1 year ago

@dgrammatiko I register the assets with $wa->registerAndUseScript() on onAfterDispatch event.

Using onAfterRoute would be one system event earlier right? But how can I register assets without instantiating the Document object? This also seems to be bad practice, according to this document.

Fedik commented 1 year ago

What you are doing is changing FIFO order. You can adjust it for specific Asset with:

$wa->getAsset('script', 'potato-consent')->setOption('weight', -1);

However it does not guarante to be first, especialy when the asset have a dependency, or someone else already doing the same.

In the end I would not recommend to abuse this option. If possible better use @dgrammatiko suggestion but with onAfterDispatch event. I closing the issue, the feature already exists.

maggus commented 1 year ago

Thank you both (@dgrammatiko, @Fedik) for the detailed feedback!

I was not aware that there is an option you can use to request a change of the assets order. I have already tried the approach with WAM and onAfterDispatch event but this hasn't led to a stable result as system and component assets might be enabled before.

Therefore I have created the workaround shown at the beginning but this approach only generates workload without a stable result either. To change the weight of a certain asset seems to be the lightest solution with highest success. Of course, this can be overwritten again but it might suit to most of the scenarios considered.