Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why does this package pull in core-js? #524

Open
ersinakinci opened this issue May 12, 2020 · 9 comments
Open

Why does this package pull in core-js? #524

ersinakinci opened this issue May 12, 2020 · 9 comments

Comments

@ersinakinci
Copy link

Just curious why the distributed code has references to core-js. Wouldn't it be better to let end users polyfill as needed?

Specifically, found-relay is causing core-js-pure to get pulled in, which ends up duplicating a lot of the core-js functions that normally get polyfilled (and pollute global namespace).

@taion
Copy link
Member

taion commented May 12, 2020

The duplication is a bit unfortunate, yeah.

We pull in core-js because we use the runtime transform, and we use the runtime transform because this pulls in a (pure) polyfill for async iterators, which we use for the resolver.

I'm not aware of a better way to package this such that it works out-of-the-box for apps targeting older browsers, without forcing those apps to pull in their own polyfills.

@taion
Copy link
Member

taion commented May 12, 2020

But if you can think of a better way to package this, I'd be happy to not pull in these runtime polyfills.

@ersinakinci
Copy link
Author

I agree that for making it work out-of-the-box, there doesn't seem to be another way. Is supporting that behavior that a strict requirement for the project?

My personal belief is that downstream apps should be responsible for polyfills and that libraries should alert devs to the potential need for polyfills in the docs. That's also how Relay and React do it. I don't think that it's ideal to do it that way, but IMHO that's the least surprising behavior. When making a web app, you always have to think about browser support and polyfills are generally part of the planning process.

I don't think of polyfills as normal dependencies. The fact that they're needed at all is kinda a hack that we're forced to live with because of the shortcomings of the web as a platform. If we were building Linux libraries and desktop apps, for example, we'd expect the package manager and the package maintainers to ensure that everything uses the one blessed version of core-js. In Windows, static bundling is much more common, but then again package size is not critical on desktops the way that it is on the web.

Anyway, my point is that given that polyfills are a kludge to begin with, I think it's right for the app devs to make the call over which ones are included. Even if one doesn't see them as a hack, it's arguably just as important to enable a dev to make their own choices about which browsers not to support as it is to make it easy for them to support older browsers. IMHO, alerting them to the potential need for polyfills in the docs without forcing them to use one strikes the right balance, and it's an expectation that's consistent with what they'd already be familiar with if they're supporting Relay on older browsers (see above).

My $0.02.

@taion
Copy link
Member

taion commented May 12, 2020

I think the distinction here is that async iterators are a bit more exotic than the kinds of polyfills that React and Relay tend to use. Saying "you need to have a Promise" polyfill is a little easier to communicate and understand than saying "you need to have a polyfill for Symbol.asyncIterator". If I get some time, I'll take a look at what we're actually pulling in, though. I haven't had the chance to do a comprehensive inventory, and if we are pulling in "pure" polyfills for Promise, &c., then we should probably not do that.

@ersinakinci
Copy link
Author

You're right, the async iterator polyfill is more obscure and would probably cause a few web searches.

But would it be any different in terms of documentation? You'd simply add import 'core-js/es/symbol/async-iterator' (or import 'core-js/es7/symbol' for core-js 2). Seems straightforward enough for me.

@ersinakinci
Copy link
Author

ersinakinci commented May 16, 2020

I'm sorry to bug you about this again, but are you sure there isn't any way to drop the core-js-pure dependency? It's kind of crazy that this one package is forcing a 14 KB-gzipped and minified--polyfill in a bundle. It's the fourth largest dependency in my app, and that's not counting found-relay itself.

Screen Shot 2020-05-15 at 9 06 01 PM

I understand the desire to provide a better OOBE, but I firmly believe that libraries shouldn't force polyfills on end users.

@jquense
Copy link
Contributor

jquense commented May 16, 2020

We'd be happy for this to be more minimal! Historically this has been hard tho, polyfilling an built-in symbol can be tricky b/c of how Symbol is it's own primitive. Code using fake well known symbols may need to know implementation details about how the symbol was polyfilled in order to work. This has caused an annoying light coupling between babels generated async iterator code and the global symbol that frequently caused breakages when babel would change almost unrelated plugins. That is more of an issue if you have polyfill Symbol itself, along with Symbol.asyncIterator though. That is a lot to say, that historically for lots of inexplicable reasons, having users provide their own asyncIterator polyfill hasn't actually worked, even when it seems like it should be a single core-js import

Babel is also annoying in that you can't pick and choose (that I know of) which things it should expect native implementations for and which things to pull a polyfill in. CoreJS 3 is also super aggressive about which things it pulls in, even when you cherry-pick only the things you want. So you get annoying things like polyfill fills for Object.keys https://unpkg.com/browse/[email protected]/es/ReadyStateRenderer.js

We should fix this tho, and there is already some work underway by @manon-pilaud to update the babel targets to something more modern and minimal. It might also be worth trying to use a specifically scoped async iterator transform if such a thing exists

@ersinakinci
Copy link
Author

ersinakinci commented May 16, 2020

@jquense aha, I didn't realize that there's a deep history behind this particular polyfill. That makes sense. I had assumed that it would be trivial to simply do a import 'core-js/es/symbol/async-iterator'. Unfortunately, I don't know enough about polyfilling symbols and asyncIterator to speak authoritatively on the subject or to offer meaningful suggestions.

Babel is also annoying in that you can't pick and choose (that I know of) which things it should expect native implementations for and which things to pull a polyfill in.

I believe that I'm misunderstanding you, but Babel is agnostic as to what you polyfill and what you don't (assuming that you configure useBuiltIns appropriately). You can pick what gets polyfilled by choosing what modules you import from CoreJS and other polyfill libraries. For example, my current setup looks like:

import 'regenerator-runtime/runtime';
import 'core-js/es/map';
import 'core-js/es/object';
import 'core-js/es/promise';
import 'core-js/es/set';
import 'isomorphic-fetch';

Any feature not on that list is implicitly native--except for the features polyfilled by found-react! 🙂

@taion
Copy link
Member

taion commented May 29, 2020

We might be able to use babel/babel#3934 (comment) to omit some of the polyfills.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants