1

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

SoftTimur
  • 5,630
  • 38
  • 140
  • 292

2 Answers2

1

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();
  })
}
Christos Lytras
  • 36,310
  • 4
  • 80
  • 113
  • I removed `defer` for `patches.js` in the three files, but it had still the same error. Note that unlike your snippet, I have three files `MainWrapper/index.js`, `CssvarsWrapper/index.js` and `OfficejsWrapper/index.js`. I don't know if that complicates the issue. – SoftTimur Apr 24 '20 at 17:07
  • I'll try to reproduce it exactly as you have it and I'll report back. – Christos Lytras Apr 24 '20 at 17:11
  • @SoftTimur please check my new answer. I've also tested this on a new Docusaurus project and I can upload it on Github if you want to check it out. – Christos Lytras Apr 24 '20 at 20:31
  • Thank you. I tried `scripts:` with a recent version of Docusaurus, it did ensure the loading order. However, for my case, my website has to run under IE, [I cannot make the recent version of Docusaurus work with IE](https://github.com/facebook/docusaurus/issues/2657). So I have to stick with my previous version of Docusaurus which yet does not permit of `scripts:`. I think I still have to find out a way to load files in order inside the components... – SoftTimur Apr 25 '20 at 08:21
  • Indeed it does not work on IE. Which version are you using now for IE, V1? I can help to implement this with a more proper way. – Christos Lytras Apr 25 '20 at 09:25
  • I mean which version of Docusaurus, V1? – Christos Lytras Apr 25 '20 at 09:27
  • Please see [this issue](https://github.com/facebook/docusaurus/issues/2657), my developer added some polyfills to a previous version, which was still V2. – SoftTimur Apr 25 '20 at 09:28
  • I see `2.0.0-alpha.24` in the [commit](https://github.com/BurdenBear/docusaurus/compare/78159f6..f5c6ffd) that issue is pointint out. – Christos Lytras Apr 25 '20 at 09:44
  • Though it is V2, my test showed that `scripts-array` did not exist in this version. – SoftTimur Apr 25 '20 at 09:47
  • Yes it doesn't support `scripts-array`. I suggest you keep only the `MainWrapper`, remove both `CssvarsWrapper` and `OfficejsWrapper` components and have all the scripts inside the `MainWrapper` `` in the correct order and using `defer` only for `office.js` and `css-vars-ponyfill.min.js`. Just remember to change the arrow function `() => {}` to a normal function for the `onload` script tag calls. – Christos Lytras Apr 25 '20 at 10:27
0

TLDR;

You need to add to add defer to your <script> tag if you want your scripts to be executed in order.

You can read more about it here: https://www.w3schools.com/tags/att_script_defer.asp


A bit longer version

I want to highlight few things over your implementation though

You are working in a virtual DOM env, your components might mount/unmount depending over many use cases. I would never recommend any one loading files like this:

<script 
    src="/lib/patches.js" 
    onload="(function(){console.log('patches.js fully loaded 1')}).call(this)">
</script>

Instead I would recommend defining the methods in a single file and export it so I can just use any method I want to use within my app. This will not just kill the loading of file each time your component is mounted but also improve the efficiency and execution.

This approach is rather called modular approach. Check this out https://stackoverflow.com/a/49616670/1681154 and if you want to read more about modular approach, I found a good explanation here: https://javascript.info/import-export.

If you can't afford to break down the modules but rather want to use the approach you are currently using, you need to use defer to ensure script is executed in the same order it is defined.

Saurabh Sharma
  • 2,422
  • 4
  • 20
  • 41
  • I just tested, `defer` did not work... Please see my OP. – SoftTimur Apr 24 '20 at 02:23
  • Additionally, I don't know how to turn the code to the modular approach (e.g., how to use `office.js` and `css-vars-ponyfill.min.js` as you suggested). – SoftTimur Apr 24 '20 at 02:29