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

Polyfill Checker for Library Projects #8089

Open
KevinAst opened this issue May 31, 2018 · 15 comments
Open

Polyfill Checker for Library Projects #8089

KevinAst opened this issue May 31, 2018 · 15 comments

Comments

@KevinAst
Copy link

KevinAst commented May 31, 2018

Feature Request: Polyfill Checker for Library Projects

Feature Request ...

I would like to propose a new babel feature that would greatly simplify polyfill verification for library projects. This enhancement would emit a lightweight JavaScript executable that verifies the running JS engine supports the language constructs "used" in a specific library package.

Background ...

Library developers that use es2015+ constructs, face an issue as to whether they should polyfill their library or not (please refer to the W3C Polyfill Findings specifically Advice for library authors).

Personally, I am in the alternative camp that project libraries should NOT polyfill. Rather, that is the responsibility of the client app. My reasoning for this is:

  • As a library author you have absolutely NO idea what your target env is going to be. One can attempt to apply polyfills to a library (with "non polluting" due diligence), however that is from a perspective of potential problematic targets. If an antiquated target is run, there will still be issues.

  • In reality, we want babel to free us from this burden, and it is becoming better and better at doing this!

  • From an app perspective, applying the needed polyfills has become a fairly simple and straightforward process, with the advent of the "babel-polyfill" import, in conjunction with babel's "babel-preset-env" configured with useBuiltins:"usage".

This alternative tactic requires the library to:

  1. document needed polyfills, and
  2. abort early when run in an environment that is feature deficient.

Both of these tasks are tedious when done manually. It occurred to me that babel could assist in both of these tasks.

Feature Requirements ...

The requirements for this enhancement are:

  1. Provide a run-time hook that verifies the language constructs found in the library project are available in the executing JavaScript engine. If not, return a summation (nicely formatted for human consumption) which can be used either in warning logs or an error processor.

    In essence this requires some persistence as to what language constructs are "in use" for the library in question.

  2. Accomplish this in a very lightweight way (both in deployment size and execution speed) so as to have minimal impact on the library package.

Proposed Solution ...

Due to both the "run-time hook" and "persistence" requirements, one solution would be for babel to support an option to emit a machine-generated "checker" file (ultimately included in the library project) that would perform these checks at run-time.

The contents of this "checker" would presumably be similar to what logically is accomplished by "babel-polyfill", except:

  • instead of resolving issues by applying needed polyfills, it merely summarizes and reports violations back to the invoker

  • it would only check the constructs that are in-use by the project library code (similar to how the useBuiltins:"usage" option operates)

As a result, this solution is very light-weight, and can be included in the library project without penalty. In essence it doesn't have to include the needed polyfills - it merely performs the conditional checks.

The babel option could be something like genPolyfillChecker where two items are specified:

  • libName: identifying the project's library name - for human consumption (in the the formatted messages), and

  • outputFile: the emitted machine-generated "checker" file

This generated PolyfillChecker outputFile would be included at the start of the library project's expansion, and could also be used as the genesis of any library documentation highlighting the required environment.

Your consideration is greatly appreciated.

Thoughts?

@babel-bot
Copy link
Collaborator

Hey @KevinAst! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.

@jaydenseric
Copy link

jaydenseric commented Jun 1, 2018

I agree that polyfills should be done once by the consumer at the app level, and if you keep following the logic, all standards-based transpilation should really happen at the app level. I'm not sure I would want to bloat my packages with runtime checks though, considering how complex it is and the system should throw a native error anyway.

If you did have runtime polyfill checking, it should only cover features not natively supported under your package engines and browserslist config, and of those features, only ones that are used in your package.

A consumer might only import and use one function from the package that runs fine without polyfills, yet the polyfill checks would presumably scan the whole package and get upset about a polyfill missing for an unused API.

You could use something like invariant so the runtime checks only appear in a dev environment.

What would be awesome is a build-time report, that can be used to auto gen documentation. In my package readmes I try to manually maintain a recommended polyfills list in an environment support section, like this.

As a side note, I wonder if a new dev tool would help the problem of inexperienced developers not being able to recognise certain errors as meaning something should be polyfilled. It could regex the error (i.e. fetch being undefined) and make suggestions. Not sure where it would live though, maybe as a browser extension.

@KevinAst
Copy link
Author

KevinAst commented Jun 1, 2018

Thanks for your insight @jaydenseric.

In regard to bloat of the library package, my assumption was that the overhead of polyfill checking would be very lean (both in size and speed) because polyfill resolution is not addressed. Just because it is potentially complex (i.e. involved) does not necessarily correlate to bloat. With that said, I would defer to someone with more familiarity of the internals. My goal was to adhere to the documented alternative recommendation in the most convenient and complete way (i.e. babel is "in the know"). The nice thing about this feature request is the complexity is encapsulated within babel's knowledge base.

In regard to the native errors emitted from polyfill issues, these can very often be very obscure - even to seasoned developers. The goal would be to make this more fool-proof, identifying both the needed polyfill and the package requiring it. As an example of obscurity, the following error is emitted in an android/expo JS engine due to the usage of a "for of" loop ... Yikes!

TypeError: undefined is not a function 
           evaluating 'vals[typeof Symbol === 'function' ? Symbol.iterator : '@@iterator']()'

In regard to a consumer not needing all the polyfills due to partial usage of the package, I see your point, however it really depends on the library - some would be more susceptible to partial usage than others. Taking this mindset a bit further, you would need to document required polyfills on a function-by-function basis, which seems a bit of an overkill to me.

I agree that a documentation build-time report would be a very nice feature. I like what you are doing in this regard - within your documentation. In essence, both a run-time JS checker and built-time report are very close in nature - they are identifying polyfills required for a given library package.

