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

[docs] Add polyfill for IE 11 #13324

Merged
merged 2 commits into from
Oct 25, 2018
Merged

Conversation

eps1lon
Copy link
Member

@eps1lon eps1lon commented Oct 21, 2018

TL;DR:
Fixes the following issues in ie11:

Opened PR instead of issue so that reviewers have access to deploy preview and see if yarn size catches bundle size increase.

Current behavior

Our docs are deployed as JS that can be parsed by every browser listed in our .browserlistsrc

Expected

Our docs are deployed as JS that is equivalent in all browsers in our .browserlistrc

Context

Thanks to @babel/preset-env and .browserlistrc the syntax in our demos is transpiled so that it can be understood by mainstream browsers. However we don't have any polyfills (other than fetch). That provide functionality that was added with es6 and es7.

Since we don't have any compat tests for our docs things like Object.values or new Set(iterable) can easily slip by. We can't expect from contributors to know about all of those and expect from reviewers to catch those.

Solutions

eslint-plugin-compat

At first glance eslint-plugin-compat sounds like an easy solution but will never catch every compat issue because it does not have access to type information so bultin prototype polyfills can not be identified perfectly. However it is possible to catch those overzealous which is annoying for a linter.

useBultins with entry

@babel/preset-env with useBuiltins: 'entry' will polyfill every feature that was added in es6 and beyond and is not (correctly) implemented by the browsers in .browserlistrc. However this adds around 400KB uncompressed.

