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

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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()

Method 2

One approach you could consider is some form of dependency injection: have your React app receive RandomHelper, or some of it’s properties, from the outside world. Then you can remove it when you’re ready to cut the cord.

var Utils = (function(){
  var self = {
    MyFunction: function(name){
      return `Hello, ${name}!`;
    }
  };
  return self;
})();

var RandomHelper = (function(){
  var self = {
    DoThing: function(name){
      return Utils.MyFunction(name);
    }
  };
  return self;
})();

const ComponentOne = ({hello}) => {
  return <h1>{hello('ComponentOne')}</h1>;
}

const ComponentTwo = ({hello}) => {
  return <h2>{hello('ComponentTwo')}</h2>
}

const App = ({ExternalFunctions}) => {
  return (
    <header>
      <ComponentOne hello={ExternalFunctions.hello} />
      <ComponentTwo hello={ExternalFunctions.hello} />
    </header>
  )
}

ReactDOM.render(
  <App ExternalFunctions={{hello: RandomHelper.DoThing}} />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x