@jeffbski
Copy link

jeffbski commented Jun 1, 2018

@KevinAst This sounds like a nice idea. We don't want all of our libraries to be providing their own polyfills when they may or may not be needed, so having a machine parsable output that identifies the ES feature dependencies would be ideal. Then when one is doing an app build, the ES feature dependencies could be included and the appropriate polyfills could be included for the target engine or output as documentation, etc.

@KevinAst
Copy link
Author

KevinAst commented Jun 3, 2018

The following individuals contributed to the Library Polyfill Discussion which drove the W3C Polyfill Advice for Library Authors and therefore may have an interest in this feature request.

Your insight is greatly appreciated.

What say you: @nolanlawson @triblondon @hzoo @Rich-Harris @Kovensky @palmerj3 @bfred-it @TheLarkInn @reconbot @Draggha @tomccabe @georgezzhang @sokra @dmethvin @jacobangel

@reconbot
Copy link

reconbot commented Jun 3, 2018

I think optional lightweight runtime checks (to be run during module load in my usal cases) would be great. I think it would lower support tickets to have clear errors and making that easier is a win.

@dmethvin
Copy link

dmethvin commented Jun 4, 2018

This doesn't seem like a Babel feature to me, there could be code outside Babel that is using something that requires a polyfill. The solution @reconbot proposes sounds workable to me. It could have a big list of features that may need polyfilling and just insert stubs that say "Whoops you need the polyfill for X" for any that aren't defined in the current environment.

@jeffbski
Copy link

jeffbski commented Jun 4, 2018

Well other than having the browsers natively implement this sort of thing (which would be great but not very likely), then I think Babel IS the next best place to implement this. Since it is parsing and transforming the code, it knows what is needed. And even if you were going to use some sort of runtime checks, it seems like Babel would be the one to introduce them to the start up of the code. You certainly wouldn't want to check every time you want to use a promise or ES-feature, ideally just do it once on load.

When @KevinAst was initially thinking about it, he came to the conclusion that Babel is the tool that has the necessary knowledge to know what is needed. So whether it is part of babel core or some sort of pluggin, it seems like a good place to do this. I can't see building a whole new tool to do this when it could be built on babel.

@KevinAst
Copy link
Author

KevinAst commented Jun 4, 2018

@dmethvin - I'm not sure I understand your response. I thought that @reconbot was affirming the overall premise of this feature request.

If a developer is running their library package through babel, what code would it not see?

As @jeffbski highlights, my conclusion was that babel is a natural driver for this process because it is knowledgeable of the constructs used in the package, and can selectively apply these run-time checks (through a machine generated start-up function) taking the burden away from the developer manually checking this very tedious process.

@dmethvin
Copy link

dmethvin commented Jun 4, 2018

@KevinAst I assumed that someone was not running all their code through Babel. If the checks are runtime checks, why not just introduce stubs for any feature not present that will give good error messages? That way you're covered whether all the code goes through Babel or not.

@KevinAst
Copy link
Author

KevinAst commented Jun 4, 2018

@dmethvin - I'm not sure where these run-time checks would come from. Are you suggesting something natively built-in to the JS engine would verify a es2015+ feature is supported before it executes? If so ...

  • It would be a fantasy to assume that all JS engines would adhere to this policy. The whole reason we have babel is to bridge the gap between the various JS engines (and their versions).

  • It would be non-optimal to have this overhead for every usage instance of a es2015+ feature.

Forgive me if I am misinterpreting your idea. I may be missing something rudimentary.

With the current state of things, as a library developer using es2015+ features:

  • I am going to run all my library code through babel, insuring it runs in all target environments (at least that is the goal).

  • The objective of this feature request is to front the loading of the library with a one-time lightweight run-time check that insures the library has the polyfill environment it needs ... producing non-obscure library-specific messages of any shortcomings. Because babel would be machine generating this resource, it would be complete and thorough and take the burden away from the developer.

    I thought that was what @reconbot was saying.

Thoughts?

@reconbot
Copy link

reconbot commented Jun 4, 2018

I believe @dmethvin is suggesting that the developer do it, while @KevinAst you are suggesting a plugin do it.

I think it's hard to know what preset-env target supports what feature. I target node 4 and 6 quite often and I often don't know what I'm using that isn't supported and have to dig in to look it up. This usually happens after I break something.

I think a way to generate a specific list of features that are used and need to be polyfilled for your target env is the right thing here. A plugin that automatically adds the needed runtime checks is the request.

@dmethvin
Copy link

dmethvin commented Jun 4, 2018

I'll describe the feature as succinctly as possible, if I got it wrong please replace with a better short paragraph.

The library to be published uses Babel to process all its own code plus the code in its own node_modules (including dependencies of dependencies) to determine their use of features that may need a shim. A Babel plugin generates a small check in front of each library module for the set of required-but-potentially-needing-shims features they use. At runtime, that check runs and can generate a good error if a feature is not present.

@KevinAst
Copy link
Author

KevinAst commented Jun 4, 2018

@dmethvin - I think that sounds correct :-)

@reconbot - Yes. I think we are saying the same thing. The only clarification I would make is: Because a library may run under any target env (under the privy of the client app), a run-time check alleviates any need for the library to specify a target env (i.e. the target is the JS engine that is executing at run-time).

@iamandrewluca
Copy link

iamandrewluca commented Apr 14, 2020

Is this still a thing with babel@7?

Using bellow babel dependencies in a my-library does solve this?

  • @babel/plugin-transform-runtime
  • @babel/runtime-corejs3
  • @babel/preset-env

And when an app will use my-library. Will babel only polyfill needed features based on it's target?

What would be a desired babel config for a library author?

Thanks!

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

7 participants