Sp-dev-docs: SPFX Polyfill Out of Stack Space exception

Created on 28 Sep 2017  Â·  33Comments  Â·  Source: SharePoint/sp-dev-docs

Category

  • [ ] Question
  • [ ] Typo
  • [x] Bug
  • [ ] Additional article idea

Expected or Desired Behavior

Importing ES6-Shim or Core.js/ES6 Shims for ES6 library support in SPFX.

Any suggestions as to how to integrate the necessary Polyfills into SPFX in general would be greatly appreciated. I have tried manually adding Polyfills for each issue but the existing libraries are thousands of lines long and have required years to develop. Recreating this functionality from scratch would be an exhaustive effort.

As a large share of modern libraries rely on ES6 and its polyfills. This issue greatly limits developer options. Angular 2+ and many other solutions require this functionality.

Observed Behavior

Polyfill libraries (ES6-Shim and Core.JS/ES6) work in other TypeScript based deployment scenarios, but throw 'Out of Stack Space' errors in SPFX.

https://github.com/paulmillr/es6-shim
https://github.com/zloirock/core-js

So far in my testing I've got the error down to these 2 polyfills. All of the other Angular requirements will load without issue.
import 'core-js/es6/map';
import 'core-js/es7/reflect';

map is the only Angular 2 requirement that's causing an issue in SPFX and the SP Loader has it's own map implementation for IE.

I have tracked the issue most of the way down into the sp-loader-assembly_en-us.js file on line 703 where it seems to rerun this loop until it crashes:

var queue = new Array(1000);
function flush() {
  for (var i = 0; i < len; i += 2) {
    var callback = queue[i];
    var arg = queue[i + 1];

    callback(arg);

    queue[i] = undefined;
    queue[i + 1] = undefined;
  }
  len = 0;
}

It also pops in and out of the eval in block 37 while it goes through this loop:

