IE11 Dialog Promise Undefined

I’m in the process of updating an existing app using (Aurelia Framework 1.1.4) to the latest version of Aurelia. This is a business app therefore IE11 is still supported.

I created an App using the CLI then added the various pieces I use in the existing app, validation, dialog, etc to the app created with CLI.

The irony is that I had this working, dialog and validation. In my arrogance I did not save what I had, instead I just copied over the src folder from my existing app then I started to get an error:

Possible Unhandled Promise Rejection: ReferenceError: 'Promise' is undefined

When I started over I can’t get Aurelia Dialog working again. I don’t know what I did in the first place to get it to work. I searched the internet high and low but can’t find anything, which is odd because the problem is easy to reproduce. Create a new app, add aurelia-dialog, change main.ts that is all it takes.

CLI Version

Global aurelia-cli v1.2.3

Here is my setup

Would you like to use the default setup or customize your choices? · Custom App
Which bundler would you like to use? · Webpack
Which HTTP Protocol do you wish the outputted Webpack bundle to be optimised for? · HTTP/1.1
What platform are you targeting? · Web
What transpiler would you like to use? · TypeScript
How would you like to setup your HTML template? · None
What css preprocessor would you like to use? · None
Do you want to add PostCSS processing · None
Which unit test runner would you like to use? · None
Would you like to configure integration testing? · None
What is your default code editor? · Visual Studio Code
Which features do you want scaffolded into your project? · Minimum
Would you like to add a Dockerfile? · No

After that I do

au run

I get

Hellow world!

I then install Aureila Dialog

npm install aurelia-dialog

Installs 2.0.0

Add the following to main.ts

.plugin(PLATFORM.moduleName('aurelia-dialog'))

Which looks like this

  aurelia.use
    .standardConfiguration()
    .plugin(PLATFORM.moduleName('aurelia-dialog'))
    .feature(PLATFORM.moduleName('resources/index'));

do au run load the page and I get this

DOM7011: The code on this page disabled back and forward caching. For more information, see: http://go.microsoft.com/fwlink/?LinkID=291337
192.168.1.17:8081
HTML1300: Navigation occurred.
192.168.1.17:8081
[WDS] Live Reloading enabled.
DEBUG [aurelia] Loading plugin aurelia-templating-binding.
DEBUG [aurelia] Configured plugin aurelia-templating-binding.
DEBUG [aurelia] Loading plugin aurelia-templating-resources.
DEBUG [aurelia] Configured plugin aurelia-templating-resources.
DEBUG [aurelia] Loading plugin aurelia-event-aggregator.
DEBUG [aurelia] Configured plugin aurelia-event-aggregator.
DEBUG [aurelia] Loading plugin aurelia-history-browser.
DEBUG [aurelia] Configured plugin aurelia-history-browser.
DEBUG [aurelia] Loading plugin aurelia-templating-router.
DEBUG [aurelia] Configured plugin aurelia-templating-router.
DEBUG [aurelia] Loading plugin aurelia-dialog.
Possible Unhandled Promise Rejection: ReferenceError: 'Promise' is undefined
   "Possible Unhandled Promise Rejection:"
   {
      [functions]: ,
      __proto__: { },
      __symbol:hasInstance0.29453283920868646: undefined,
      __symbol:isConcatSpreadable0.29453283920868647: undefined,
      __symbol:iterator0.29453283920868641: undefined,
      __symbol:match0.29453283920868642: undefined,
      __symbol:replace0.29453283920868643: undefined,
      __symbol:search0.29453283920868644: undefined,
      __symbol:species0.29453283920868649: undefined,
      __symbol:split0.29453283920868645: undefined,
      __symbol:toPrimitive0.294532839208686410: undefined,
      __symbol:toStringTag0.294532839208686411: undefined,
      __symbol:unscopables0.29453283920868648: undefined,
      description: "'Promise' is undefined",
      message: "'Promise' is undefined",
      name: "ReferenceError",
      number: -2146823279,
      stack: "ReferenceError: 'Promise' is undefined
   at requireEnsure (http://192.168.1.17:8081/runtime~app.087ff77d9b7fd2b9c076.bundle.js:110:14)
   at RENDERRERS.ux (eval code:37:23)
   at DialogConfiguration.prototype._apply (eval code:62:9)
   at Anonymous function (eval code:56:35)
   at configure (eval code:268:5)
   at Anonymous function (eval code:626:9)
   at Anonymous function (eval code:59:7)"
   }
1 Like

I think you beed Bluebird and probably some polyfills as well

2 Likes

as the error states - you need a Promise polyfill. (bluebird is a good option for that).

you may also need other polyfills based on your app uses.
follow the instruction explained here if you need to support even older browsers.

1 Like

The polyfill for promise is there and is working the CLI installs and sets one up, the promise-polyfill. I think the problem is with error handling in aurelia-dialog therefore instead of getting the actual error related to aurelia-dialog a generic Promise error is shown. IDK grasping for starws.

I already had the other polyfills @ghiscoding suggested, but as stated those are for IE10 and IE9. Don’t know if they make a difference with IE11.

I found a solution, but it would be nice to know why this does not work. Is this a reproducible error. Kind of sad that one can crash and burn only after 4 or 5 steps without any documentation. Even though it is not stated anywhere I wonder if Aurelia Dialog 2.0 only works with evergreen browsers. Or what is Aurelia Dialog 2.0 dependent on that a polyfill is needed to get it to work with IE11. This might not be a problem with the code but with the documentation not stating that Dialog 2.0 will only work with evergreen browsers or if you want IE11 support then you need to do XYZ. Again IDK grasping for starws.

My solution was to roll back to Aurelia Dialog 1.1.0.

For the moment that was the only major thing holding me back, had some issues with element-closest and switched from isomorphic-fetch to whatwg-fetch. Other than that everything else works.

1 Like

The webpack project already got a promise polyfill setup in your webpack.config.js.

new ProvidePlugin({
      'Promise': ['promise-polyfill', 'default']
    }),

It could be some special aurelia-dialog code, we will sort that out and update here.

1 Like

@y2k4life I got the culprit. Update: sorry, I found out my conclusion was incorrect, after testing on a real IE11 box. The only solution worked right now is <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script> in html head.

The ProvidePlugin for webpack injects an import statement in those js files that uses Promise as global var.
Note it’s an import statement to bring in Promise, it means the Promise is still not global var, but a local var visible to the modified js file.

It turns out webpack did not apply ProviderPlugin for every js files. When webpack bundles aurelia-dialog,
It uses UMD build because by default it honours package.json browser, module, and main fields (in the exact order).

The fields in aurelia-dialog package.json.

  "main": "dist/commonjs/aurelia-dialog.js",
  "module": "dist/native-modules/aurelia-dialog.js",
  "browser": "dist/umd/aurelia-dialog.js",

Webpack didn’t touch the umd js file, I guess it’s because it needs more work to inject dep to umd. UMD doesn’t understand import statement, to support injecting dep to umd, the ProviderPlugin would need to inject dep to AMD, CommonJS module loaders, and not sure what they can do with browser globals when amd and commonjs are absent.

The fix is to manually force webpack to load esm build for aurelia-dialog.

You can either use https://webpack.js.org/configuration/resolve/#resolvemainfields to force mainFields: ['module', 'main'] (removed “browser” from the default list) but that affects all other npm packages.

Or you can use use resolve.alias to hack dialog only.

  resolve: {
    alias: {
      'aurelia-binding': path.resolve(__dirname, 'node_modules/aurelia-binding'),
      'aurelia-dialog': path.resolve(__dirname, 'node_modules/aurelia-dialog/dist/native-modules/aurelia-dialog.js')
    }
  },

cc @StrahilKazlachev

1 Like

There is another option. Remove the ProviderPlugin in webpack config, load the promise polyfill manually in index.ejs head.

<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>

Note it would NOT work if you try to add import 'promise-polyfill/src/polyfill'; to src/main.ts because main.ts is loaded after aurelia-bootstrap started. aurelia-bootstrap (and deps) uses promise too, the polyfill needs to be ready before bootstrap. This is the reason why our default setup uses ProviderPlugin instead of simple import 'promise-polyfill/src/polyfill'; in main.ts.

FYI: as a side note, the upcoming Aurelia 2 simplified app startup by starting your src/main.ts as the entry module, there is no more aurelia-bootstrap to wrap your code, so that you could simply use import 'promise-polyfill/src/polyfill'; in src/main.ts in an Aurelia 2 project.

Update: Another working alternative, remove ProviderPlugin, then load promise polyfill before aurelia-bootstrap in webpack config.

entry: {
    app: ['promise-polyfill/src/polyfill', 'aurelia-bootstrapper']
  },

I still have no idea what code in aurelia-dialog created all this trouble.

Also the promise polyfill seems confused webpack WDS in IE11. I am seeing many error pops in console.

1 Like

@huochunpeng thank you for the feedback this will help with the troubleshooting. I find it odd that other plugins and components in particular aurelia-validation and aurelia-fetch don’t have issues with promise-polyfill. Not to mention the Aurelia framework. I will try some of your suggestions, in particular the last suggestion of moving promise-polyfill from ProviderPlugin to entry and making sure it is before aurelia-bootstrapper.

I’m just a bit hesitant to mess with WebPack and configurations for just one piece of my code not working. I rather go back to aurilia-dialog 1.1.0.

I just wonder what is in the aurelia-dialog 2.0 that makes this not work.

Thank you.

1 Like

Dialog v2 does not require anything special to work. As long as theres a Promise, it should work. Though i think you may want to apply conditional polyfilling instead

Based my experience, aurelia-dialog did affect ie11 in some strange way, although I had no idea what’s going on.

Even though I solved the problem by going back to 1.1.0 I wanted to figure out what the issue is.

It seems as if Webpack Runtime gets to a point where the promise-polyfill seem to be no longer present. The irony though is that when the error is thrown it is caught by code in the promise-polyfill. At first I thought this was due to loading async chunks in WebPack. Once I got WebPack to bundle all of aurlie-dialog into a single chunk there was still a Promise undefined error just in a different place.

I have tried everything to get the promise-polyfill loaded before the run time. Move it to entry, create vendor, put import 'promise-polyfill' everywhere. Why at times the run time can find Promise but other times it can’t I don’t know. But still puzzled as to why I would need to do get promise-polyfill if the stack trace returns back to promise-polyfill. If it returns back to there then it had to be loaded before the run time in order to start from there. You then think Promise would be defined. IDK grasping for straws.

A solution is to put the following line in the index.ejs but that seems to be against the grain.

<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8.1.3/lib/index.min.js"></script>

In aurelia-dialog this is where it starts

dialog/src/ dialog-configuration.ts

  private _apply(): Promise<void> {
    const renderer = this.renderer;
    const cssText = this.cssText;
    return Promise
      .all([
---->        typeof renderer === 'string' ? RENDERRERS[renderer]() : renderer,
        cssText
          ? typeof cssText === 'string'
            ? cssText
            : cssText()
          : ''

.
.
.

Which then calls ux: () => from the RENDERRERS.

const RENDERRERS: Record<string, () => Promise<RendererStatic>> = {
  ux: () => import('./renderers/ux-dialog-renderer').then(m => m.DialogRenderer) as Promise<RendererStatic>,
.
.

The above is transpiled to this

ux: function () { return __webpack_require__.e(/*! import() */ "vendors.async~3e799143").then(__webpack_require__.bind(null, /*! ./ux-dialog-renderer.js */ "yfWE")).then(function (m) { return m.DialogRenderer; }); },

Which then calls the WebPack run time to resolve vendors.async~3e799143

__webpack_require__.e = function requireEnsure(chunkId) {
/******/ 		var promises = [];
/******/
/******/
/******/ 		// JSONP chunk loading for javascript
/******/
/******/ 		var installedChunkData = installedChunks[chunkId];
/******/ 		if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ 			// a Promise means "currently loading".
/******/ 			if(installedChunkData) {
/******/ 				promises.push(installedChunkData[2]);
/******/ 			} else {
/******/ 				// setup Promise in chunk cache
/******/ 				var promise = new Promise(function(resolve, reject) {
/******/ 					installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ 				});
/******/ 				promises.push(installedChunkData[2] = promise);

It dies at // setup Promise in chunk cache when creating a new Promise
var promise = new Promise(function(resolve, reject) ....

But then you continue and you end up in the promise-polyfill try … catch

   try {
      ret = cb(self._value);
    } catch (e) {
      reject(deferred.promise, e);
      return

Now that I know more of what might be the issue now it is trying to figure out the ideal solution without having to put a script tag in my index file.

I will close with one of my favorite movie quotes from Tommy Boy.

Ray Zalinsky:
Goin’ a little heavy on the pine tree perfume there kid?

Tommy:
No, it’s an auto air freshener.

Ray Zalinsky:
Good, you’ve pinpointed it, now the next step is washin’ it out.

1 Like

Thank you @y2k4life, you remind me something I forgot. I have not touched our webpack setup for a while.

Our aurelia-webpack-plugin changed webpack behaviour on loading aurelia-* packages, it enforces to load dist/native-modules/* instead of those fields (browser, module, main) in package.json.

That’s why the aurelia-dialog is bundled using native-modules dist files, NOT UMD dist files.

The native-modules dist files transpiled the dynamic import() to __webpack_require__ which obviously only works with better browser.

However, the UMD files transpiled the import() statements into native promise, (not __webpack_require__) which works in IE11.

// UMD transpiled code
  var RENDERRERS = {
      ux: function () { return Promise.resolve().then(function () { return uxDialogRenderer; }).then(function (m) { return m.DialogRenderer; }); },
      native: function () { return Promise.resolve().then(function () { return nativeDialogRenderer; }).then(function (m) { return m.NativeDialogRenderer; }); }
  };

This means if you enforce using UMD dist files, everything goes back to normal.

alias: {
      'aurelia-binding': path.resolve(__dirname, 'node_modules/aurelia-binding'),
      'aurelia-dialog': path.resolve(__dirname, 'node_modules/aurelia-dialog/dist/umd/aurelia-dialog.js')
    },

Hope this helped.

However, I am also going to cleanup our webpack setup to move promise polyfill from ProviderPlugin to entry config, as suggested by @jods4.

1 Like

@huochunpeng closer but still no go, we are on the right track.

I tried the moving promise-polyfill from ProviderPlugin to entry and that made things worse. What specifically is the clean up on that. I could be doing something wrong.

The idea is working but the syntax is not or something, for some reason WebPack is not honoring the Resolve alias. I figured what you are trying to do is not use native-modules folder. I tested by removing the folder. I renamed to native-modulesx and it worked! But that is not the solution. The alias though is not working although it should.

1 Like

That’s bit strange, what worked for me on a windows box is

  1. leave ProviderPlugin unchanged.
  2. use alias
    'aurelia-dialog': path.resolve(__dirname, 'node_modules/aurelia-dialog/dist/umd/aurelia-dialog.js')
1 Like

I figure if it works for you it should work for me. I tried again, but there was one thing I did not do that I should have done.

Start over.

I made so many changes that I believe my setup become corrupted. I also questioned if it was Linux.

I tried both Linux and Windows.

It works!!!

@huochunpeng thank you. This is noted on GitHub Issue #387. I chalk this up as a documentation error.

1 Like

@huochunpeng thanks again.

Here is the fruits of our labor, updates to the app using Dialog 2.0 and IE11 (and much more).

aurelia-dialog

3 Likes

Im glad you got it working, and congratz on the upgrade. It could have been a nice and seemless experience without this bump. I still think its better to use the polyfill to normalize APIs such as Promise. One service you can use is polyfill.io

@bigopon thank you. Sold framework when you can upgrade an old app to the most recent version of the framework. I expected more than just this one little hiccup.

Just to clarify, this was one of those errors where what you are told is the issue is not the issue. The promise polyfill was not the issue even though that was the error reported. I do have a promise policyfill and it is working just fine, the one that comes with the CLI. From what I gathered from @huochunpeng the root cause is Dialog 2.0 uses native dynamic import() which does not work with IE11. Had to force Webpack to bundle the UMD.

v2 switched to native dynamic import() statement to simplify the code. This is indeed a breaking change for IE11.

*huochunpeng

2 Likes

For polyfilling, I meant I was suggesting the use of some service like polyfill.io, to make it globally available, independent of your build, and thus will be usable without any hiccup, in any part of your application (the bundle, or 3rd party script). Something like this in your index.html:

<head>
  <script src="https://polyfill.io/...promise">
  ...
</head>
<body>
  ... bundle here
</body>