Shims vs HTML Entry Points

If you use a standard HTML Entry Point for development, you may need to provide script loads for js libraries that are declared external in your webpack bundle, hence are not bundled, but are needed for correct operation.

If you use a shim, you either have to modify your shim or modify the shim connector in your jsx file. It's easier to modify the shim connector. You could of course, create a shim that contains a static set of scripts to load using standard script tag but that means you have to specify the list of javascript libraries to load in CRM in that shim file and the sim file becomes less generic. It's better to modify the shim connector that lives inside your .jsx file.

If you have a script like:

export function loadScripts(array, callback){
    var loader = function(src,handler){
        var script = document.createElement("script");
        script.src = src;
        script.onload = script.onreadystatechange = function(){
            script.onreadystatechange = script.onload = null;
            handler();
        }
        var head = document.getElementsByTagName("head")[0];
        (head || document.body).appendChild( script );
    };
    (function run(){
        if(array.length!=0){
            loader(array.shift(), run);
        }else{
            callback && callback();
        }
    })();
}

then your shim connector can look like:

window.addEventListener('load', function() {
    if(typeof runmain !== "undefined" && runmain) {
        // scripts loaded relative to shim location e.g. publisher_/react_shim.html
        // these are the exact same script loaded in the "final" .html for your react app
        loadScripts([
            "dataapi.js",
            "common.js"
        ], () => run());
    }
})

Notice that run() is only called in the callback function after the script loads are complete. Since the scripts are dynamically run, you cannot rely on their load order and calling run() in the callback is the safest approach. You cannot have a script contain HTML, only javascript so you'll want to load your CRM GlobalContext.js.aspx in the shim itself.

If these scripts load global variables that you depend on, the global variables in the dynamically loaded scripts will not have loaded before your entire bundled javascript has been loaded--after all it had to load your main bundle just to get to the window.addEventListener function. Since the globals are not loaded yet, you will generate errors from within your bundle if you required the global using standard "require()" or "import" approaches. Instead, access the global variables you need directly in your modules without the require/import. If you split your load file up and have multiple pieces that get loaded, then you may be ok to declare the require/import but load order problems have always been tricky.

You may find that if you have a "lowest common denominator" file, such as a data API file, that everything depends on, you may include it in your basic shim, just like GlobalContext.js.aspx.

Last updated