webpack: JsonpMainTemplatePlugin.js uses Promises to resolve dependencies which is not working in Internet Explorer 9-11
Code splitting with require.ensure
for webpack 2.2.0-rc.1 generates loader that uses Promise
"",
"var promise = new Promise(function(resolve, reject) {",
this.indent([
These prevents splitted modules to run normally in any internet explorer 9-11 cause IE doesn’t have Promise
implementation and polyfill is loaded within one of splitted modules.
My generated entry point module looks like this. It loads 2 other modules (1-vendor with Promise implementation, 2 - app code)
var library = function (n) {
function e(t) {
if (r[t])return r[t].exports;
var o = r[t] = {i: t, l: !1, exports: {}};
return n[t].call(o.exports, o, o.exports, e), o.l = !0, o.exports
}
var t = window.webpackJsonp;
window.webpackJsonp = function (e, r, u) {
for (var c, i, a = 0, s = []; a < e.length; a++)i = e[a], o[i] && s.push(o[i][0]), o[i] = 0;
for (c in r)Object.prototype.hasOwnProperty.call(r, c) && (n[c] = r[c]);
for (t && t(e, r, u); s.length;)s.shift()()
};
var r = {}, o = {2: 0};
return e.e = function (n) {
function t() {
u.onerror = u.onload = null, clearTimeout(c);
var e = o[n];
0 !== e && (e && e[1](new Error("Loading chunk " + n + " failed.")), o[n] = void 0)
}
if (0 === o[n])return Promise.resolve();
if (o[n])return o[n][2];
var r = document.getElementsByTagName("head")[0], u = document.createElement("script");
u.type = "text/javascript", u.charset = "utf-8", u.async = !0, u.timeout = 12e4, u.crossOrigin = "anonymous", e.nc && u.setAttribute("nonce", e.nc), u.src = e.p + "main." + {
0: "cdb3",
1: "caf4"
}[n] + ".js";
var c = setTimeout(t, 12e4);
u.onerror = u.onload = t, r.appendChild(u);
var i = new Promise(function (e, t) {
o[n] = [e, t]
});
return o[n][2] = i
}, e.m = n, e.c = r, e.i = function (n) {
return n
}, e.d = function (n, t, r) {
e.o(n, t) || Object.defineProperty(n, t, {configurable: !1, enumerable: !0, get: r})
}, e.n = function (n) {
var t = n && n.__esModule ? function () {
return n.default
} : function () {
return n
};
return e.d(t, "a", t), t
}, e.o = function (n, e) {
return Object.prototype.hasOwnProperty.call(n, e)
}, e.p = "", e.oe = function (n) {
throw n
}, e(e.s = 4)
}({
4: function (n, e, t) {
"use strict";
function r(n, e, r) {
void 0 === n && (n = document.body);
var o = [], u = null, c = function () {
for (var n = 0, e = o; n < e.length; n++) {
var t = e[n];
u.update(t)
}
o.length = 0
};
return t.e(0).then(function () {
t.e(1).then(function (o) {
u = t(0).run(n, e), c(), "function" == typeof r && r(u)
}.bind(null, t)).catch(t.oe)
}.bind(null, t)).catch(t.oe), {
update: function (n) {
u ? u.update(n) : o.push(n)
}
}
}
n.exports = r
}
});
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 8
- Comments: 44 (20 by maintainers)
Here’s a “real world” use case where this seems to be an issue:
Suppose you’re writing a JavaScript plugin that gets included on thousands of websites or more. The plugin provides simple “out of the box” functionality, so that non-technical users can just add a script tag for the plugin to their pages (or even add the tag with the click of a button, via a WordPress plugin) and be done with it. Many of these users won’t know what a Promise polyfill is or how to add one to their sites. At the same time, many of these users might be using various other plugins, some of them quite old – especially if they are using WordPress, where plugins can be added very quickly and almost indiscriminately – or catering to users whom they expect not to be using the most cutting edge browsers.
In a scenario of that sort, including a global polyfill with the plugin tends to be bad for the users: it bloats the plugin and risks breaking pages in the wild, since the polyfill will sometimes modify objects in the global scope, and there may be other plugins on those pages that will expect those global objects to behave slightly differently (especially if they are older plugins), or that will overwrite the polyfill later, and/or there may be users visiting these sites who have browsers that will throw errors under certain conditions when the polyfill is present (and so on, and so on). But because of the number of websites using the plugin, and because of the non-technical nature of many of the users, it also won’t be reasonable for developers of the plugin to expect that these users themselves will add a
Promise
polyfill to their pages (even if asked politely!). So, if the developers of this plugin rely on code-splitting at all, and want to provide the best experience for their users, they seemingly can’t use webpack 2+, given its reliance onPromise
in the bootstrap code related to code-splitting. They’re stuck using webpack 1 or forking.That may be the intention, but I figured I’d put that scenario out there as one where webpack 2+'s behavior can be problematic. (Even so, thanks to the maintainers/contributors for all of the hard work!)
I think part of the issue is that the tools that webpack provides for handling polyfills in the bundle, such as ProvidePlugin, don’t work on the bootstrap code. This might not normally be an issue, but if you’re using a polyfill that doesn’t pollute the global namespace (like babel-runtime), then you either need to load two polyfills (one global and one not) or manually handle mapping the global yourself (eg,
window.Promise = require('my-polyfill-of-choice')
).If ProvidePlugin also worked for the bootstrap code that would probably go a long way toward alleviating this issue.
I am writing a library that makes use of code-splitting. I therefore don’t want to pollute the global namespace and to make it as easy as possible for my users to include the library I also don’t want to force my users to include a specific Polyfill.
What would be arguments against letting
babel-plugin-transform-runtime
transform bootstrap code as well (or alternatively allowingProvidePlugin
to manipulate bootstrap code)?This is still a big problem
I have written a simple plugin to solve this problem :
https://github.com/mc-zone/webpack2-polyfill-plugin
Welcome to try! 😃
The problem for me is that “babel-plugin-transform-runtime” can’t transform it. I found some site use the following code.
<script>window.Promise||document.write('<script src="/js/es6-promise.js"><\/script>')</script>
This may be a suitable solution.@alexander-akait Regardless of IE9/IE10/IE11, there are still use cases when bundling embeddable software and when you don’t want to reuse host website’s Promise which might be polyfilled with some mumbo-jumbo broken Promise implementation.
Just a warning for folks that need to support IE 8-11, using webpack is not a good idea. Webpack needs Promise and Function.bind to be available for webpackJsonp loading, and will break when loading your async loaded code if it is not present.
In this way users could Babel transform that module
If your code is a third party library you really shouldn’t pollute the global namespace.
Kirk Elliott 202.733.0317
Because I do not want to mutate the environment. I already use a promise Ponyfill via Babel transform-runtime and core-js
This is still a huge problem!
On Tue, 15 Oct 2019, 08:43 webpack bot, notifications@github.com wrote:
Any developments on this issue? I’m in exactly the same situation that @jmrog describes (thanks for the eloquent example, by the way!). My code is running on thousands of third party sites, and I absolutely can’t polyfill in any globals. It would be great if
ProvidePlugin
worked for this use-case.I was able to solve this with an approach largely inspired by webpack’s BannerPlugin:
Purpose: Allow application developers to provide their own Promise ponyfill to the webpack runtime without modifying the global Polyfill. This is especially important for 3rd party scripts that need to function on another party’s site without affecting the native environment.
How it works: Adds a local Promise variable to the end of the outermost IIFE in the webpack output for each file matching the given test/include/exclude option(s). These are exactly the same as BannerPlugin’s:
In your webpack config:
At the top of any entry/chunk that will be affected:
The solution:
Caveats: I tried to find a better way than converting to
RawSource
. I imagine it may slow the build down a little. This also relies heavily on the last characters (}()
) remaining correct. I by trying to add a custom runtime module which would contain something like__webpack_require__.Promise = __webpack_require("your-ponyfill")
and then hook into the rest of the RuntimePlugin templates to replacePromise
with${RuntimeGlobals.require}.Promise
. Unfortunately, I didn’t come up with anything that wouldn’t require extending each template individually.That said, it works for every scenario I’ve used it in thus far.
I’ve managed to overcome this issue by providing minimal promise like function to bootstrap. This “Promise” (actually it’s not) will only be used in bootstrap and actual modules will use polyfill provided by
ProvidePlugin
, this “Promise” won’t pollute global scope.Sample plugin
For me it’s not an issue anymore so feel free to close it. Also I think that
ProvidePlugin
should also work with bootstrap code, though it’s really complicatedThis was an intentional change done since the very early betas (https://gist.github.com/sokra/27b24881210b56bbaff7#promise-polyfill).
Apparently it’s missing from the https://webpack.js.org migration guide, but a Promise polyfill is mandatory when using code splitting.
babel-polyfill
andcore-js
include one. You can avoid including the rest ofcore-js
by only requiringcore-js/fn/promise
, or using another polyfill such ases6-promise/auto
.Polyfills also should always be loaded synchronously, and run before any other code. You can ensure webpack generates an entry point that synchronously loads the polyfill by giving an array for the entry point; e.g.