Search code examples
javascriptreactjsdocusaurus

Undertake operations upstream to Docusaurus and ensure the correct order of loading files


I'm taking over a website by V2 Docusaurus.

One particularity of our website is that we need to load office.js and css-vars-ponyfill.min.js, and do some operations in the very beginning. So the previous developer decided to use the following approach.

In every .mdx.md page, he wrapped the content by a component MainWrapper:

<MainWrapper>
    ... ...
    Real content
    ... ...
</MainWrapper>

MainWrapper/index.js is defined as follows

function MainWrapper(props) {
    return (<>
        <Head>
            <script defer 
                src="https://www.10studio.tech/lib/patches.js" 
                onload="(function(){console.log('patches.js fully loaded MainWrapper')}).call(this)" >
            </script>
        </Head>
        <CssvarsWrapper></CssvarsWrapper>
        <OfficejsWrapper></OfficejsWrapper>
        {props.children}
    </>)
}

export default MainWrapper;

CssvarsWrapper/index.js is defined as follows

function CssvarsWrapper(props) {
    return (<>
        <Head>
            <script defer 
                src="https://www.10studio.tech/lib/patches.js"
                onload="(function(){console.log('patches.js fully loaded in CssvarsWrapper')}).call(this)">
            </script>
            {console.log("CssvarsWrapper > index.js > CssvarsWrapper")}
            <script defer
                src="https://unpkg.com/css-vars-ponyfill@2/dist/css-vars-ponyfill.min.js"
                onload="(function(){console.log('css-vars-ponyfill.min.js fully loaded in CssvarsWrapper'); onCssVarsPonyfillLoad()}).call(this) ">
            </script>
        </Head>
        {props.children}
    </>)
}

OfficejsWrapper/index.js is defined as follows

function OfficeWrapper(props) {
    return (
        <>
            <Head>
                <script defer
                    src="https://www.10studio.tech/lib/patches.js"
                    onload="(function(){console.log('patches.js fully loaded in OfficeWrapper')}).call(this)">
                </script>
                {console.log("OfficejsWrapper > index.js > OfficeWrapper")}
                <script defer
                    src='https://appsforoffice.microsoft.com/lib/1/hosted/office.js'
                    onload="(function(){console.log('office.js fully loaded in OfficeWrapper'); onOfficejsLoad()}).call(this) ">
                </script>
            </Head>
            {props.children}
        </>
    )
}

lib/Patches.js contains real operations:

console.log("in patches")
... ...

function onCssVarsPonyfillLoad() {
    console.log("patches.js > onCssVarsPonyfillLoad()")
    cssVars({
        onlyLegacy: false,
        onComplete: function (cssText, styleElms, cssVariables, benchmark) {
        }
    });
}

function onOfficejsLoad() {
    Office.onReady(function () {
        console.log("office.js is ready.");
        patch();
    })
}

However, my test showed that this implementation cannot always respect a correct order of loading of files, regardless of the defer tag. For example, as the following screenshot shows, css-vars-ponyfill.min.js fully loaded in CssvarsWrapper and office.js fully loaded in OfficeWrapper were before patches.js fully loaded, as a consequence onCssVarsPonyfillLoad and onOfficejsLoad were not ready when they were called.

Actually, we should ensure that patches.js is always loaded before css-vars-ponyfill.min.js and office.js. Does anyone know how to ensure that?

Additionally, is this approach (i.e., wrapping a component around content of every page to do some operations upstream) correct?

enter image description here


Solution

  • The way the previous developer decided to implement this loads scripts multiple times and particular it loads patches.js more than once.

    I suggest you try to ditch this implementation and use Docusaurus scripts-array inside docusaurus.config.js to define these scripts and use defer on office.js and css-vars-ponyfill.min.js and define the onload script for each script there. That's the proper way to load external (and internal like patches.js) scripts in Docusaurus.

    Set the scripts inside docusaurus.config.js:

    module.exports = {
      ...
      scripts: [
        {
          src: '/lib/patches.js'
        },
        {
          src: 'https://appsforoffice.microsoft.com/lib/1/hosted/office.js',
          defer: true,
          onload: "(() => { console.log('office.js loaded'); onOfficejsLoad(); })()"
        },
        {
          src: 'https://unpkg.com/css-vars-ponyfill@2/dist/css-vars-ponyfill.min.js',
          defer: true,
          onload: "(() => { console.log('css-vars-ponyfill.min.js loaded'); onCssVarsPonyfillLoad(); })()"
        }
      ],
    }
    

    We use defer on these two scripts and that means that the scripts will be executed and loaded after the document has been parsed, but before firing DOMContentLoaded.

    Trying this on a local Docusaurus project I got the scripts loading on the expected order every single time after clearing the cache:

    in patches
    office.js loaded
    patches.js > onCssVarsPonyfillLoad()
    css-vars-ponyfill.min.js loaded
    patches.js > onCssVarsPonyfillLoad()
    cssVars:onComplete
    office.js is ready.
    

    using this patches.js file:

    console.log("in patches")
    
    function onCssVarsPonyfillLoad() {
      console.log("patches.js > onCssVarsPonyfillLoad()");
    
      cssVars({
        onlyLegacy: false,
        onComplete: function (cssText, styleElms, cssVariables, benchmark) {
          console.log('cssVars:onComplete');
        }
      });
    }
    
    function onOfficejsLoad() {
      console.log("patches.js > onCssVarsPonyfillLoad()");
    
      Office.onReady(function () {
        console.log("office.js is ready.");
        // patch();
      })
    }