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

core(legacy-javascript): reduce polyfills, fix core-js import in test #10937

Merged
merged 6 commits into from
Jun 12, 2020

Conversation

connorjclark
Copy link
Collaborator

@connorjclark connorjclark commented Jun 10, 2020

The PR addresses two things:

  1. require('core-js') is vital for preset-env to import any polyfills. The esmodules variants were missing this, so the bundles only differed minimally (transforms). After adding this, I noticed that Object.create was included for core-js@2 + esmodule: true, so I removed that polyfill.
  2. At this point, the core-js@3 esmodules: true variant had many signals–the expectation is that there be none. I realized that many polyfills should not be used as discriminators for identifying an module/nomodule build. See preset-env core-js 3 includes extra polyfills babel/babel#11700 (comment) . This resulted in removing ~25 polyfills (see below).
Int8Array, Uint8ClampedArray, ArrayBuffer, DataView, Map, WeakMap, Promise, Object.getOwnPropertyDescriptor, Array.prototype.lastIndexOf, Number.parseFloat, Object.assign, Reflect.set, Set, String.prototype.endsWith, String.prototype.includes, String.prototype.padEnd, String.prototype.padStart, String.prototype.startsWith, String.prototype.trim, Float32Array, Float64Array, Int16Array, Int32Array, Uint16Array, Uint32Array, Uint8Array

Related: #10852

@connorjclark connorjclark requested a review from a team as a code owner June 10, 2020 01:45
@connorjclark connorjclark requested review from patrickhulce and removed request for a team June 10, 2020 01:45
{signal: 'String.prototype.codePointAt'},
{signal: 'String.fromCodePoint'},
{signal: 'String.raw'},
{signal: 'String.prototype.repeat'},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have been a clue something was wrong. the legacy-javascript.js fixture here is the core-js@3 esmodules: false variant, which should have dozens of variants.

}, /** @type {string[]} */ ([])).join(', ');
variants.push({name: dir, signals});

const signals = [];
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#10867 should have failed this test in CI .... fixing here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this is a thorny problem. we don't know when to run these tests because something they depend on changed :/

I don't have any great ideas to fix this though other than catching it the next time. you?

Copy link
Collaborator Author

@connorjclark connorjclark Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but test-legacy-javascript.sh checks for any file with legacy-javascript in the path. It should have triggered on a change to the audit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well then that's even more alarming :)

my point is still a problem I don't know a good way to deal with, but perhaps less urgent than outright broken testing 😆

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it did fail but didn't exit with code 1

https://travis-ci.org/github/GoogleChrome/lighthouse/builds/696156441#L1224

need to add

main().catch(err => {
  console.error(err);
  process.exit(1);
})

1352 true/main.bundle.min.js
188400 false/main.bundle.min.js
64217 true/main.bundle.min.js
63536 true-and-bugfixes/main.bundle.min.js
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The small difference from bugfixes: true is because it only reduces the number of transforms applied. And the main.js file used is super small.

Copy link
Collaborator Author

@connorjclark connorjclark Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, and adding bugfixes variant is orthogonal to the rest of this PR, I just wanted to add this.

@@ -144,11 +144,12 @@ describe('LegacyJavaScript audit', () => {
'String.prototype[\'repeat\'] = function() {}',
'Object.defineProperty(String.prototype, "repeat", function() {})',
'Object.defineProperty(String.prototype, \'repeat\', function() {})',
'Object.defineProperty(window, \'WeakMap\', function() {})',
// Currently are no polyfills that declare a class. May be in the future.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hopeful that we can somehow get preset-modules to workaround the bugs that require Promises to be polyfilled. keeping this around + the code in legacy-javascript that detects this.

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to interpret the output in babel/babel#11700 (comment) a bit. Could you help me out?

Does each row of the polyfill say which target requires that the polyfill be used?

For example for the typed arrays it looks like safari 10.1 forces it, even though Uint8Array was in Safari 5. I'm guessing there is some bug that makes it not behave exactly the same way?

I wish we had a better way to track each of these bugs, what they are/why you need them and/or an even more modern delivery discriminant than esmodules. Some of these are really massive polyfills just for what I'm guessing is like one obscure bug fix of a 3 year old versioned browser.

}, /** @type {string[]} */ ([])).join(', ');
variants.push({name: dir, signals});

const signals = [];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh this is a thorny problem. we don't know when to run these tests because something they depend on changed :/

I don't have any great ideas to fix this though other than catching it the next time. you?

