Pageworks / papertrain

Papertrain: a Craft CMS 3 framework
http://download.papertrain.io
MIT License
5 stars 2 forks source link

Update Runtime #210

Closed codewithkyle closed 4 years ago

codewithkyle commented 4 years ago

Also see #204 and #208

interface Window
{
    stylesheets : Array<string>
    packages : Array<string>
    components : Array<string>
    modules : Array<string>
    criticalCss : Array<string>
    libraries : Array<string>
}

class Runtime
{
    private _initialFetch : boolean;

    constructor()
    {
        this._initialFetch = true;
        this.init();
    }

    private handleStylesheetsFetchEvent:EventListener = this.getStylesheets.bind(this);
    private handleScriptFetchEvent:EventListener = this.getScripts.bind(this);

    private async fetchFile(element:Element, filename:string, filetype:string, directory:string)
    {
        switch (filetype)
        {
            case 'css':
                element.setAttribute('rel', 'stylesheet');
                element.setAttribute('href', `${ window.location.origin }/automation/${ directory }-${ document.documentElement.dataset.cachebust }/${ filename }.${ filetype }`);
                break;
            case 'js':
                element.setAttribute('type', 'text/javascript');
                element.setAttribute('src', `${ window.location.origin }/automation/${ directory }-${ document.documentElement.dataset.cachebust }/${ filename }.${ filetype }`);
                break;
        }
    }

    private fetchResources(fileListArray:Array<string>, element:string, filetype:string, directory:string) : Promise<any>
    {
        return new Promise((resolve) => {
            if (fileListArray.length === 0)
            {
                resolve();
            }

            let count = 0;
            const required = fileListArray.length;

            while (fileListArray.length > 0)
            {
                const filename = fileListArray[0].replace(/(\.js)$|(\.css)$/gi, '');
                let el = document.head.querySelector(`${ element }[file="${ filename }.${ filetype }"]`);
                if (!el)
                {
                    el = document.createElement(element);
                    el.setAttribute('file', `${ filename }.${ filetype }`);
                    document.head.appendChild(el);
                    el.addEventListener('load', () => {
                        count++;
                        if (count === required)
                        {
                            resolve();
                        }
                    });
                    this.fetchFile(el, filename, filetype, directory);
                }
                else
                {
                    count++;
                    if (count === required)
                    {
                        resolve();
                    }
                }

                fileListArray.splice(0, 1);
            }
        });
    }

    private criticalCssLoadCallback() : void
    {
        // Do something after the stylesheets finish loading
        const pageLoadingElement = document.body.querySelector('page-loading');
        setTimeout(() => {
            pageLoadingElement.classList.remove('is-loading');
        }, 250);
    }

    private loadingCompleteCallback() : void
    {
        if (this._initialFetch)
        {
            this._initialFetch = false;

            /** Do something on initial load */
        }

        /** Do something every time the app loads or reloads */
    }

    private librariesLoadCallback() : void
    {
        /** Do something with 3rd party libraries */
    }

    private modulesLoadCallback() : void
    {
        const modulesLoadedEvent = new CustomEvent('runtime:modulesLoaded');
        document.dispatchEvent(modulesLoadedEvent);
    }

    private async getScripts()
    {
        try
        {
            await this.fetchResources(window.packages, 'script', 'js', 'packages');
            await this.fetchResources(window.libraries, 'script', 'js', 'libraries');
            this.librariesLoadCallback();
            await this.fetchResources(window.modules, 'script', 'js', 'modules');
            this.modulesLoadCallback();
            await this.fetchResources(window.components, 'script', 'js', 'components');
            this.loadingCompleteCallback();
        }
        catch (error)
        {
            console.error(error);
        }
    }

    private async getStylesheets()
    {
        try
        {
            await this.fetchResources(window.criticalCss, 'link', 'css', 'styles');
            this.criticalCssLoadCallback();
            await this.fetchResources(window.stylesheets, 'link', 'css', 'styles');
        }
        catch (error)
        {
            console.error(error);
        }
    }

    private init() : void
    {
        if ('requestIdleCallback' in window)
        {
            // @ts-ignore
            window.requestIdleCallback(()=>{
                this.getStylesheets();
                this.getScripts();
            });
        }
        else
        {
            console.warn('Idle callback prototype not available in this browser, fetching stylesheets');
            this.getStylesheets();
            this.getScripts();
        }

        document.addEventListener('fetch:stylesheets', this.handleStylesheetsFetchEvent);
        document.addEventListener('fetch:scripts', this.handleScriptFetchEvent);
    }
}
codewithkyle commented 4 years ago

Released in Papertrain v0.3.1 (#211)