Loading dependencies with or without react

Viewed 302

I have an old IIFE that is injected into legacy pages via <script src.

However, I want to use all these old libraries in a react app. I just need to use the global function exposed.

I figure loading dependencies that will work both via script or via react's import or nodejs require

Here is an example of an example IIFE

example.js :

var $ = $;
var geocomplete = $.fn.geocomplete;
var OtherExternalLib = OtherExternalLib;

var Example = (function() {
    return {
        init: function () {
            // stuff
        }
    }
)();

Where the legacy code is calling Example.init(), and likewise the react code will call the same function.

Where $ (jQuery), $.fn.geocomplete, and OtherExternalLib are all dependencies that must be loaded, either they should be loaded on-demand or just throw a big loud error message.

I suspect if the solution loads dynamically example.js would look something like

var $ = load("\libs\jquery.js");
var geocomplete = load("\libs\$.fn.geocomplete.js");
var OtherExternalLib = load("\libs\OtherExternalLib.js");

var Example = (function() {
    return {
        init: function () {
            // stuff
        }
    }
)();

And the legacy application can still use <script src=example.js and React can use import {Example} from example

Understandably this is somewhat a round-about way to of using legacy code in new applications, so I am open to other ideas on how best to expose an IIFE (with or without dependencies) and using it in React

1 Answers

I am using react+typescript in my project with some limitations which is why I had to dynamically import my package (my project runs in a shell project with AMD module, not having my own startup, and change the way project files get bundled).

Since I could only use the dependent modules on the fly during the run time, I had to assume them were valid while building and bundling . Most of them were IIFE. So I used the lazy dynamic import .

something like this

import("somePolyfill");

This would be translated by TSC

new Promise(function (resolve_3, reject_3) { require(["arrayPolyfill"], resolve_3, reject_3); }); 

This would call the IIFE and execute the polyfills or initializing any window or global variable, so the rest of the code is aware of that. If it returns a module or throughs error can be handled like normal promise then and catch.

So I created a wrapper

export class DepWrap {
public static Module: any = {};

public constructor() {
    this.getPI();
    this.getSomeModule();
}

public async getPI() {
    DepWrap.Module["PI"] = 3.14;
}

public async getSomeModule() {
    await import('somepath/somemodule').then(($package) => {
        DepWrap.Module["somemodule"] = $package;
    }).catch(() => {
        window.console.log("Some Module error");
    });
}

}

this got compiled to

define(["require", "exports", "tslib"], function (require, exports, tslib_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DepWrap = /** @class */ (function () {
    function DepWrap() {
        this.getPI();
        this.getSomeModule();
    }
    DepWrap.prototype.getPI = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                DepWrap.Module["PI"] = 3.14;
                return [2 /*return*/];
            });
        });
    };
    DepWrap.prototype.getSomeModule = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, new Promise(function (resolve_1, reject_1) { require(['somepath/somemodule'], resolve_1, reject_1); }).then(function ($package) {
                            DepWrap.Module["somemodule"] = $package;
                        }).catch(function () {
                            window.console.log("Some Module error");
                        })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    DepWrap.Module = {};
    return DepWrap;
}());
exports.DepWrap = DepWrap;

});

with this I could use all the dependency modules from my wrapper and every time i need to import a new one I would create another function to add that to my wrap module.

import { DepWrap } from "wrapper/path";
const obj = new DepWrap(); // initialize once in the beginning of project so it would import all the dependencies one by one .

Afterwards in all file, I can import my module from the wrapper

import { DepWrap } from "wrapper/path";
const { PI, somemodule} = DepWrap.Module;

I am not sure if the code will work for your scenario as well, but I guess tweaking it a bit might come in handy for your useCase .

Plus : if you are writing unit test case it will help jest to just ignore the modules and can create a mock for this so that you can test your actual code .

Related