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.
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.
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.
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.
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.
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.
BundleNext, 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.
Another reason to look at the entire line of code is to support 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.
CacheOnce 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.
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!
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