s2b / vite-asset-collector

Bundle your TYPO3 frontend assets with Vite
GNU General Public License v2.0
41 stars 11 forks source link

Check loading order in TYPO3 backend #57

Open s2b opened 4 months ago

s2b commented 4 months ago

There has been a report that JS loaded by vite in the TYPO3 backend doesn't have access to the global TYPO3 variable. This might be related to the loading order of JS files in the backend. It is probably not related to the extension, but should be investigated and potentially fixed in the TYPO3 core.

mediaessenz commented 4 months ago

After some more investigation I found the cause of the global TYPO3 var loading problem: I moved the reading of the global TYPO3 var to an extra file (Typo3Vars.js):

export const lang = Typo3Utilities.getTypo3LanguageLabels(TYPO3.lang ?? []); 

and imported the lang var inside my App.vue like so:

import { lang } from './Typo3Vars';

Then I created the App before the DOMContentLoaded event was fired:

import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

document.addEventListener('DOMContentLoaded', () => {
    app.mount('#app');
});

To fix it, I put the const lang = Typo3Utilities.getTypo3LanguageLabels(TYPO3.lang ?? []); direct into my App.vue code and changed the init code like so:

import { createApp } from 'vue';
import App from './App.vue';

document.addEventListener('DOMContentLoaded', () => {
    const app = createApp(App);
    app.mount('#app');
});

It could be, that only the last change (moving the createApp into the event callback) does the trick.

However, I think this issue can be closed from my point of view.

mediaessenz commented 4 months ago

Meanwhile I found out that this issue is still not solved in safari and other webkit based browsers. The global var TYPO3 is still not present when DOMContentLoaded is fired.

To solve this problem, I use a helper function waitForVariable which looks like this:

function waitForVariable(variableName, callback, checkInterval = 100) {
    const intervalId = setInterval(() => {
        if (typeof window[variableName] !== 'undefined') {
            clearInterval(intervalId);
            callback(window[variableName]);
        }
    }, checkInterval);
}

The initialization of my vue app looks like this now:

waitForVariable('TYPO3', () => {
    const app = createApp(App);
    app.mount('#app');
});
mediaessenz commented 4 months ago

To reproduce this behavior this js can be helpfull:

document.addEventListener('DOMContentLoaded', () => {
    if (typeof TYPO3 === 'undefined') {
        console.log('global var TYPO3 is undefined!');
    }
});
mediaessenz commented 4 months ago

After further research, I found the reason, why the DOMContentLoaded event is not working: The JS containing the global TYPO3 vars coming from the TYPO3 "java-script-item-handler" is loaded async! Since DOMContentLoaded is not waiting for async loaded stuff, the TYPO3 var not available at this time.

But even if I put the initialization code inside of a construction like this:

window.addEventListener('load', () => {
    if (typeof TYPO3 === 'undefined') {
        console.log('global var TYPO3 is undefined!');
    }
})

it only works half the time in safari and consorts! I think, that the async code containing the global vars has to be handled by a TYPO3 script first to get into the window object. Maybe this process fires an event itself if finished, but I can not find any documentation about it.

mediaessenz commented 4 months ago

I think I found the script which loads the global TYPO3 vars: vendor/typo3/cms-core/Resources/Public/JavaScript/java-script-item-processor.js (Source https://github.com/TYPO3/typo3/blob/12.4/Build/Sources/TypeScript/core/java-script-item-processor.ts)

There is a "typo3:import-javascript-module" event dispatched somewhere in this code, but I'm not sure if it is usefull.

mediaessenz commented 3 months ago

Since the TYPO3 core injects the global backend module js variables by loading it async I do not think that there is a way to fix it from your side. In my opinion, it does not matter when the JS delivered by vite is loaded. It is always uncertain whether the global variables have already been loaded.

Helmut Hummel suggested at the vite slack channel, that this problem could be solved by typo3 by emitting an event when labels are populated, but I'm not sure if this is really error proof since in the end the browsers and its caching mechanism are involved ....

The only save way I see, is to go the "official" way and register the js as an es6 module over the typo3 api. Unfortunately this would kills the main advantage of vite: hot module replacement.