Polyfills & Babel

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Babel is pretty amazing. But, it's even doing something else automatically that we haven't realized yet! Back in admin_article_form.js, and it doesn't matter where, but down in ReferenceList, I'm going to add var stuff = new WeakSet([]);:

... lines 1 - 47
class ReferenceList
{
constructor($element) {
var stuff = new WeakSet([]);
... lines 52 - 81
}
... lines 83 - 136
}
... lines 138 - 163

WeakSet is an object that was introduced to JavaScript, um, ECMAScript in 2015. Because the Encore watch script is running, go over and refresh the built file. Here it is: var stuff = new WeakSet([]);.

New Features & Polyfills

That's not surprising, right? I mean, we're telling Babel that we only need to support really new browsers, so there's no need to rewrite this to some old, compatible code... right? Well... it's more complicated than that. WeakSet is not a new syntax that Babel can simply change to some old syntax: it's an entirely new feature! There are a bunch of these and some are really important, like the Promise object and the fetch() function for AJAX calls.

To support totally new features, you need something called a polyfill. A polyfill is a normal JavaScript library that adds a feature if it's missing. For example, there's a polyfill just for WeakSet, which you can import if you want to make sure that WeakSet will work in any browser.

But, keeping track of whether or not you imported a polyfill... and whether or not you even need a polyfill - maybe the feature is already available in the browsers you need to support - is a pain! So... Encore pre-configures Babel to... just do it for us.

Check it out. Go back to package.json and change this to support older browsers:

30 lines package.json
{
... lines 2 - 25
"browserslist": [
"> .05%"
]
}

Then, just like before, go to your terminal and manually clear the Babel cache:

rm -rf node_modules/.cache/babel-loader/

And restart Encore:

yarn watch

Ok, let's go back to the browser, refresh the built JavaScript file and search for WeakSet. It still looks exactly like our original code. But now, just search for "weak". Woh. This is a bit hard to read, but it's importing something called core-js/modules/es.weak-set.

This core-js package is a library full of polyfills. Babel realized that we're trying to use WeakSet and so it automatically added an import statement for the polyfill! This is identical to us manually going to the top of the file and adding import 'core-js/modules/es.weak-set'. How cool is that?!

A Polyfill from the Past!

And... this is not the first time Babel has automatically added a polyfill! Open up build/app.js. Back in the editor, the get_nice_message module used a String method called repeat():

export default function(exclamationCount) {
return 'Hello Webpack Encore! Edit me in assets/js/app.js'+'!'.repeat(exclamationCount);
};

Whelp, it turns out that repeat() is a fairly new feature!

Search for "repeat" in the built file. There it is: it's importing core-js/modules/es.string.repeat. When I used this function, I wasn't even thinking about whether or not that feature was new and if it was available in the browsers we need to support! But because Encore has our back, it wasn't a problem. That's a powerful idea.

By the way, this is all configured in webpack.config.js: it's this .configureBabel() method:

... lines 1 - 2
Encore
... lines 4 - 48
// enables @babel/preset-env polyfills
.configureBabel(() => {}, {
useBuiltIns: 'usage',
corejs: 3
})
... lines 54 - 76
;
... lines 78 - 79

Generally-speaking, this is how you can configure Babel. The useBuiltIns: 'usage' and corejs: 3 are the key parts. Together, these say:

Please, automatically import polyfills when you see that I'm using a new feature and I've already installed version 3 of corejs.

That package was pre-installed in the original package.json we got from the recipe.

Next: let's demystify a feature that we disabled way back at the beginning of this tutorial: the single runtime chunk.

Leave a comment!

  • 2019-11-27 weaverryan

    Nice one! My favorite regex is the kind... that works... and ideally one I have a test for - because it's not easy for me either ;)

    Cheers!

  • 2019-11-24 D F

    ha, it's good to have an explanation ;). I have come up with a better solution using regex:
    exclude: /node_modules(?!(\\)@ckeditor)/
    Not only is this easier to read, it also targets only the files that are part of @ckeditor, and not all files that are being referenced to in /assets/ckeditor5.
    ps. There might be better ways to write the regex, I'm not that great with it.

  • 2019-11-18 weaverryan

    Ohhhhhhhhhhhh. THANK YOU. Of COURSE. Ultimately, the "exclude" is passed to an "exclude" on the "loader". That *usually* is a regexp (e.g. /node_modules) but you can *also* set it to an "object" where (by coincidence) "exclude" is an option: :p. I'm still not sure why you weren't able to pass it to the top-level exclude... but I at least know why that was parsing correctly.

    Cheers!

  • 2019-11-18 D F

    Hey Ryan,

    No need to apologize for taking some time to reply! I can only imagine how busy you are. Maybe this comment helps (scroll down to the second sections, you've seen it before ;)). It was the reason why I even tried such a strange thing.

    Cheers, Dirk

  • 2019-11-18 weaverryan

    Hey D F!

    Sorry for my slow reply!

    Wow... that is WEIRD! I can't explain this fully. But, I can give you some info that might help. There are (and this confused me, I had to triple-check some logic) two different "exclude" things going on. First, the "exclude" option here is meant to tell Webpack to *not* process matching paths through "babel-loader". That appears to be working correctly (from double-checking and dumping some files in Encore). I can't explain why the first would be not working and the second *would* be working. Also, Babel itself has an "exclude" option - that is something totally different - just mentioning to avoid confusion.

    Btw, the "exclude" option defaults to ignoring node_modules. When you override it, you replace that default. But, that should be happening on both "variations" of what you're doing - so I don't think it's really related.

    Sorry I can't give you a more clear answer on this one - it's a bit of a mystery!

  • 2019-11-11 D F

    I tried including a custom build of CKEditor5 in my Webpack Encore config, which not too easy. I made it work, but I have one question left. In my assets directory, I created a directory 'ckeditor5', which contains all the code for my custom build. Babel should not transpile this code because, well.. the editor does not work with older versions of JS (even when transpiled). So I wanted to exclude this directory and everything in it from Babel. I tried


    .configureBabel(() => {}, {
    exclude: /assets\/ckeditor5/
    )}

    That did not work. What does work is:


    .configureBabel(() => {}, {
    exclude: {
    exclude: /assets\/ckeditor5/
    }
    )}

    But why? (ps. sorry for the layout of the code. Even though I tried to edit it to make it look normal, it does not seem to work).

  • 2019-09-13 weaverryan

    Hey David Wilson!

    Hmm, I was afraid that one would be the problem :/. I say "afraid" because that's a Webpack setting that controls a variety of things - https://webpack.js.org/conf... - so it's tough to know which is causing the real issue :/. If you are willing to a bit more digging, I would:

    A) Set the mode back to development... or basically.. undo any hacks you added for debugging
    B) Go to the optimization config - https://github.com/symfony/... - and start changing some of the keys from their development-mode defaults https://webpack.js.org/conf... to their production mode values - https://webpack.js.org/conf... - optimization isn't the only spot where these two modes change config, but it is the most significant (note that each mode also enables different plugins, but this is actually caused by the optimization flags usually - e.g. if you change minimize to true, it *causes* the TerserPlugin to be added).

    The fact that the development mode is the problem and production works is really odd - I would (if anything) expect the opposite, as code is minified in production... so in theory you could "lose more". That's why I'm a bit perplexed as to which of these configs could be causing the issue... :/

    Let me know what you find out!

    Cheers!

  • 2019-09-13 David Wilson

    Hi Ryan,

    Thanks for getting back to me. Changing the mode to 'production' in https://github.com/symfony/...

    fixed it, but that still doesn't really tell me much unless you have an idea? I had turned the babel cache directory off earlier hoping that was the issue and that didn't work either.

  • 2019-09-13 weaverryan

    Hey David Wilson !

    > So it seems like it only transpiles down in general if I run a production build, I was assume it would transpile for dev build as well?

    Hmm... yea... it *should* transpile down in all cases. Well, specifically here, it's all about the "polyfill"... which is also done by Babel... but they're two (sort of) separate things (rewriting syntax == transpiling vs adding features == polyfill). Either way, I can't see anything that would cause this behavior... so we'll need to do a bit of digging to figure it out... if you're up for it ;).

    First, you can see where the "Encore.isProduction()" value is used internally in Encore - it's just a few places: https://github.com/symfony/...

    The easiest way to debug this would be to manually go into node_modules/@symfony/webpack-encore and "tweak" these settings ones-by-one and see if there is a difference. I'm particularly interested in tweaking lib/loaders/babel.js - https://github.com/symfony/... - I would change that line to "false" (the value for the production build) and see if it changes the behavior of the dev build. I'm also interested in the "mode" key - https://github.com/symfony/... - hardcoding that to "production". Basically, we want to see what is triggering the behavior change... which is super weird.

    If you don't care so much... and don't want to debug - I understand! But if you *are* curious... then I think with a bit of digging, we can find the root cause.

    Cheers!

  • 2019-09-13 David Wilson

    Hi Ryan,

    We need to support IE11 and higher, so in my package.json I added:

    ```
    "browserslist": [
    "ie >= 11"
    ]
    ```
    However, I run `yarn dev` I get a javascript error about `Object.assign not found`. But if I run `yarn build` for a production build everything works in IE11. I have tried deleting the node_modules cache like it instructed in this video. Why does the transpiling for IE11 not work for a dev build? Also if I run `yarn build` without the browserslist it works in IE11. So it seems like it only transpiles down in general if I run a production build, I was assume it would transpile for dev build as well?

  • 2019-06-11 weaverryan

    Good find Krzysztof Krakowiak! It's a pretty new feature - happy it's working for you!

  • 2019-06-11 Krzysztof Krakowiak

    SOLVED: seems that includeNodeModules does the job!

    Hi Ryan, is it possible that you could add option to encore configuration to specify which libraries form node_modules should be transpiled?

    Vue cli seems to have such option: transpileDependencies

    Can we have something similar for webpack encore?

    At the moment each time I have such issue (related to IE11), I just copy the library to my private vendor folder, but this is poor solution.

  • 2019-05-15 Krzysztof Krakowiak

    Thanks Ryan, I really appreciate that you always answering my questions, this is really helpful. Again - Thanks a lot!

  • 2019-05-14 weaverryan

    Hey Krzysztof Krakowiak!

    > Does it transpile/poly-fill code from node_modules too?

    Excellent question! By default, no, it does not. Actually, it's a setting of Babel and Encore pre-configures it to *not* process things in node_modules/ - here is some info about that - https://github.com/symfony/.... We do this for performance - Babel slows things down and *usually* you don't need Babel to be executed on node_modules stuff (that's not *always* true, but running it all the time when you usually don't need it would be a huge pain). You had an interesting situation where you had a 3rd party module that didn't support IE11. In a sense, you were "fixing" their code. Ideally, 3rd party libraries would support all "reasonable" version of browsers.

    > What about: "@babel/plugin-transform-runtime", is this plugin required for anything?

    To be honest with you, I'm not sure. You could try adding this and see if it has any affect. I'm not familiar with this plugin... it *might* save on some filesize - but I really don't know. Sorry I can't give you a better answer on that one!

    Cheers!

  • 2019-05-11 Krzysztof Krakowiak

    Hello Ryan, could you tell something more about Babel? Does it transpile/poly-fill code from node_modules too? I had issue with one dependency from node_modules under IE11, and to resolve it I copied it to asset/vendor folder ad now it works.

    Before this tutorial I had different babel configuration through encore, not sure what are the differences, but I had poly-fills included in my main.js, but I have changed it now to your setup and all works :)

    What about: "@babel/plugin-transform-runtime", is this plugin required for anything?