Skip to content
This repository was archived by the owner on Apr 14, 2021. It is now read-only.

Correct way to polyfill v1.0 in the browser (webpack) #118

Closed
gpbl opened this issue Jul 6, 2015 · 15 comments
Closed

Correct way to polyfill v1.0 in the browser (webpack) #118

gpbl opened this issue Jul 6, 2015 · 15 comments

Comments

@gpbl
Copy link

gpbl commented Jul 6, 2015

I've got my code working with v1.0 rc4 πŸŽ‰ yet I'm not sure why it works. My goal is to require the polyfill using webpack's code splitting, loading the locale-data after Intl has been polyfilled – e.g. in Safari.

Before v1.0 I was doing:

const IntlPolyfill = require("intl/Intl");
console.log(Intl) // OK!
Intl.NumberFormat = IntlPolyfill.NumberFormat;
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;

// later, load and add the locale data
require("intl/locale-data/jsonp/it");

Now I see the package to load has been moved in intl/dist/Intl. However just requiring it does not add a Intl global:

const IntlPolyfill = require("intl/dist/Intl");
console.log(Intl) // undefined
Intl.NumberFormat = IntlPolyfill.NumberFormat; // error!

Looking to the objects exported from dist/Intl i see Intl and IntlPolyfill. So I wrote the following, but loading the locale-data throws a IntlPolyfill is not defined:

window.Intl = require("intl/dist/Intl").Intl;

// later, load and add the locale data
require("intl/locale-data/jsonp/it"); // Error: IntlPolyfill is not defined

Instead, everything works fine when I do:

window.Intl = require("intl/dist/Intl").Intl;
window.IntlPolyfill = require("intl/dist/Intl").IntlPolyfill;

require("intl/locale-data/jsonp/it");

I'm using it right? What is the difference between the exported IntlPolyfill and Intl? Why is locale-data requiring IntlPolyfill (I was expecting it augmenting Intl)? I looked into the issues and PRs but couldn't find any help.
Thanks!

@okuryu
Copy link
Contributor

okuryu commented Jul 6, 2015

@gpbl
Copy link
Author

gpbl commented Jul 9, 2015

Thanks, interesting - will this change ship with v1.0? I'd keep this issue open until then.

@gpbl gpbl changed the title Correct way to polyfill in v1.0 in the browser (webpack) Correct way to polyfill v1.0 in the browser (webpack) Jul 9, 2015
gpbl added a commit to gpbl/isomorphic500 that referenced this issue Jul 9, 2015
@okuryu
Copy link
Contributor

okuryu commented Jul 9, 2015

polyfill-service v1.5.0 RC is now on QA. It depends on intl.js v1.0.0-rc-4. You're able to try it via qa.polyfill.io.

@gpbl
Copy link
Author

gpbl commented Jul 9, 2015

great thanks! however I'm not sure yet how polyfill-service would help with webpack's code splitting :)

@caridy
Copy link
Collaborator

caridy commented Jul 9, 2015

@gpbl once the polyfill service supports Intl, you should not try to bundle up the polyfill with your app code, instead you should load the polyfill and patch the runtime only when it is absolutely needed, and that's what the polyfill service from FT does. This concept extends to any polyfill :)

@gpbl
Copy link
Author

gpbl commented Jul 9, 2015

...so the polyfill service doesn't actually help with webpack, it is meant to replace it. But as @xjamundx writes, webpack already provides the way to apply the polyfill when needed. Why should I add a new library or depend from an external service?

I understand this is not the way the library authors think the polyfill has to be applied, yet it has been always possible to bundle it with webpack. However, differently from the versions < v1, we must both load Intl and IntlPolyfill to the window scope. Since not documented anywhere I wonder which side effects could cause, hence my questions.

@caridy
Copy link
Collaborator

caridy commented Jul 9, 2015

But as @xjamundx writes, webpack already provides the way to apply the polyfill when needed.

Apply !== Load/Parsing, are you going to penalize all your users (even those that are running in browsers with support for Intl), which could be +90% in some cases (for desktop apps)?

Polyfills are optional patches for runtimes, they have nothing to do with app code, they should not be bundled with app code IMO.

@gpbl
Copy link
Author

gpbl commented Jul 9, 2015

Apply !== Load/Parsing, are you going to penalize all your users (even those that are running in browsers with support for Intl), which could be +90% in some cases (for desktop apps)?

Ah! I see the misunderstanding. No, this is not the case. Look to this:

if (!window.Intl) {
    // hello safari
        require.ensure(['intl/Intl'], (require) => {
            window.Intl = require('intl/Intl')
            runMyApp()
        }, "IntlBundle");
    }
else {
   // hello chrome, ie, etc.!
   runMyApp()
}

it reads: if window.Intl is not available, download and require (i.e. execute) a file called IntlBundle.js which contains the intl/Intl package, then run the app. Browser supporting Intl will not even see the polyfill.

require.ensure is a pretty neat feature of webpack: during build time, with require.ensure webpack is instructed to create a separate file (chunk), with the specified packages in it, Intl in this case. On execution time, require.ensure will instead download the chunk, which in this case happens only if intl is not available.

We don't bundle Intl in the app code, rather in a separate file. This file is downloaded only by browsers not supporting Intl. As proof, check http://isomorphic500.herokuapp.com both on Safari and Chrome. You will see that in Safari the Intl polyfill is available in the Resources pane, while in Chrome is not.

This is different from the polyfill alone, which is loaded (but not applied) even on browsers that don't require it. polyfill-service will probably remove the need to download Intl on supporting browser, but it is still some code every browser needs to run, and a library developers have to include in their tools.

Code splitting is one of the main reasons to choose webpack as module bundler, that's why I insist Intl should work easily with it. It works great for this case.

@caridy
Copy link
Collaborator

caridy commented Jul 9, 2015

Ok, that makes sense. We can add the same configuration we use in react-intl, to define a custom way to require intl, and the locale data when using browserify/webpack, that should be easy. Will try to get that flush soon.

@caridy
Copy link
Collaborator

caridy commented Jul 9, 2015

Some progress here #121

@caridy caridy closed this as completed in 7f34a58 Jul 11, 2015
caridy added a commit that referenced this issue Jul 11, 2015
fixes #118: ignoring the inclusion of all locale date when bundling the polyfill with browserify/webpack.
@gpbl
Copy link
Author

gpbl commented Jul 15, 2015

Thanks! Very happy with 1.0 :-)

@donnrri
Copy link

donnrri commented Sep 27, 2015

Hi
When I add the polyfill service script tag into my html file ( <script src="https://cdn.polyfill.io/v1/polyfill.min.js?features=Intl.~locale.en"></script>), as the first script tag, I open my project in IE8 and the following error occurs:
'Int is undefined'

Im using React-Intl, and it cannot find window.intl I think ?

Any ideas on what I am doing incorrectly.. Thanks

@caridy
Copy link
Collaborator

caridy commented Sep 27, 2015

@donnrri are you using the polyfill service?

@wub
Copy link

wub commented May 4, 2016

@caridy I get the same error as @donnrri, even when using the polyfill service (in UC Browser)

@matte00
Copy link

matte00 commented Oct 5, 2017

@gpbl Ciao Giampaolo, have you got an example for angular


/*
 * Angular bootstraping
 */
import { platformBrowser } from '@angular/platform-browser';
import { decorateModuleRef } from './app/environment';
/*
 * App Module
 * our top level module that holds all of our components
 */
import { AppModuleNgFactory } from '../compiled/src/app/modules/app.module.ngfactory';

/*
 * Bootstrap our Angular app with a top level NgModule
 */
export function main(): Promise<any> {
  return platformBrowser()
    .bootstrapModuleFactory(AppModuleNgFactory)
    .then(decorateModuleRef)
    .catch((err) => console.error(err));
}

export function bootstrapDomReady() {
  document.addEventListener('DOMContentLoaded', main);
}

bootstrapDomReady();

Above is my startup script.
This is my require.ensure:


if ((!Modernizr.intl) || (!areIntlLocalesSupported(localesMyAppSupports))) {
  
  require.ensure(['intl'], (require) => {
    global.Intl = require('intl');
    require('intl/locale-data/jsonp/it.js'); // Italiano
    require('intl/locale-data/jsonp/en.js'); // Inglese
    require('intl/locale-data/jsonp/de.js'); // Tedesco
    require('intl/locale-data/jsonp/fr.js'); // Francese
    require('intl/locale-data/jsonp/da.js'); // Danese
    require('intl/locale-data/jsonp/es.js'); // Spagnolo
    require('intl/locale-data/jsonp/fa.js'); // Persiano
    require('intl/locale-data/jsonp/fi.js'); // Finlandese
    require('intl/locale-data/jsonp/el.js'); // Greco
    require('intl/locale-data/jsonp/nl.js'); // Olandese
    require('intl/locale-data/jsonp/nb-NO.js'); // Norvegese
    require('intl/locale-data/jsonp/pl.js'); // Polacco
    require('intl/locale-data/jsonp/ru.js'); // Russo
    require('intl/locale-data/jsonp/sv.js'); // Svedese

  });

}

I need that the intl chunk is downloading before app start. Any idea?

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

No branches or pull requests

6 participants