Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load and consume legacy JS modules (e.g. IIFEs) via ES6 module imports

I have IIFE functions for some of the library code in an legacy application that needs to work for IE10+ (No ES6 module loading, etc).

However, I am starting to develop an React app that will be using ES6 and TypeScript and I want to reuse the code I already have without duplicating the files. After a bit of research I found that I'd want to use a UMD pattern to allow these library files to work both as <script src=*> imports and to allow the React app to import them via ES6 module loading.

I came up with the following conversion:

var Utils = (function(){
  var self = {
    MyFunction: function(){
      console.log("MyFunction");
    }
  };
  return self;
})();

to

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.Utils = {})));
}(this, (function (exports) { 
  exports.MyFunction = function(){
      console.log("MyFunction");
    };
})));

This will allow loading via Import Utils from './Utils.js' command and also allow it to be inserted using a script tag <script src='Utils.js'></script>

However, some of my IIFE use other IIFE's as a dependency (bad I know but a reality).

var Utils = Utils; // Used to indicate that there is dependency on Utils
var RandomHelper = (function(){
  var self = {
    DoThing: function(){
      Utils.MyFunction();
    }
  };
  return self;
})();

If correctly turn RandomHelper and Utils into files that can be imported, the React app isn't compatible with this technique. Doing simply

Import Utils from './Utils.js'
Import RandomHelper from './RandomHelper.js'

does not work because I believe Utils is not window scoped. It will load without issue but RandomHelper.DoThing() will throw that Utils is not defined.

In the legacy app

<script src='Utils.js'></script>
<script src='RandomHelper.js'></script>

works flawlessly.

How can I have RandomHelper be able to use Utils in a React app, keeping it IE and ES5 compatible but still work in react. Perhaps somehow setting a window/global variable?

PS: I understand the point of the ES6 module loading is to deal with dependencies and my existing IIFEs are not ideal. I plan to eventually switch es6 classes and better dependency control but for now I want to use whats available to be without re-writing

like image 895
ParoX Avatar asked Oct 20 '19 04:10

ParoX


1 Answers

Let's get this out of the way first, module features, if not explicitly exported, are privately scoped to the defining module. You can't get around this fact. But there are work-around options you may consider.

1. Assuming minimal modification of legacy code is acceptable

A work around with minimal changes to your legacy code would be to simply add Utils and RandomHelper to the window object. For instance, change var Utils = (...)(); to window.Utils = (...)();. Consequently, the object will be accessible from the global object by both legacy codes (loaded via import) and newer code base.

2. Assuming absolutely no modification in the legacy code can be tolerated

A new ES6 module should be created as proxy for loading the legacy scripts:

// ./legacy-main.js

const utilsScript = await fetch( './Utils.js' )
const randomHelperScript = await fetch( './RandomHelper.js' )

const utilsScriptText = await utilsScript.text()
const randomHelperScriptText = await randomHelperScript.text()

// Support access to `Utils` via `import` 
export const Utils = Function( `${utilsScriptText}; return Utils;` )()
// Additionally support access via global object 
Object.defineProperty(window, 'Utils', { value: Utils })

// Support access to `RandomHelper` via `import`
// Note that `Utils` which is a dependency for `RandomHelper` ought to be explicitly injected
// into the scope of execution of `RandomHelper`.
export const RandomHelper = Function( 'Utils', `${randomHelperScriptText}; return RandomHelper;` )( Utils )
// Additionally support access via global object 
Object.defineProperty(window, 'RandomHelper', { value: RandomHelper })

Finally, you may import Utils and RandomHelper from legacy-main.js when required:

import { Utils, RandomHelper } from './legacy-main.js'

Utils.MyFunction()
RandomHelper.DoThing()
like image 134
Igwe Kalu Avatar answered Nov 15 '22 16:11

Igwe Kalu