After months of community work, I'm am thrilled to be able to talk about the release of Webpack Encore 0.21.0. This represents a huge step forward to stay with the latest versions of industry-standard tools and a bunch of great features along the way.

So, what changes and improvements happened? Let's find out!

WebpackEncoreBundle & encore_entry_script_tags()

Besides the Webpack 4 upgrade itself (described below) the most noticeable change is that a new bundle has been created: WebpackEncoreBundle. If you've previously installed the symfony/webpack-encore-pack, you can remove it and install the bundle instead:

1
2
composer require symfony/webpack-encore-bundle
composer remove symfony/webpack-encore-pack

Notice there is no --dev flag for symfony/webpack-encore-bundle. That's on purpose: it contains a new feature that makes it much easier to include the script and link tags for an entry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{# templates/.../checkout.html.twig #}

{% block javascripts %}
    {{ parent() }}

-     <script src="{{ asset('build/checkout.js') }}"></script>

+     {{ encore_entry_script_tags('checkout') }}
{% endblock %}

{% block stylesheets %}
    {{ parent() }}

-     <link rel="stylesheet" href="{{ asset('build/checkout.css') }}">

+     {{ encore_entry_link_tags('checkout') }}
{% endblock %}

Why have we added these functions? Because using these functions gives you versioning and CDN support for free, as well as enabling the new "Split Chunks" feature described below.

Webpack 4, Babel 7 and Major Upgrades

The main focus of this release was to upgrade Encore to use the latest versions of Webpack & Babel. Webpack 4 contains a long list of improvements - like faster build times and a new way to "split" your built files into more optimized pieces (more on that later). Babel 7 is also a huge release - it contains optimizations and support (via @babel/preset-env) for browserslist (more on that later).

Several other libraries were also upgraded - see the CHANGELOG for full details. If you're using Encore's features exclusively, you shouldn't have any problem with these major version upgrades. If you've added custom features - be sure to check the changes for each library.

splitChunks() & encore_entry_script_tags()

One of the biggest innovations in Webpack 4 is SplitChunksPlugin. This solves the problem of duplicated code between multiple compiled entry files. Before Webpack 4, this was solved in Encore via the createSharedEntry() method. This method will not go away soon, but Webpack now has a better option.

Encore 0.21.0 adds a new splitEntryChunks() method. When this feature is enabled, Webpack's SplitChunksPlugin will use an optimization algorithm to automatically "split" the final JavaScript and CSS files into multiple pieces. For example, an entry called "app" will normally output app.js and app.css files. But with splitEntryChunks(), it may output app.js and vendor~app.js (which would contain code shared by other entry files) as well as app.css and vendor~app.css.

But this now means you may need multiple script and link tags in your template! To solve this problem, Encore outputs a new entrypoints.json file that contains all the JavaScript and CSS files needed for each "entry". The new WebpackEncoreBundle Twig functions know how to read from this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{# templates/.../checkout.html.twig #}

{% block javascripts %}
    {{ parent() }}

    {#
        May now render multiple script tags:
            <script src="/build/runtime.js"></script>
            <script src="/build/checkout.js"></script>
            <script src="/build/vendor~checkout.js"></script>
    #}
    {{ encore_entry_script_tags('checkout') }}
{% endblock %}

{% block stylesheets %}
    {{ parent() }}

    {#
        May now render multiple link tags:
            <script src="/build/checkout.css"></script>
            <script src="/build/vendor~checkout.css"></script>
    #}
    {{ encore_entry_link_tags('checkout') }}
{% endblock %}

That's it! Webpack will intelligently "split" your entry files and the correct script and link tags will always be rendered. The manifest.json file is still output by Encore, but is only used if you need to refer to a static asset (like an image) from your Twig template via the asset() function.

New runtime.js File

This version of Encore also introduces an optional new output file called runtime.js. You should explicitly enable or disable it by calling either enableSingleRuntimeChunk() or disableSingleRuntimeChunk(). If you enable it, a new runtime.js file will be output and needs to be included in your page (but encore_entry_script_tags() will handle this automatically)

What's the point of runtime.js? Suppose you include multiple entry JavaScript files on the same page - like an app.js in your layout and also checkout.js when you're on a checkout page. Then:

  • With enableSingleRuntimeChunk(): if the same module (e.g. jquery) is required by both entry files, they will require the same object.
  • With disableSingleRuntimeChunk(): if the same module (e.g. jquery) is required by both entry files, each will receive a different object.

Using enableSingleRuntimeChunk() will give you the same behavior as Webpack 3, but you can choose whichever setting works better for your app.

New copyFiles() Feature

One of the most-asked-for features in Encore is the ability to copy static files into your build directory. Until now, we recommended using the copy-webpack-plugin. This plugin is great, but if you used version hashes in your filenames, the manifest.json file would not contain the correct key for this copied file (the key would be the versioned/hashed filename). In practice, this made it impossible to version your copied files with filename hashes.

To fix this, Encore now has a copyFiles() method! It's simple: use it to copy files from one directory (e.g. assets/images) into your "build" directory. If versioning is enabled, all files are given version hash names and are included in manifest.json so you can fetch the correct filename via the asset() function in Twig.

Async Code Splitting out-of-the Box

Did you know that Webpack allows you to require modules asynchronously, only when you need them? The feature is called Code Splitting. It's not new, but in the latest version of Encore, you can use it without any extra configuration:

1
2
3
4
5
6
7
$('.js-open-video').on('click', function() {
    // async load the VideoPlayer module
    import('./components/VideoPlayer').then(({ default: VideoPlayer }) => {
        // once it loads, use it!
        const player = new VideoPlayer('some-element');
    });
});

browserslist Support

There are several tools in Encore that need to know what versions of browsers your site needs to support. For example, both Babel and autoprefixer (from PostCSS) will compile your code differently if you need to support older browsers versus only newer browsers.

Until now, there was no one place to configure this. But, thanks to upgrades to @babel/preset-env, you can now set your browsers via a browserslist key in package.json.

For more details, see the browserslist documentation for Encore.

Support for Uglifying ES6 Code

Before this release, Uglify (the library that minifies your JavaScript for production), was unable to process ES6 code. This meant that you were forced to transpile all your JavaScript to ES5 code, even if your site didn't need to support older browsers.

Because of this, Uglify was replaced with terser - a fork of Uglify that processes ES6 code. You shouldn't notice any difference in your app unless you use the configureUglifyJsPlugin() method (use configureTerserPlugin() now).

Ready to Upgrade?

Because this is a big release, upgrading requires a few steps:

  1. Install the bundle via composer require symfony/webpack-encore-bundle
  2. Remove the webpack-encore-pack via composer remove symfony/webpack-encore-pack
  3. Update your version constraint in package.json for @symfony/webpack-encore to ^0.21.0
  4. Run yarn upgrade
  5. Replace your manual script and link tags with the new encore_entry_script_tags() and encore_entry_link_tags() functions.

And, over time, replace createdSharedEntry() with splitEntryChunks(). If you have any issues, please report them on the symfony/webpack-encore repository.

Enjoy!

Published in #Living on the edge