This option will add the following polyfills:

 Replaced `@babel/polyfill` with the following polyfills:
  es6.array.copy-within { "ie":"11" }
  es6.array.fill { "ie":"11" }
  es6.array.find { "ie":"11" }
  es6.array.find-index { "ie":"11" }
  es6.array.from { "chrome":"49", "edge":"14", "ie":"11" }
  es7.array.includes { "ie":"11" }
  es6.array.iterator { "ie":"11" }
  es6.array.of { "ie":"11" }
  es6.array.sort { "chrome":"49", "node":"6.11", "safari":"10" }
  es6.array.species { "chrome":"49", "ie":"11" }
  es6.date.to-primitive { "edge":"14", "ie":"11" }
  es6.function.has-instance { "chrome":"49", "edge":"14", "ie":"11" }
  es6.function.name { "ie":"11" }
  es6.map { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11" }
  es6.math.acosh { "ie":"11" }
  es6.math.asinh { "ie":"11" }
  es6.math.atanh { "ie":"11" }
  es6.math.cbrt { "ie":"11" }
  es6.math.clz32 { "ie":"11" }
  es6.math.cosh { "ie":"11" }
  es6.math.expm1 { "ie":"11" }
  es6.math.fround { "ie":"11" }
  es6.math.hypot { "ie":"11" }
  es6.math.imul { "ie":"11" }
  es6.math.log1p { "ie":"11" }
  es6.math.log10 { "ie":"11" }
  es6.math.log2 { "ie":"11" }
  es6.math.sign { "ie":"11" }
  es6.math.sinh { "ie":"11" }
  es6.math.tanh { "ie":"11" }
  es6.math.trunc { "ie":"11" }
  es6.number.constructor { "ie":"11" }
  es6.number.epsilon { "ie":"11" }
  es6.number.is-finite { "ie":"11" }
  es6.number.is-integer { "ie":"11" }
  es6.number.is-nan { "ie":"11" }
  es6.number.is-safe-integer { "ie":"11" }
  es6.number.max-safe-integer { "ie":"11" }
  es6.number.min-safe-integer { "ie":"11" }
  es6.number.parse-float { "ie":"11" }
  es6.number.parse-int { "ie":"11" }
  es6.object.assign { "ie":"11" }
  es7.object.define-getter { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es7.object.define-setter { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es7.object.entries { "chrome":"49", "ie":"11", "node":"6.11", "safari":"10" }
  es6.object.freeze { "ie":"11" }
  es6.object.get-own-property-descriptor { "ie":"11" }
  es7.object.get-own-property-descriptors { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11", "safari":"10" }
  es6.object.get-own-property-names { "ie":"11" }
  es6.object.get-prototype-of { "ie":"11" }
  es7.object.lookup-getter { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es7.object.lookup-setter { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es6.object.prevent-extensions { "ie":"11" }
  es6.object.is { "ie":"11" }
  es6.object.is-frozen { "ie":"11" }
  es6.object.is-sealed { "ie":"11" }
  es6.object.is-extensible { "ie":"11" }
  es6.object.keys { "ie":"11" }
  es6.object.seal { "ie":"11" }
  es7.object.values { "chrome":"49", "ie":"11", "node":"6.11", "safari":"10" }
  es6.promise { "chrome":"49", "ie":"11" }
  es7.promise.finally { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11", "node":"6.11", "safari":"10" }
  es6.reflect.apply { "ie":"11" }
  es6.reflect.construct { "ie":"11" }
  es6.reflect.define-property { "ie":"11" }
  es6.reflect.delete-property { "ie":"11" }
  es6.reflect.get { "ie":"11" }
  es6.reflect.get-own-property-descriptor { "ie":"11" }
  es6.reflect.get-prototype-of { "ie":"11" }
  es6.reflect.has { "ie":"11" }
  es6.reflect.is-extensible { "ie":"11" }
  es6.reflect.own-keys { "ie":"11" }
  es6.reflect.prevent-extensions { "ie":"11" }
  es6.reflect.set { "ie":"11" }
  es6.reflect.set-prototype-of { "ie":"11" }
  es6.regexp.constructor { "chrome":"49", "edge":"14", "ie":"11" }
  es6.regexp.flags { "edge":"14", "ie":"11" }
  es6.regexp.match { "chrome":"49", "edge":"14", "ie":"11" }
  es6.regexp.replace { "chrome":"49", "edge":"14", "ie":"11" }
  es6.regexp.split { "chrome":"49", "edge":"14", "ie":"11" }
  es6.regexp.search { "chrome":"49", "edge":"14", "ie":"11" }
  es6.regexp.to-string { "chrome":"49", "edge":"14", "ie":"11" }
  es6.set { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11" }
  es6.symbol { "chrome":"49", "edge":"14", "ie":"11" }
  es7.symbol.async-iterator { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11", "node":"6.11", "safari":"10" }
  es6.string.anchor { "ie":"11" }
  es6.string.big { "ie":"11" }
  es6.string.blink { "ie":"11" }
  es6.string.bold { "ie":"11" }
  es6.string.code-point-at { "ie":"11" }
  es6.string.ends-with { "ie":"11" }
  es6.string.fixed { "ie":"11" }
  es6.string.fontcolor { "ie":"11" }
  es6.string.fontsize { "ie":"11" }
  es6.string.from-code-point { "ie":"11" }
  es6.string.includes { "ie":"11" }
  es6.string.italics { "ie":"11" }
  es6.string.iterator { "ie":"11" }
  es6.string.link { "ie":"11" }
  es7.string.pad-start { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es7.string.pad-end { "chrome":"49", "edge":"14", "ie":"11", "node":"6.11" }
  es6.string.raw { "ie":"11" }
  es6.string.repeat { "ie":"11" }
  es6.string.small { "ie":"11" }
  es6.string.starts-with { "ie":"11" }
  es6.string.strike { "ie":"11" }
  es6.string.sub { "ie":"11" }
  es6.string.sup { "ie":"11" }
  es6.typed.array-buffer { "chrome":"49", "ie":"11" }
  es6.typed.int8-array { "chrome":"49", "ie":"11" }
  es6.typed.uint8-array { "chrome":"49", "ie":"11" }
  es6.typed.uint8-clamped-array { "chrome":"49", "ie":"11" }
  es6.typed.int16-array { "chrome":"49", "ie":"11" }
  es6.typed.uint16-array { "chrome":"49", "ie":"11" }
  es6.typed.int32-array { "chrome":"49", "ie":"11" }
  es6.typed.uint32-array { "chrome":"49", "ie":"11" }
  es6.typed.float32-array { "chrome":"49", "ie":"11" }
  es6.typed.float64-array { "chrome":"49", "ie":"11" }
  es6.weak-map { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11" }
  es6.weak-set { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11" }
  web.timers { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11", "node":"6.11", "safari":"10" }
  web.immediate { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11", "node":"6.11", "safari":"10" }
  web.dom.iterable { "chrome":"49", "edge":"14", "firefox":"52", "ie":"11", "node":"6.11", "safari":"10" }

useBultins with usage

useBuiltins: 'usage' is currently flagged as experimental but is probably the best tradeoff. It's still overzealous (which it will always be due to missing type information). For example it polyfills string.prototype.link every time a link property is accessed. I think this will not catch usage in other packages.

This option will add the following polyfills:

es6.array.find-index
es6.array.iterator
es6.array.sort
es6.function.name
es6.map
es6.number.constructor
es6.object.assign
es6.object.keys
es6.regexp.constructor
es6.regexp.match
es6.regexp.replace
es6.regexp.search
es6.regexp.split
es6.regexp.to-string
es6.set
es6.string.anchor
es6.string.includes
es6.string.iterator
es6.string.link
es7.array.includes
es7.object.entries
regenerator-runtime
web.dom.iterable

Just fix the code™

This is what web development was >10 years ago. Current offenders are Object.values, new Set(iterable), Array.prototype.findIndex. Object.keys is most likeley safe. I think there is some difference in the implementation concerning Symbols in some browsers but that doesn't affect our usage I believe. Array.prototype.includes is only used in server side render so we should be fine there.

@eps1lon eps1lon added the docs Improvements or additions to the documentation label Oct 21, 2018
@oliviertassinari
Copy link
Member

oliviertassinari commented Oct 21, 2018

@eps1lon As discussed in gitter. I'm against this change using polyfills in the docs. Not using any polyfill as been an explicit call. It has allowed us to surface issues with our core components. I would rather keep the docs as naked as possible. Also, we advocate for IE11 support and yet, we use non IE11 features in our demo.

@eps1lon
Copy link
Member Author

eps1lon commented Oct 21, 2018

@eps1lon It has allowed us to surface issues with our core components.

This is clearly not the case. I don't really care if we use polyfills or not in the demos but issues can not surface if we don't run tests on the docs.

That we use ESNext features does not contradict our pledge to support IE11 in our core components. How users transform/polyfill their code is up to them. Sure it's probably better to write .browserlistrc compliant code in the demos so that users don't introduce broken code into their codebase but our docs are not just demos (see stackblitz issue).

What is way worse is that we apparently support IE11 in the core but our docs might break in those browsers.

Again this was not a proposal for a specific implementation but remark that our docs have an issue. The PR was just for demonstration.

@oliviertassinari
Copy link
Member

oliviertassinari commented Oct 21, 2018

This is clearly not the case. I don't really care if we use polyfills or not in the demos but issues can not surface if we don't run tests on the docs.

What I mean is that we can reproduce the issue with the documentation demos. If the button requires a polyfill that we provide in the docs, people won't see it in our docs, they will only notice it in their app. They can't link the docs as a reproduction, they need to provide a codesandbox. For context, we didn't have codesandbox for a long time. It's easier now, so it's less important, I agree. Also, I have been considering for a long time to add Sentry on our documentation.

What is way worse is that we apparently support IE11 in the core but our docs might break in those browsers.

Yes, we really need to fix that!

@mui-org/core-contributors How do you guys want to handle the problem?

@oliviertassinari oliviertassinari self-assigned this Oct 25, 2018
@@ -43,6 +43,7 @@ const styles = theme => ({
},
fabButton: {
position: 'absolute',
zIndex: 1,
Copy link
Member

Choose a reason for hiding this comment

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

capture-d_ecran-2018-10-25-a-12 00 13

@@ -67,8 +69,7 @@ ga('create', '${config.google.id}', 'material-ui.com');
`,
}}
/>
<NextScript />
<script async src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js" />
<script defer src="https://cdn.polyfill.io/v2/polyfill.js?features=Array.prototype.findIndex" />
Copy link
Member

Choose a reason for hiding this comment

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

@oliviertassinari oliviertassinari force-pushed the docs/add-polyfill branch 2 times, most recently from 7a4570a to b828ea1 Compare October 25, 2018 11:01
@oliviertassinari oliviertassinari changed the title [docs] Add polyfills according to browserlist [docs] Add polyfill for IE 11 Oct 25, 2018
@oliviertassinari oliviertassinari removed their assignment Oct 25, 2018
@oliviertassinari
Copy link
Member

After, looks good
capture d ecran 2018-10-25 a 13 19 03

{/* Dynamic polyfill for IE 11 */}
<script
defer
src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Array.prototype.findIndex"
Copy link

Choose a reason for hiding this comment

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

🙊

Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't it be better to import the polyfill in the actual file so that next.js will only use it in that chunk? RIght now this is still somewhat dangerous when people land on pages that require that polyfill.

Copy link
Member

@oliviertassinari oliviertassinari Oct 25, 2018

Choose a reason for hiding this comment

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

The polyfill is only needed for dynamic interactions. It's a tradeoff, this way, we can keep using findIndex without bloating everybody bundles.

@@ -75,7 +75,7 @@ class HorizontalLinearStepper extends React.Component {
}

this.setState(state => {
const skipped = new Set(state.skipped.values());
const skipped = new Set(state.skipped);
Copy link
Member Author

Choose a reason for hiding this comment

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

that is still new Set(iterable) which is not supported in IE11: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility

I guess the underlying problem is that IE 11 does not implement the iterator protocol via @@iterator.

Copy link
Member

@oliviertassinari oliviertassinari Oct 25, 2018

Choose a reason for hiding this comment

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

It's the demo working on IE 11?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is. Either IE 11 fast tracks this somehow and not considers this an iterable or the MDN data is outdated.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

@oliviertassinari oliviertassinari Oct 25, 2018

Choose a reason for hiding this comment

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

Ok, it doesn't work 😢

var s1 = new Set()
s1.add('a')
console.log(s1.has('a'))
var s2 = new Set(s2)
console.log(s2.has('a'))

outputs

true
false

@eps1lon Well spotted 👍

{/* Dynamic polyfill for IE 11 */}
<script
defer
src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Array.prototype.findIndex"
Copy link
Member Author

Choose a reason for hiding this comment

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

Wouldn't it be better to import the polyfill in the actual file so that next.js will only use it in that chunk? RIght now this is still somewhat dangerous when people land on pages that require that polyfill.

@oliviertassinari oliviertassinari merged commit da96ecf into mui:master Oct 25, 2018
@eps1lon eps1lon deleted the docs/add-polyfill branch October 26, 2018 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Improvements or additions to the documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants