Preact-cli: Promise polyfill is not applied

Created on 8 Jun 2017  路  21Comments  路  Source: preactjs/preact-cli

Apps created via cli do not work on browsers without Promise implementation. For Windows users the easiest way to reproduce is to open https://clidemo.preactjs.com in IE11 and look into dev console.

This is problems occurs because of polyfills reside in separate chunks - which would be fine if webpack didn't use Promises in chunk loading code 馃槅 (which it does and states so in docs).

I'm thinking we can go several ways:

  • drop non ES2015 browsers support (which means dropping PhantomJS and possibly large part of potential cli users)
  • load promise polyfill eagerly
  • load promise polyfill via some custom JS code (dynamic script tag?)

Steps to reproduce

  1. preact create app
  2. cd app
  3. npm run serve
  4. Visit localhost:8080 in browser without ES6 support
  5. Click any navigation link (e.g. Profile)

Expected result:

  • Page is changed

Actual result:

  • Nothing happens. Error Promise is not defined is seen in console

All 21 comments

I'm receiving lots of Promise is not defined errors today. Not sure why but loading Promise must be synchronous (and on main chunk) with Webpack 2

Ah yeah. We should inject a script into the HTML template. I believe this used to be how it worked, not sure if that was committed or from earlier experiments.

All we need to do is produce a consistently named chunk like preact-cli-polyfills.js.

@developit inject = <script src='...'>? We use async tag so injecting <script async src=''> might not work (IMHO download order = random order). Going with defer might slow down perceived time to interactive tough.

I'm thinking about going with inline and load only promise polyfill. It's <1KB according to repo so network overhead with downloading might be bigger than parser & evaluation when including it in html.

Gosh, performance in unknown scenarios is sooo hard. 馃槶

BTW I was thinking the same for aggresive route spliting - it does more harm than good on slow 3G - might be worth to only split once they reach certain size - but that's another topic.

It's around 1100b. RE splitting - I think we may want to provide options to tweak the splits. As an example, I've seen people inline their homepage/index chunk to ensure quick page loads on the most popular page of a site.

@developit @rkostrzewski Instead of a defaultAttribute across the board, we can specify a pattern to match everything except ~ promise.polyfill.js (or whatever name).

Also, I would assume that this script be loaded under a HTML conditional:

<!--[if IE]>
 <script src='promise.polyfill.js'></script>
<![endif]-->

Hmm - that doesn't address Safari 9- which has no fetch polyfill :(

Perhaps this? It'll get blocked in chrome on mobile though:

<script>if(typeof fetch==='undefined')document.write('<script src="/polyfills.js">')</script>

Or less bad:

<script>if(typeof fetch==='undefined')document.head.appendChild(document.createElement('script')).src='/polyfills.js'</script>

How about breaking up fetch & promise polyfill?

maybe, though then we're hitting the cost issue like you noted with 3G. I had kindof thought of the poyfills as being injected based on a "cutting the mustard" test. I've amemded the above - detecting the existence of fetch is most reliable since fetch being available means Promise is available.

This could also be personal preference, but I don't think it actually makes sense to include a promise-polyfill by default into all bundles. The majority of the time (according to global usage stats), it won't be needed or loaded anyway.

I could have sworn Safari 9 supports Promise...? I'm looking at MDN & caniuse and they both assert this too. I've also shipped Promise-based code to Safari 9 before... <gulp>

@developit good call I haven't thought about that fetch requires promise implementation.

@lukeed Webpack requires Promise to load async chunk to we'll need a Promise polyfill in the bundle anyway.

@thangngoc89 Correct, but in the majority case, Promise _is_ supported already. A conditional tag (as provided) will load before the webpack runtime, which means Promise will always be ready.

@lukeed Please don't target only IE. I'm receiving error about Promise is not available on UC browser (Android)

Ah, right. Silly me 馃槣 Then yeah, the conditional tag that @developit provided will be a better fit.

Aplogies - Safari 9 supports Promise but not fetch. That's where detecting fetch is nice, it implies both.

Was there any resolution on this? @thangngoc89, I'm developing for UC as well and am currently just importing and initializing promise-polyfill in my app.js, which obviously feels ridiculous given that it's sitting in the polyfills chunk already.

Not yet, I've been trying to come up with a nice solution that doesn't block during render.

Okay. Thanks for the quick reply.

@developit

Perhaps this? It'll get blocked in chrome on mobile though:
<script>if(typeof fetch==='undefined')document.write('<script src="/polyfills.js">')</script>

That's not exactly true based on this.

Specifically Chrome will not execute the