/* 37 */
/***/ (function(module, exports) {

(function (global) {
  eval('/*\r\n * SystemJS v0.19.25......................................');
}.call(exports, (function() { return this; }())))

I have tried to step through the code until it finally dies but so far I haven't been able to track it that far. My thoughts at the moment are that this is a collision of type 'Map' which is defined globally in the SP-Loader and in the Core-JS polyfills.

sp-loader-assembly_en-us.js
if (typeof Map == 'undefined' || typeof((new Map).values) !== 'function' || !(new Map).values().next) { exports.Map = createCollection({ // WeakMap#delete(key:void*):boolean 'delete': sharedDelete, //:was Map#get(key:void*[, d3fault:void*]):void* // Map#has(key:void*):boolean has: mapHas, // Map#get(key:void*):boolean get: sharedGet, // Map#set(key:void*, value:void*):void set: sharedSet, // Map#keys(void):Iterator keys: sharedKeys, // Map#values(void):Iterator values: sharedValues, // Map#entries(void):Iterator entries: mapEntries, // Map#forEach(callback:Function, context:void*):void ==> callback.call(context, key, value, mapObject) === not in specs forEach: sharedForEach, // Map#clear(): clear: sharedClear }); }

map.js
require('../modules/es6.object.to-string'); require('../modules/es6.string.iterator'); require('../modules/web.dom.iterable'); require('../modules/es6.map'); module.exports = require('../modules/_core').Map;

If you console.log Map before you import the core-js map implementation in your webpart file, it will spit out the createCollection function. Once you've imported core-js/es6/map your next console.log will never finish because of an out of stack space exception.

This issue has been noted here as well, though the relation to polyfills only was not noticed:

https://github.com/angular/zone.js/issues/316
https://github.com/webtechy/sp-dev-fx-webparts/issues/1

Steps to Reproduce

Yo @Microsoft/SharePoint a new solution and add either ES6 Polyfill to your code, run a gulp serve and try to use it in Internet Explorer.

spfx-general question

Most helpful comment

ES6 polyfills isn't something you should reinvent - and SPFx has chosen to reinvent them for whatever reasons. This workaround will not work in IE11. It will however work fine in Chrome/Edge/FFox etc. Anyway, the only real solution here is to either
a - Drop support for IE11
b - Not use Angular or Angular elements
c - SPFx to adopt well accepted community standards for polyfills (i.e. get off the sp-polyfills bandwagon and get on the es6-polyfills bandwagon like the rest of the world).

I feel the best solution is c .. but I'm not holding my breath.

All 33 comments

Hi @ArcanDotNet did you find any way to fix this? We're facing the same issue, in IE11 indeed.
Thanks

So I came up with a workaround-ish. I am by no means advocating this as a proper solution, but it does resolve the problem.

SP-Loader defines a global object named 'Map' which is an ES6 standard they are essentially polyfilling. In SPFX v1.3 this happens in sp-loader-assembly_en-us.js.

After an exhaustive amount of debugging and trying various things I tried logging the value of map, and eventually undefining it in my web part code just before I load the proper core-js polyfill for the same ES6 standard. By this point in the SP-Loader lifecycle, it seems replacing the existing Map function with the core-js implementation does not break SPFX and the web part will load.


import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/weak-map';

// Check for native support of Map vs Polyfill
if(Map.toString().indexOf('function Map()') === -1)
{
     Map = undefined;
}
import 'core-js/es6/map';
import 'core-js/es6/set';
import 'core-js/es6/reflect';

A proper resolution to this issue would be for SPFX to import core-js polyfills for the sp-loader or something of the like. The current Map implementation that's defined in the loader and handed down to the rest of the web part does not meet the specification.

@ArcanDotNet indeed, the Map object inside es6-shim seemed to be the cause with us. Thanks for the pointers!

The default (SP-Loader) implementation of the Map object breaks when you use it with other core-js polyfills (such as Array.from), hence the need to use Map from core-js as well.

This work around definitely saved me a lot of time, thank you for investigating.

Did someone find a solution?

I tried what @ArcanDotNet indicated but it did not work. I suppose that the incompatibility in my case comes from another object different from the "Map"

Thank you very much in advance,
Javier

Polyfills are global and affect all components on the page. Although I understand the motivation here, a third-party component really should not be loading polyfills. Even if it doesn't break today (as seen above), it may break with some future SPFx update or in some other context that wasn't tested (e.g. classic pages or a mobile app).

The sp-polyfills package was designed to be very conservative for performance reasons, since it is guaranteed to be loaded on every page where SPFx runs. In some cases the footprint may be very important, e.g. because the page is shared with a legacy application's scripts. We even discourage first-party developers from loading custom polyfills because it complicates the test matrix and third-party environment for each page.

If there is a community need for heavyweight polyfills, the right solution would be to create a separate package (e.g. sp-expensive-polyfills) that a web part can depend on. Then it would be loaded only if such a web part is present on the page. This would be a feature request for SPFx.

That said, if you can find some workaround that seems to work, you're free to use that approach as long as you're okay with the risks involved.

@patmill fyi

@pgonzal
A polyfill is a piece of JavaScript code that is intended to exactly simulate a native, interoperable feature of the web platform, for the purpose of allowing website developers to use modern web features in older browsers. - https://www.w3.org/2001/tag/doc/polyfills/

You either implement the feature to the spec, or you do not. The sp-polyfills MAP object does not meet the ES6 standard which is why it does not support 3rd party components. If it did, these components would simply work without the need for loading additional polyfills.

If performance is an issue, I would highly suggest removing the eval function I pointed out above. This the single most expensive task JavaScript can perform.

This GitHub issue seemed to start from the position of "If I do something which is not supported for SPFx, I get an error." I assume you already understand why SPFx cannot reasonably be expected to support custom polyfills; if not I can give more details.

If you believe the sp-polyfills Map class has a bug or is failing to implement the ES6 standard, please open a separate issue with repro steps. The error cited above seems to simply be two different polyfills conflicting with each other in their approach for modifying the global object space. So we'd want to see repro steps that don't involve trying to load core-js/es6/map.

Alternatively, if you'd like to propose specific improvements to sp-polyfills to make it more compatible with other libraries, that's good feedback. It was a long time ago when we decided which libraries to include in sp-polyfills, and probably the landscape has changed since them. The primary design goal -- of keeping the bundle size as small as possible -- still stands, though. For example the default core-js bundle is 83kb minified unzipped, which is pretty huge. And es6-shim.min.js is 56kb. The entire sp-polyfills bundle today is only 32kb minified unzipped.

var queue = new Array(1000);
function flush() {
  for (var i = 0; i < len; i += 2) {
    var callback = queue[i];
    var arg = queue[i + 1];

    callback(arg);

    queue[i] = undefined;
    queue[i + 1] = undefined;
  }
  len = 0;
}

Is this SPFx code? If so could you provide a little more context? I tried searching for "queue" and "flush" in our code base but it turned out a huge number of results. :-)

Solution for me was removing import 'core-js/es6/map'; at all

Does Angular require all of core-js? Or is there a core subset of it that is sufficient for most scenarios? We could measure the size of that.

@pgonzal No, Angular does not require all of core-js, the following are the most common:

import 'core-js/es6/symbol';
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es7/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
import 'core-js/es6/map';
import 'core-js/es6/set';
import 'core-js/es7/array';


/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install --save classlist.js`.
/** Evergreen browsers require these. **/
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';

/** IE requires the following pollyfills to support promises and Angular's httpClient */
import 'es6-promise';
import 'whatwg-fetch';

What I found is that es6/map, es6/reflect, es7/reflect make the sharepoint app crash in Internet Explorer, when I remove them the app loads but then I get a problem with an ngIf in ng Bootstrap alerts when Angular is initating, this happens only in Internet Explorer, so I think I lack one more pollyfill to make it work.

Are there individual TypeScript typings for these modules? It might be odd to polyfill all these APIs but have no way for people to use them from TypeScript. (We also wouldn't want turn on all the ES6 typings since that would enable IntelliSense for APIs that are NOT polyfilled, which would be confusing for developers.)

Not that I know of.
On monday will try compiling the code on es5 target and not on 6, to see if I can bypass the situation, if i succeed I will post the polyfills I needed to make it work.

Edit:
It didn't work, it seems like es6/reflect and es7/reflect are giving trouble too when I tried to run the app.

To clarify, which version of Angular are you targeting? IIRC SPFx doesn't recommend Angular 2 through 5 (?) and recommends Angular Elements.

Angular adds the list of polyfills above to any new project you create so I believe they are all needed. The real issue here is that sp-polyfills is adding Map to the global namespace without fulfilling all the requirements of the Map spec in ES6.

If nothing else I would replace that one polyfill with the CoreJS one, or just remove it altogether. For long term support I would suggest adding a configuration option to SPFX that allows users to add the entire CoreJS library or each section of it as needed. CoreJS is pretty much the web standard for Polyfills at this point and any attempt to reinvent this functionality is unwise.

While I have not used Angular Elements, it will likely require ES6 polyfills as well in IE. Almost all modern frameworks rely on parts of ES6. It's a 3 year old standard. But for the moment Custom Elements, the standard on which Angular Elements is based is unsupported in all MS browsers. (IE and Edge)

By recommending Elements the SPFX docs directly contradict SharePoint in terms of support. SharePoint is recommended to be run on IE11 which has no ES6 support and will never support the Custom Elements standard. So until Edge does support this new standard and Microsoft updates the SharePoint docs to recommend Edge as the preferred browser, the dev community has no options for using angular in SPFX if they want to full platform compatibility.

This whole issue would be much simpler if IE was end of life, or if SPFX officially supported Angular 2-X. For now devs should avoid any version of Angular in SPFX if they want their product to be considered inline with SharePoint standards. (A truth that is disheartening)

The real issue here is that sp-polyfills is adding Map to the global namespace without fulfilling all the requirements of the Map spec in ES6.

What ES6 requirements does it not fulfill? Can you give an example of a specific API that doesn't work as documented? If the Map polyfill is lacking somehow that should be easy to address.

Exactly what it's missing I couldn't say. But if you add all of the CoreJS polyfills but Map and load Angular you will get an exception. If you null out the SP-Polyfill version of Map and load any of the other mainline polyfills this does not happen. This also never happens in modern browsers like Chrome and Edge where Map is natively supported. This clearly means to me that the current implementation has an issue or does not meet the spec. If it did, it would not throw exceptions when a common library like Angular tried to use it.

Debugging the exact issue with the SPFX implementation of Map is a lot to ask and I don't understand why the wheel is trying to be reinvented here. Polyfills are very difficult to write and a new implementation of widely adopted code is not something that makes a lot of sense.

If you would like to try and find the issue, reproducing it is very easy to achieve and I have documented the steps above.

After trying a new dummy project with Angular 6 Elements I can confirm that the problem are the SP-Pollyfills, event in a self contained element the SP Map implementation breaks the app with the out of stack space exception.
I think the easiest solution would be for sharepoint to import all the ES6 polyfills, that would mean bigger packages, but would solve a lot of headaches not only for Angular Devs but also for a lot of other developers who may find this issue when polyfilling their apps.

EDIT: The reason the workaround wasn't working for me was that our Sharepoint Site was on IE 10 compatibility mode of IE 11, which for a reason I ignore breaks the app even more. The workaround works for now on IE 11, but it would be great if we got some kind of fix for this.

Thanks @ArcanDotNet, you saved me a lot of debugging! I got our solution working by removing both 'core-js/es6/map' and 'core-js/es6/set' polyfills. So it seems polyfill for Set might cause this issue too.

I've been debugging this from Core JS and found the part from thier side that causes the error. Core JS seems to 'fix' existing map polyfills if they don't match the spec. In this case it is testing support for iterables in 'core-js/modules/_collections.js'. By commenting the lines below from _collections.js it starts working fine with IE.

// most early implementations doesn't supports iterables, most modern - not close it correctly
var ACCEPT_ITERABLES = $iterDetect(function (iter) { new C(iter); }); // eslint-disable-line no-new

...

if (!ACCEPT_ITERABLES) {
    C = wrapper(function (target, iterable) {
    anInstance(target, C, NAME);
    var that = inheritIfRequired(new Base(), target, C);
    if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that);
    return that;
    });
    C.prototype = proto;
    proto.constructor = C;
}

At this point I'd assume this means that SP polyfill collections do not support iterables, meaning that it doesn't match Map spec. At this point I don't know what "support for iterables" means, so I still need to go through the spec, core-js code and compare it to SP polyfills to make sure of this.

Polyfills don't just implement functionality, they also replace it if it's wrong. With so many browsers in the world, each defining subsets of ES6 standards the point of polyfills is to evaluate the JS engine and adjust it as needed to support modern JS.

@SamiEklund You certainly are determined my friend.

@ArcanDotNet My motivation dropped a bit as we did a workaround that worked for us in this scenario. I still might investigate what is missing from the SP polyfill, bit its not a priority atm.

We simply check if window.Map or window.Set exist, and if not we require the corejs polyfills. So far we've had no issues with it.

if (!window.Map) {
    require('core-js/es6/map')
}

if (!window.Set) {
    require('core-js/es6/set')
}

Well last week I got notified that the app stopped working in Internet Explorer even with the workaround @ArcanDotNet found, so I started experimenting and it seems that sharepoint added new pollyfills that break even more the app than before, by trial and error I found a new workaround:

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
import 'core-js/es6/symbol';
import 'core-js/es6/object';
import 'core-js/es6/function';
import 'core-js/es6/parse-int';
import 'core-js/es6/parse-float';
import 'core-js/es6/number';
import 'core-js/es6/math';
import 'core-js/es6/string';
import 'core-js/es6/date';
import 'core-js/es6/array';
import 'core-js/es6/regexp';
if(WeakMap.toString().indexOf('function WeakMap()') === -1)
{
     WeakMap = undefined;
}
import 'core-js/es6/weak-map';

// Check for native support of Map vs Polyfill
if(Map.toString().indexOf('function Map()') === -1)
{
     Map = undefined;
}
import 'core-js/es6/map';
// Check for native support of Map vs Polyfill
if(Set.toString().indexOf('function Set()') === -1)
{
     Set = undefined;
}
import 'core-js/es6/set';

This is how my polyfills look now, I removed the reflect polyfills and the app is working perfectly fine, I guess it has been added in an update. It's far from Ideal, but at least it works for now.

Which file in spfx webpart package do I include this workaround in?

You have to add it where ever you are importing the core-js polyfills. If you are not using the polyfills, but still think you are having this issue you should check if one of your dependencies or their dependencies are using the polyfills.

Looks like this was addressed with @JuanCangelosi's workaround... thanks for the follow-up! Going to close this but if you feel I did this pre-maturely, please feel free to reopen with more detail.

This was my issue, and I found the workaround. It seems extremely hackish to me, but I'm not even in the SharePoint realm anymore so I guess people can do what they want.

I feel the team should address it properly and not just embrace an absolute hack that will actually blank out the proper implementation of ES6 code even in modern browsers.


From: Andrew Connell notifications@github.com
Sent: Tuesday, January 22, 2019 7:22 AM
To: SharePoint/sp-dev-docs
Cc: Warren Hall; Mention
Subject: Re: [SharePoint/sp-dev-docs] SPFX Polyfill Out of Stack Space exception (#888)

Closed #888https://nam05.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FSharePoint%2Fsp-dev-docs%2Fissues%2F888&data=02%7C01%7C%7Ce5228addc2464fec5dab08d6807d5da9%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636837673257365836&sdata=QleuKoiI0TB6o%2BXF9liGeSoLm4nKe%2B7xKdXaFmz3rQ4%3D&reserved=0.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHubhttps://nam05.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2FSharePoint%2Fsp-dev-docs%2Fissues%2F888%23event-2089083941&data=02%7C01%7C%7Ce5228addc2464fec5dab08d6807d5da9%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636837673257375847&sdata=bzgipbKRKeyWmGthbiD1hfp93ybRMzVDcu7kTjAS5dM%3D&reserved=0, or mute the threadhttps://nam05.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fnotifications%2Funsubscribe-auth%2FAe332TLAbXBYIk1NR6CHxb6hLIl8CYMXks5vFyybgaJpZM4Pnyhs&data=02%7C01%7C%7Ce5228addc2464fec5dab08d6807d5da9%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C636837673257385858&sdata=kuaStB%2FO7HdKirgZiJo8JkfI8awdlnrGKhL33yGKrqY%3D&reserved=0.

I think this issue should be reopened. This is solution is so hacky, it disturbs me.

ES6 polyfills isn't something you should reinvent - and SPFx has chosen to reinvent them for whatever reasons. This workaround will not work in IE11. It will however work fine in Chrome/Edge/FFox etc. Anyway, the only real solution here is to either
a - Drop support for IE11
b - Not use Angular or Angular elements
c - SPFx to adopt well accepted community standards for polyfills (i.e. get off the sp-polyfills bandwagon and get on the es6-polyfills bandwagon like the rest of the world).

I feel the best solution is c .. but I'm not holding my breath.

I don't see get this particular issue in SPFx 1.9.1. But recently I did receive
"TypeError: Object doesn't support property or method 'from'" exception in IE browser when I was using the super awesome 'Yup' validation module in my code. In order to fix this I had to include the following import.
import "core-js/es6/array";

So far this is the only polyfill I had to manually include. Hope Microsoft includes the above polyfill in newer versions of SPFx.

I will update here if in future I am forced to import additional polyfills manually.

Issues that have been closed & had no follow-up activity for at least 7 days are automatically locked. Please refer to our wiki for more details, including how to remediate this action if you feel this was done prematurely or in error: Issue List: Our approach to locked issues

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StfBauer picture StfBauer  Â·  3Comments

waldekmastykarz picture waldekmastykarz  Â·  3Comments

karishmaTCS picture karishmaTCS  Â·  3Comments

SteIvanov picture SteIvanov  Â·  3Comments

jonthenerd picture jonthenerd  Â·  3Comments