['String.prototype.startsWith', 'es6.string.starts-with'],
['String.prototype.trim', 'es6.string.trim'],
// These break the coreJs2/coreJs3 naming pattern so are set explicitly.
{name: 'ArrayBuffer', coreJs2Module: 'es6.typed.array-buffer', coreJs3Module: 'es.array-buffer.constructor'},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aren't these all the biggest ones? and they're really all included in every esmodules: true bundle? :(

Copy link
Collaborator Author

@connorjclark connorjclark Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ye :(

['Number.parseInt', 'es6.number.parse-int'],
['Object.assign', 'es6.object.assign'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still incredulous that modern browsers need Object.assign polyfilled... 😢

@@ -205,7 +200,6 @@ class LegacyJavascript extends Audit {
['Object.preventExtensions', 'es6.object.prevent-extensions'],
['Object.seal', 'es6.object.seal'],
['Object.setPrototypeOf', 'es6.object.set-prototype-of'],
['Promise', 'es6.promise'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and promises!?

@connorjclark
Copy link
Collaborator Author

connorjclark commented Jun 10, 2020

I'm struggling to interpret the output in babel/babel#11700 (comment) a bit. Could you help me out?

Does each row of the polyfill say which target requires that the polyfill be used?

Yes. esmodules: true sets the targets to

{
  "android": "61",
  "chrome": "61",
  "edge": "16",
  "firefox": "60",
  "ios": "10.3",
  "opera": "48",
  "safari": "10.1",
  "samsung": "8.2"
}

and each polyfill or transform that matches one of these targets is printed out.

For example for the typed arrays it looks like safari 10.1 forces it, even though Uint8Array was in Safari 5. I'm guessing there is some bug that makes it not behave exactly the same way?

Honestly, I can't figure this out.

core-js@3 uses this data to determine what to pull in: https://github.com/zloirock/core-js/blob/master/packages/core-js-compat/src/data.js#L1037

It seems that because safari is not listed, that it would get polyfilled for all versions of safari (ios too).

If this is WAI I must assume there is some bug in all versions Safari that makes typed arrays inconsistent with other browsers. typed-array was added in core-js@2, can't find much discussion other than this: zloirock/core-js#90

@developit said that all methods that accept a regex parameter must be polyfilled in older browsers. I don't see a similar reason to polyfill the typed arrays.

EDIT: seems it's only b/c of a bug in toString: zloirock/core-js#300 (comment) .

A really nice thing about the caniuse data is that is gives reasons/comments in the data for such things. core-js-compat has none of that, so it's hard for us to work out if this is intentional or not.

I wish we had a better way to track each of these bugs, what they are/why you need them and/or an even more modern delivery discriminant than esmodules

that's the dream ...

@paulirish and I want to explore extending preset-modules to reduce the polyfill footprint. AFAIK it currently only reduces the number of transforms necessary. seems that typed-array may be plausible if it's really just b/c of toString.

Promise is included for a few reasons...

es.promise { "android":"61", "chrome":"61", "edge":"16", "firefox":"60", "ios":"10.3", "opera":"48", "safari":"10.1", "samsung":"8.2" }

https://github.com/zloirock/core-js/blob/master/packages/core-js-compat/src/data.js#L705

  • Chrome 61 (same version that first supports esmodules) V8 has a bug, probably not fixable w/ a code transform but don't know yet
    • Makes me thing a motherfnBugfixes: true for arcane crap like this might be good...
  • Edge 16 because not explicitly given in core-js-compat, so all versions of Edge apply ...
  • FF 60, idk why b/c caniuse has marks y at 29
  • ios, safari again IDK, caniuse has safari mark y at 7.1 ...

@connorjclark
Copy link
Collaborator Author

Also, I have some catching up to do on the babel polyfill space :) babel/babel#11700 (comment)

It seems the logic for determining what polyfills are needed is being pulled up to a new plugin @babel/polyfills–instead of what we have today which is core-js@3 doing it's own thing. I'll need to look into how the babel plugin is determining what polyfills are needed. I hope its process has more attribution than core-js does.

@hzoo I understand this stuff is experimental atm, just wondering if the plan is to later modify @babel/preset-env to use any polyfill provider, or if the plan is to require @babel/preset-env for transforms (no core-js) and @babel/polyfills for polyfills.

@nicolo-ribaudo
Copy link

nicolo-ribaudo commented Jun 10, 2020

The plan is to keep the polyfills and the transforms separated, so eventually everyone will need to use one of the new polyfill providers.

On the other hand, we are exploring the idea moving back preset-env into @babel/core as a long-term goal,so you might have to only enable the polyfill provider plugin in the future.


If you are 100% sure that the typed arrays bug doesn't affect your code, you can use the exclude option to forcefully disable the polyfills.

@connorjclark
Copy link
Collaborator Author

connorjclark commented Jun 10, 2020

The plan is to keep the polyfills and the transforms separated, so eventually everyone will need to use one of the new polyfill providers.
On the other hand, we are exploring the idea moving back preset-env into @babel/core as a long-term goal, so you might have to only enable the polyfill provider plugin in the future.

Interesting, thanks for the insight.

If you are 100% sure that the typed arrays bug doesn't affect your code, you can use the exclude option to forcefully disable the polyfills.

Thanks @nicolo-ribaudo . The goal of this audit ultimately is to recommend other developers to use preset-modules and esmodules to create two bundles and use the module/nomodule pattern. In our documentation we'll also recommend bugfixes: true.

We could go further and say "exclude typed polyfills" because the bug is seemingly so negligible yet the polyfill so large. We'd much rather explore the possibility of getting that to be the default behavior of esmoudles: true bugfixes: true (much wider reach). But using excludes as part of our recommended pattern might be a nice short-term solution ... (#10945).

@nicolo-ribaudo
Copy link

Also note that currently preset-modules should now have the same behaviour as preset-env { bufgixes: true, esmodules: true }, so you don't need to recommend both.

Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sad day but important 👍

['Array.prototype.fill', 'es6.array.fill'],
['Array.prototype.filter', 'es6.array.filter'],
['Array.prototype.find', 'es6.array.find'],
['Array.prototype.findIndex', 'es6.array.find-index'],
['Array.prototype.forEach', 'es6.array.for-each'],
['Array.from', 'es6.array.from'],
['Array.isArray', 'es6.array.is-array'],
['Array.prototype.lastIndexOf', 'es6.array.last-index-of'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of removing these can we flag them with an extra property or move them to another array somewhere? it'd be nice to keep track of what we should detect once we figure out how to solve the bugfixes problem and not lose all this research

Copy link
Collaborator Author

@connorjclark connorjclark Jun 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what we should detect is simply any polyfill that will never be in a bundle w/ esmodules: true, bugfixes: true (using either core-js@2 or core-js@3). everything being removed fails that criteria. if we add any back, it'll be because something changed upstream (and we can just add back to this list) or we accompany it with documentation for how and why to exclude (what we may do for typed-array, promise).

so I'm ok w/ just deleting these items. git blame and these PRs are enough history for me if I need to refer back for some reason.

This list is derived from the diff of the debug output when you toggle esmodule: true/false. It may be good to print out that output in legacy-javascript/run.js (doing much more than that would be more work than I'd like)


sidenote on the line you commented on... last-index-of used to be set to an older version of safari but was bumped here: zloirock/core-js@e4cea55

no idea what bug this is preventing lol https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/array-method-uses-to-length.js

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright wfm

I'd think future me would be annoyed by having to hunt down an ancient PR and copy paste individual lines from a diff that no longer merges for this info but I'll defer to your future self since it's almost definitely going to be you anyway :)

}, /** @type {string[]} */ ([])).join(', ');
variants.push({name: dir, signals});

const signals = [];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it did fail but didn't exit with code 1

https://travis-ci.org/github/GoogleChrome/lighthouse/builds/696156441#L1224

need to add

main().catch(err => {
  console.error(err);
  process.exit(1);
})

@@ -1,29 +1,11 @@
all-legacy-polyfills
175764 all-legacy-polyfills-core-js-3/main.bundle.min.js
125257 all-legacy-polyfills-core-js-2/main.bundle.min.js
85169 all-legacy-polyfills-core-js-3/main.bundle.min.js
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ouch, takin' a 50% hit to the ceiling of benefit :(

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:(

'WeakMap = function() {}',
'window.WeakMap = function() {}',
'function WeakMap() {}',
// 'WeakMap = function() {}',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we delete these or are these comments important?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you see my comment a few lines up

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did, I just didn't make the connection :)

maybe just group all 4 of them together with that comment?

@connorjclark connorjclark changed the title core(legacy-javascript): fix core-js import in test, reduce polyfills core(legacy-javascript): reduce polyfills, fix core-js import in test Jun 12, 2020
Copy link
Collaborator

@patrickhulce patrickhulce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

'WeakMap = function() {}',
'window.WeakMap = function() {}',
'function WeakMap() {}',
// 'WeakMap = function() {}',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did, I just didn't make the connection :)

maybe just group all 4 of them together with that comment?

['Array.prototype.fill', 'es6.array.fill'],
['Array.prototype.filter', 'es6.array.filter'],
['Array.prototype.find', 'es6.array.find'],
['Array.prototype.findIndex', 'es6.array.find-index'],
['Array.prototype.forEach', 'es6.array.for-each'],
['Array.from', 'es6.array.from'],
['Array.isArray', 'es6.array.is-array'],
['Array.prototype.lastIndexOf', 'es6.array.last-index-of'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright wfm

I'd think future me would be annoyed by having to hunt down an ancient PR and copy paste individual lines from a diff that no longer merges for this info but I'll defer to your future self since it's almost definitely going to be you anyway :)

@hzoo
Copy link

hzoo commented Jun 15, 2020

I wish we had a better way to track each of these bugs, what they are/why you need them and/or an even more modern delivery discriminant than esmodules

@paulirish and I want to explore extending preset-modules to reduce the polyfill footprint. AFAIK it currently only reduces the number of transforms necessary. seems that typed-array may be plausible if it's really just b/c of toString.

I think we would be glad to chat or work with y'all on this!

@zloirock
Copy link

If next time you will have questions about what and why polyfilled, please, ask me or at least take a look at tests that generate core-js-compat.

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

Successfully merging this pull request may close these issues.

7 participants