Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

Tree Shaking ES2015 modules

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Thu Jan 19 2017

In this post I will show that putting entities in the same file does not impact Tree Shaking.

My personal preference is to avoid putting more than one class or “thing” in the same JS file. I find that over time the shared file just grows into something unmanageable that forces a refactor down the road.

However, does sharing a file between multiple functions or classes impact Tree Shaking?

One might worry that a bundler like Rollup will include the contents of the entire file if any portion of it is used by the application.

Luckily this is not the case. It doesn't matter how many statements there are in a single file. Only referenced statements will be included in the final bundle.

Here are a few examples of this.

I have created my own version of the dreaded “utilities.js” file. Utilities.js is of course the most nondescript filename possible, and a famous dumping ground for JavaScript.

utilities.js
export class Logger { logMessage(msg) { console.log(msg); } debugMessage(msg) { console.debug(msg); } } export class NameHelper { getFullName(firstName, lastName) { return `${firstName} ${lastName}`; } }

In utilities.js we see two classes; Logger and NameHelper.

My app (main.js) happens to only need NameHelper, so I don't want Logger to be included in my final bundle.

Luckily the Tree Shaker (Rollup) is smart enough to handle this for me.

Below are three examples of ways to import NameHelper and still get the desired effect.

Option 1

In the first option I am calling out the NameHelper specifically in the import statement

main.js
import { NameHelper } from './utilities'; let nameHelper = new NameHelper(); let fullName = nameHelper.getFullName('Joe', 'Smith'); console.log(fullName);

Even though there are multiple classes hiding behind './utilities', only NameHelper is included in my bundle)

Option 2

In my next example I have listed the Logger in the import statement by mistake. Will that have any bearing on the result?

main.js
import { NameHelper, Logger } from './utilities'; let nameHelper = new NameHelper(); let fullName = nameHelper.getFullName('Joe', 'Smith'); console.log(fullName);

Luckily no. As long as you're not using the imported Logger symbol beyond the import statement you are fine.

Option 3

In my last example I have used a * in the import statement with an alias. Will this confuse the bundler?

main.js
import * as utilities from './utilities'; let nameHelper = new utilities.NameHelper(); let fullName = nameHelper.getFullName('Joe', 'Smith'); console.log(fullName);

Nope, we are still good. The only difference is that we have to account for the alias when referencing the NameHelper.

Final Bundle

In all three cases we end up with the same exact bundle.

(function () { 'use strict'; class NameHelper { getFullName(firstName, lastName) { return `${firstName} ${lastName}`; } } let nameHelper = new NameHelper(); let fullName = nameHelper.getFullName('Joe', 'Smith'); console.log(fullName); }());

No trace of the Logger in the bundle.

I have put the code up on Github in case you want to take a look.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter