Webpack Encore under the hood

A more in-depth guide on the workings and internals of Webpack Encore

webpack encore
webpack encore

Nowadays webpages, sites and apps leverage the power of frontend frameworks more than ever. These come with their own ecosystem and integrating them into your current development stack can be tricky. That's where Webpack Encore comes to the rescue. It was released together with Symfony 4 and aims to be the go to asset manager in Symfony projects but can be used anywhere.

What is Webpack Encore and how does it work ?

If you've ever tried to set up Webpack from scratch you'll know it can be somewhat time consuming. Well, that's what Webpack Encore tries to solve. On its own you will see it isn't all that special, just a simple wrapper around Webpack doing some of the heavy lifing for you by exposing a more human readable API. Don't be fooled though! This package wont solve everything, you should still have a basic understanding of Webpack as well!

 

Installation.. magic..

Adding the package is nothing more than adding any other NPM module, we've all done this before... If you didn't, prepare yourself for the universe to be downloaded. If you're using Symfony4/Flex, instructions can be found in Symfony's documentation.
# When you prefer to install with NPM
npm i @symfony/webpack-encore --save-dev
# When you prefer to install with Yarn
yarn add @symfony/webpack-encore --dev
# When you prefer to install with SF Flex
composer require encore && yarn install

Unboxing the package

Create a new webpack.config.js file in the root of your project with the following contents:
// webpack.config.js
var Encore = require('@symfony/webpack-encore');

Encore;

// export the final configuration
module.exports = Encore.getWebpackConfig();

Diving in

If you take a look in the Encore repository you can find the index.js file. That's the API object that we just imported. It exposes a set of functions that we can use, this will create and alter the final exported Webpack config.

If you scroll through the file you will see it's just a javascript object that loads the default config from the WebpackConfig object or taking a callback as argument for custom config. Note that this object isn't directly exported but passed through a Proxy object . The proxy acts as a guard in front of the raw object, making sure that called properties and methods exist. If not, it will log an error and suggest the most relatable existing property.

// index.js
// imports ....

webpackConfig = new WebpackConfig(runtimeConfig);

// The api object
const publicApi = {
   get: (target, prop) => {
      // api exposed funtions
   }
};

const publicApiProxy = new Proxy(publicApi, {

});

Furthermore it will check if the runtime environment has been set, this can be set manualy or it is created by the Encore executable which is the bin folder.

This runtime config contains the command and option values as well as some parameters needed to run the original wepack command like what environment it should pack for and should it start a dev server, HMR etc.

class RuntimeConfig {
    this.command = null;
    this.context = null;
    this.isValidCommand = false;

    this.useDevServer = null;
    this.devServerUrl = null;
    this.devServerHttps = null;
    this.devServerKeepPublicPath = false;
    this.useHotModuleReplacement = null;

    this.babelRcFileExists = null;

    this.helpRequested = false;
    this.verbose = false;
}

Usage

Now that we have a basic understanding of how it wraps itself around Webpack we'll explore the WebpackConfig object defaults and the custom callbacks.

The most basic and minimal settings are the addEntry and setOutputPath, these should already ring a bell. The addEntry takes two string arguments, the first is the name of the generated package, the second is the path to the entry file. The setOutputPath is as you guessed, the directory where generated files should be stored.

// webpack.config.js
const Encore = require('@symfony/webpack-encore');

Encore
  .addEntry('main', './app/Resources/assets/main.js')
  .setOutputPath('web/build/');

module.exports = Encore.getWebpackConfig();
The remaining functions and parameters are mostly for small tweaks to the config. The params enableVersioning, enableSourceMaps and autoProvideJquery just take booleans to enable/disable the default config provided by the plugin. Almost all the other config methods will do the same with the exception that you can pass a callback function. These functions should return our own config object.

Conclusion

Webpack Encore is a great package to get started quickly, providing a more readable layer over the complex webpack configuration. A downsite to this approach is that a lot of config is pre-set and without any basic Webpack knowledge it might be hard to finetune it to your projects needs.