When making an app to run on a web page with ES6 code, multiple instances of the app have conflicts when using static variables or singletons.
Iframes are commonly used and provide code isolation between the instances. For example, an ad on a website can be embedded without conflicting with another ad.
One method I've seen suggested and works ok is to publish the application as an IIFE/UMD using Babel and then wrap this inside a function where the function runs the IIFE, creating a new instance of the entire app, including dependencies and returns the exports as an API. This isolates statics and singletons.
export wrappedModule = function() {
var module = {IIFE bundle, which exposes exports};
return exposedExports;
}
This works but I'm trying to figure out how best to automate the wrapping. So far, I setup a module and used a copy/replace process to embed the entire UMD/IIFE output from Babel inside the wrapped function.
Everything works but it feels like a method that shouldn't be relied on for production builds because the final output is being patched together manually.
I looked into making a custom Babel plugin to transform the code based on the Babel UMD plugin by modifying the buildWrapper. This is quite complex to setup but I will try to get this working if there isn't a simpler way:
https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-modules-umd/src/index.ts
Is there an existing production workflow (bundlers, plugins etc) for isolating entire Javascript module code in a wrapper function? I read some mentions of embedded Javascript widgets like the following that reference Google Analytics widgets:
https://blog.jenyay.com/building-javascript-widget/
These are usually quite simple apps without dependencies that only need a single instance of the widget so they can just build as a UMD/IIFE without wrapping. For multiple instances of the same widget, there needs to be another layer of isolation.
Added code example. With embedWrapped, I can create new versions of an entire module with independent statics and singletons unlike with direct imports of embed.js. Even if I added a separate import like import { Embedded as e2 } from "./embed.js"; in page.js, this refers to the same value as the first import.
// embed-wrapped.js
export const embedWrapped = function () {
const isolated = (function () {
class Embedded { }
Embedded.value = Math.random();
window.exported = { Embedded };
})();
return window.exported;
}
// embed.js
export class Embedded { }
Embedded.value = Math.random();
//page.js
import { Embedded } from "./embed.js";
import { embedWrapped } from "./embed-wrapped.js";
console.log(Embedded.value);
const embed1 = embedWrapped();
console.log(embed1.Embedded.value);
const embed2 = embedWrapped();
console.log(embed2.Embedded.value);