Skip to content

Instantly share code, notes, and snippets.

@developit
Last active July 25, 2023 12:54
Show Gist options
  • Save developit/220246bc99044af74a6680ce13284f59 to your computer and use it in GitHub Desktop.
Save developit/220246bc99044af74a6680ce13284f59 to your computer and use it in GitHub Desktop.

Module Workers Polyfill npm version

This is a 1.1kb polyfill for Module Workers.

It adds support for new Worker('..',{type:'module'}) to all modern browsers (those that support fetch).

Usage

Copy module-workers-polyfill.js to your web directory, then load it using an import or a script tag. It just needs to be loaded before instantiating your Worker.

<script src="/module-workers-polyfill.js"></script>
<script>
  const worker = new Worker('/worker.mjs', { type: 'module' });
</script>

Example

// load the polyfill:
import '/module-workers-polyfill.js';

// now we can use import-from-worker, which relies on Module Workers:
import importFromWorker from 'https://unpkg.com/import-from-worker@1.0.1/dist/import-from-worker.js';

function getAcornAST(code) {
  const { parse } = await importFromWorker('https://unpkg.com/acorn@7.1.0/dist/acorn.mjs');
  return await parse(code, { sourceType: 'module' });
}
(function(W) {
if (W && W._$P === true) return;
// if Worker is present and doesn't support Module Workers, install polyfill:
if (W) {
var s;
var OPTS = Object.defineProperty({}, 'type', {
get: function() {
s = true;
}
});
try {
var url = URL.createObjectURL(new Blob([''],{type:'text/javascript'}));
new W(url, OPTS).terminate();
URL.revokeObjectURL(url);
} catch (e) {}
if (!s) {
try {
new W("data:text/javascript,", OPTS).terminate();
} catch (e) {}
}
if (s) return;
(self.Worker = function Worker(url, opts) {
if (opts && opts.type == "module") {
opts = { name: url + '\n' + (opts.name || '') };
url = typeof document == "undefined" ? location.href : (document.currentScript && document.currentScript.src) || new Error().stack.match(/[(@]((file|https?):\/\/[^)]+?):\d+(:\d+)?(?:\)|$)/m)[1];
}
return new W(url, opts);
})._$P = true;
}
function p() {
// esm-polyfill
var r = {}, n = {}, x;
function e(r, e) {
for (
e = e.replace(/^(\.\.\/|\.\/)/, r.replace(/[^/]+$/g, "") + "$1");
e !== (e = e.replace(/[^/]+\/\.\.\//g, ""));
);
return e.replace(/\.\//g, "");
}
function t(s, u) {
var o, a = s;
u && (s = e(u, s));
return r[s] || (r[s] = fetch(s).then(function(u) {
if ((a = u.url) !== s) {
if (null != r[a]) return r[a];
r[a] = r[s];
}
return u.text().then(function(r) {
if (!u.ok) throw r;
var m = { exports: {} };
o = n[a] || (n[a] = m.exports);
var i = function(r) { return t(r, a); }, c = [];
return (
(r = (function(r, e) {
e = e || [];
var n, t = [], j = 0;
function u(r, e) {
for (var s, u = /(?:^|,)\s*([\w$]+)(?:\s+as\s+([\w$]+))?\s*/g, o = []; s = u.exec(r); )
e ? t.push((s[2] || s[1]) + ":" + s[1]) : o.push((s[2] || s[1]) + "=" + n + "." + s[1]);
return o;
}
return (
r = r
.replace(
/(^\s*|[;}\s\n]\s*)import\s*(?:(?:([\w$]+)(?:\s*\,\s*\{([^}]+)\})?|(?:\*\s*as\s+([\w$]+))|\{([^}]*)\})\s*from)?\s*(['"])(.+?)\6/g,
function(r, t, o, a, b, i, c, p) {
return (
e.push(p),
(t += "var " + (n = "$im$" + ++j) + "=$require(" + c + p + c + ")"),
o && (t += ";var " + o + " = 'default' in " + n + " ? " + n + ".default : " + n),
b && (t += ";var " + b + " = " + n),
(a = a || i) && (t += ";var " + u(a, !1)),
t
);
}
)
.replace(
/((?:^|[;}\s\n])\s*)export\s*(?:\s+(default)\s+|((?:async\s+)?function\s*\*?|class|const\s|let\s|var\s)\s*([a-zA-Z0-9$_{[]+))/g,
function(r, e, n, u, o) {
if (n) {
var a = "$im$" + ++j;
return t.push("default:" + a), e + "var " + a + "=";
}
return t.push(o + ":" + o), e + u + " " + o;
}
)
.replace(
/((?:^|[;}\s\n])\s*)export\s*\{([^}]+)\}\s*;?/g,
function(r, e, n) {
return u(n, !0), e;
}
)
.replace(
/((?:^|[^a-zA-Z0-9$_@`'".])\s*)(import\s*\([\s\S]+?\))/g,
"$1$$$2"
)
).replace(
/((?:^|[^a-zA-Z0-9$_@`'".])\s*)import\.meta\.url/g,
"$1" + JSON.stringify(s)
) + "\n$module.exports={" + t.join(",") + "}";
})(r, c)),
Promise.all(
c.map(function(r) {
var s = e(a, r);
return s in n ? n[s] : t(s);
})
).then(function(e) {
r += '\n//# sourceURL=' + s;
try {
var f = new Function("$import", "$require", "$module", "$exports", r);
} catch (e) {
var line = e.line - 1;
var column = e.column;
var lines = r.split('\n');
var stack = (lines[line-2] || '') + '\n' + lines[line-1] + '\n' + (column==null?'':new Array(column).join('-')+'^\n') + (lines[line] || '');
var err = new Error(e.message + '\n\n' + stack, s, line);
err.sourceURL = err.fileName = s;
err.line = line;
err.column = column;
throw err;
}
var n = f(i, function(r) { return e[c.indexOf(r)]; }, m, m.exports);
return (
null != n && (m.exports = n),
Object.assign(o, m.exports),
m.exports
);
})
);
});
}));
}
// import while queuing messages
var q = [], m = q.push.bind(q);
addEventListener("message", m);
function d() {
removeEventListener("message", m);
q.map(dispatchEvent);
}
var u = self.name.match(/^[^\n]+/)[0];
self.name = self.name.replace(/^[^\n]*\n/g, '');
t(u).then(d).catch(function(e) {
setTimeout(function() {
throw e;
});
});
}
if (typeof document == "undefined") p();
})(self.Worker);
!function(e){if(!e||!0!==e._$P){if(e){var n,r=Object.defineProperty({},"type",{get:function(){n=!0}});try{var t=URL.createObjectURL(new Blob([""],{type:"text/javascript"}));new e(t,r).terminate(),URL.revokeObjectURL(t)}catch(e){}if(!n)try{new e("data:text/javascript,",r).terminate()}catch(e){}if(n)return;(self.Worker=function(n,r){return r&&"module"==r.type&&(r={name:n+"\n"+(r.name||"")},n="undefined"==typeof document?location.href:document.currentScript&&document.currentScript.src||(new Error).stack.match(/[(@]((file|https?):\/\/[^)]+?):\d+(:\d+)?(?:\)|$)/m)[1]),new e(n,r)})._$P=!0}"undefined"==typeof document&&function(){var e={},n={};function r(e,n){for(n=n.replace(/^(\.\.\/|\.\/)/,e.replace(/[^/]+$/g,"")+"$1");n!==(n=n.replace(/[^/]+\/\.\.\//g,"")););return n.replace(/\.\//g,"")}var t=[],s=t.push.bind(t);addEventListener("message",s);var a=self.name.match(/^[^\n]+/)[0];self.name=self.name.replace(/^[^\n]*\n/g,""),function t(s,a){var u,o=s;return a&&(s=r(a,s)),e[s]||(e[s]=fetch(s).then((function(a){if((o=a.url)!==s){if(null!=e[o])return e[o];e[o]=e[s]}return a.text().then((function(e){if(!a.ok)throw e;var c={exports:{}};u=n[o]||(n[o]=c.exports);var i=function(e){return t(e,o)},f=[];return e=function(e,n){n=n||[];var r,t=[],a=0;function u(e,n){for(var s,a=/(?:^|,)\s*([\w$]+)(?:\s+as\s+([\w$]+))?\s*/g,u=[];s=a.exec(e);)n?t.push((s[2]||s[1])+":"+s[1]):u.push((s[2]||s[1])+"="+r+"."+s[1]);return u}return(e=e.replace(/(^\s*|[;}\s\n]\s*)import\s*(?:(?:([\w$]+)(?:\s*\,\s*\{([^}]+)\})?|(?:\*\s*as\s+([\w$]+))|\{([^}]*)\})\s*from)?\s*(['"])(.+?)\6/g,(function(e,t,s,o,c,i,f,p){return n.push(p),t+="var "+(r="$im$"+ ++a)+"=$require("+f+p+f+")",s&&(t+=";var "+s+" = 'default' in "+r+" ? "+r+".default : "+r),c&&(t+=";var "+c+" = "+r),(o=o||i)&&(t+=";var "+u(o,!1)),t})).replace(/((?:^|[;}\s\n])\s*)export\s*(?:\s+(default)\s+|((?:async\s+)?function\s*\*?|class|const\s|let\s|var\s)\s*([a-zA-Z0-9$_{[]+))/g,(function(e,n,r,s,u){if(r){var o="$im$"+ ++a;return t.push("default:"+o),n+"var "+o+"="}return t.push(u+":"+u),n+s+" "+u})).replace(/((?:^|[;}\s\n])\s*)export\s*\{([^}]+)\}\s*;?/g,(function(e,n,r){return u(r,!0),n})).replace(/((?:^|[^a-zA-Z0-9$_@`'".])\s*)(import\s*\([\s\S]+?\))/g,"$1$$$2")).replace(/((?:^|[^a-zA-Z0-9$_@`'".])\s*)import\.meta\.url/g,"$1"+JSON.stringify(s))+"\n$module.exports={"+t.join(",")+"}"}(e,f),Promise.all(f.map((function(e){var s=r(o,e);return s in n?n[s]:t(s)}))).then((function(n){e+="\n//# sourceURL="+s;try{var r=new Function("$import","$require","$module","$exports",e)}catch(n){var t=n.line-1,a=n.column,o=e.split("\n"),p=(o[t-2]||"")+"\n"+o[t-1]+"\n"+(null==a?"":new Array(a).join("-")+"^\n")+(o[t]||""),l=new Error(n.message+"\n\n"+p,s,t);throw l.sourceURL=l.fileName=s,l.line=t,l.column=a,l}var m=r(i,(function(e){return n[f.indexOf(e)]}),c,c.exports);return null!=m&&(c.exports=m),Object.assign(u,c.exports),c.exports}))}))})))}(a).then((function(){removeEventListener("message",s),t.map(dispatchEvent)})).catch((function(e){setTimeout((function(){throw e}))}))}()}}(self.Worker);
{
"name": "module-workers-polyfill",
"main": "module-workers-polyfill.min.js",
"version": "0.3.2",
"license": "Apache-2.0",
"homepage": "https://gist.github.com/developit/220246bc99044af74a6680ce13284f59",
"scripts": {
"prepare": "npx terser module-workers-polyfill.js -cmo module-workers-polyfill.min.js",
"prepack": "mv '*Module Workers Polyfill.md' README.md",
"postpack": "mv README.md '*Module Workers Polyfill.md'"
}
}
@ravicious
Copy link

I tried using it in my project under Firefox 94. Loading this polyfill before my script which inits the worker seems to make variables disappear from the global scope. document doesn't work as well as other global variables created before the polyfill was loaded. If I make the polyfill be the first script that's loaded it causes the same problem.

@Armster15
Copy link

This was literally a life saver, thank you so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment