Home
Keep Your Bundle Size Under Control
Original post Aug 2nd 2017 -Updated Jan 27th 2021

Our industry has been buzzing a lot in the last year about the bloat of modern web applications and websites. Some have even resorted to public shaming of websites to raise awareness of this issue.

While a desktop computer with a high-speed internet connection in a first world country can chew up almost any website easily, this statement falls short once you change one of its variables. Mobile devices and unreliable internet connections are much more sensitive to your application's size, whether it’s the network throughput, the time it takes to parse the code, the CPU labor, the memory footprint and the battery.

We all know this fact, but few of us keep it in mind when we develop our awesome, state-of-the-art applications on our state-of-the-art machines running the state-of-the-art browser.

Understanding the Cost Early

Since this is a hot topic, many people already tried to tackle it with excellent tools such as webpack-bundle-analyzer, cost-of-modules, command line webpack warnings and techniques like code splitting and lazy loading.

While those are great tools, they are easily overlooked if you are not conscious enough. I wanted to create a tool that will smack you right in the face and immediately let you know if you imported a hefty package that will hurt your users.

Enter Import Cost

Import-Cost is a Visual Studio Code extension (for other IDEs read on) that shows you the size of an imported 3rd party library the moment you import it.

This extension is not intended as a bundle analysis tool — there are better tools for that, some of which I stated above. I believe this extension will help you find obvious pain points and prevent shipping massive bundles to your customers.

Instant Feedback
Instant Feedback

I received numerous questions about this extension's details and how it works behind the scenes. I hope to answer some of these questions in the following segment.

How It Works
AST

Import-Cost listens to changes in the text of the active editor window. Whenever it detects a change (debounced, of course), it will analyze the code of the current window using the Babel AST parser and compile a list of valid import or require candidates. A "valid candidate" is a 3rd party, locally installed library. For example, the following code will compile a list of three candidates (react, react-dom and lodash/uniqueId) and ignore the rest.

Valid Candidates
Valid Candidates

I intentionally decided to only calculate 3rd party libraries since they have a clear boundary. They are at the root of all the bloated bundles that we ship to our users.

Bundle

Next, the extension takes the entire line of code of the import/require candidate (more on this later) and puts it in a temporary file. The extension then runs webpack configured with the temporary file as the entrypoint as well as the built-in terser minifier. Webpack will then pull all the necessary dependencies of the library and bundle them all together.

The entire line of code of the import/require candidate is taken into consideration in order to leverage webpack’s tree shaking mechanism. The bundle's final size is affected by what you actually import and not the size of the whole library itself.

Tree-shake everything except "func"
Tree-shake everything except "func"
func1 is a doozy!
func1 is a doozy!

Another reason to look at the entire line of code is to support submodule imports:

Submodule Imports
Submodule Imports

In addition to placing the entire import line into the entrypoint file, we also print the imported object using console.log to stop webpack from tree shaking our import.

The extension runs multiple webpack instances using the worker-farm library, which runs parallel child processes to parallelize the calculations of the different libraries.

Cache

Once the bundlings are done, we save the results in a file-based cache. The package sizes are saved to the cache while taking into account the version of the libraries. The reason behind that is that the same piece of code might have a different weight depending on the imported library version.

Lodash version 3.10.1
Lodash version 3.10.1
Lodash version 4.17.4
Lodash version 4.17.4
Decorations

All that is left to do is to decorate the editor in the right place. Since the package size calculation is an asynchronous task that might take a while, we run into a problem that the user might have switched to a different tab in the meantime, and the returned results are not relevant to the code that is currently displayed. The extension takes this into account and decorates the code based on the active window. If the user navigates to a previously calculated code, the extension will read from an in-memory cache and add the correct decorations in the proper position.

That's it!


FAQ

Does the plugin support tree shaking?

Yes, the extension utilizes webpack’s tree shaking mechanism.

Does the plugin support React/Vue/Svelte/Angular?

Yes.

Does the plugin support Typescript?

Yes.

Do you display the minified size? Gzipped size?

You can configure the extension to display the minified size, the gzipped size, or both. The default is both.

What about common dependencies between packages?

Common dependencies (package A and package B both use package C) will be counted twice, both for package A and B. As I stated earlier, this extension is not meant to be treated as an analysis tool. Instead, it should be treated as a "scare-you-straight" tool.

Is there an Import-Cost extension for other IDEs?

I developed the extension initially for VSCode, but I split the calculated logic to a different package to support the development of a similar extension for other IDEs.

Since then, multiple extension were developed:


I'd love to hear what you think.

Yair Haimovitch@yairhaimo
If you made it to here, here are some in-depth tutorials that might interest you: