webpack-bundle-analyzer: Webpack bundle analyzer does not accurately represent effects of tree-shaking in webpack 4
Issue description
Webpack 4 enables dead code elimination from ES6 modules which declare themselves as side-effect free via "side-effects": free
in package.json.
I tested this feature on an example project which I published here: https://github.com/mpontus/tree-shaking-example
I attempted to verify that I can use destructuring import from ESM index file without significant increase in bundle size in comparison to importing modules indivudally.
In other words, the following code:
import add from "ramda/es/add";
document.write(add(2, 3));
Should be equavalent to:
import { add } from "ramda/es";
document.write(add(2, 3));
Judging by the bundle size, this assumption is correct:
Asset Size Chunks Chunk Names
bundle.js 1.29 KiB 0 [emitted] main
vs
Asset Size Chunks Chunk Names
bundle.js 3.22 KiB 0 [emitted] main
Small increase in bundle size can be attributed to webpack artifacts, and their portion in the total size will be insignificant in a real project.
I used source-map-explorer to confirm this, and it shows no significant difference:
vs
Webpack bundle analyzer, on the other hand, does not make it obious that any tree shaking has taken place.
vs
The reported stat sizes for ramda module are 2.06 KB and 311.77 KB respectively.
Is it possible to make webpack-bundle-analyzer give better representation of the effects of tree-shaking on the bundle?
Technical info
- Webpack Bundle Analyzer version: 2.11.1
- Webpack version: 4.1.1
- Node.js version: 8.9.4
- npm/yarn version: yarn 1.3.2
- OS: Linux
Debug info
How do you use this module? As CLI utility or as plugin?
As a plugin
If CLI, what command was used? (e.g.
webpack-bundle-analyzer -O path/to/stats.json
)
webpack --mode production
If plugin, what options were provided? (e.g.
new BundleAnalyzerPlugin({ analyzerMode: 'disabled', generateStatsFile: true })
)
none
What other Webpack plugins were used?
UglifyJsWebpackPlugin
It would be nice to also attach webpack stats file.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 25
- Comments: 27 (12 by maintainers)
@samouss do note that the output from
source-map-explorer
will be similar towebpack-bundle-analyzer
when the checkbox “Show content of concatenated modules (inaccurate)” is not toggled.Only when this checkbox is toggled, will WBA potentially show tree-shaken module contents. When that checkbox is left untoggled (which is the default), then the output will be accurate.
This is the difference:
Untoggled
Toggled
Some of the content might’ve been tree-shaken but WBA does not know which of those modules shown there are missing from bundle output. The size of the “index.js + 20 modules (concatenated)” will be accurate in both cases, though.
The thing is actual dead code elimination is done by
UglifyJSPlugin
, not webpack itself. Webpack just packs and annotates source code in the way that UglifyJS will be able to determine unused code and remove it from the resulting bundle.This is the reason you see a lot of actually “excluded” modules under concatenated module - webpack concatenated them all but UglifyJS removed all the unused ones.
As for the absence of
parsed
andgzipped
sizes for contents of concatenated modules: that’s because there is just no way we could parse them there. Those child modules of concatenated module don’t have edges anymore - they’re joined together into one large code chunk! So the best thing we can do is just use the information thatwebpack
gives us about list of concatenated modules and theirstat
(original) sizes.Actually, we calculate approximate
parsed
andgzip
sizes for those child modules under the hood using this formula:approximateChildParsedSize = childStatSize / concatenatedStatSize * concatenatedParsedSize
. But I decided not to show them in the report tooltip because they’re too inaccurate and may cause a lot of confusion.@sokra do you have any thoughts how we can improve it?
Could you post a new issue and we can debug there? This is getting off-topic for this particular issue
@valscion thanks, I did it, just added
bunderAnalizer
to my build config, not to modified dev, seems properoutput
is needed.@valscion Thanks for the clarifications! Is it written somewhere inside the doc? Because from a user perspective it’s confusing. It took me a while to understand that output of the treemap was not completely accurate depending on the options selected. I was mainly using this view to understand which modules were tree-shaken or not.
Look at the resulting bundle code. Try running the minified code through
prettier
for example, and see if the module you wanted removed is indeed removed.There might be other tools for that, but I haven’t had the need to check that. I’m happy that my bundle sizes are smaller and I can see that a large swath of one huge module has been tree-shaken from our application by just seeing the size differences.
Thanks for explaining this out to us, @mpontus. The current behavior is indeed confusing and unoptimal. I’m not sure if there’s much we can do, as @th0r said in his first comment, but at least we’re now on the same page about this issue 😄
Ah right, yeah this is because of the
(concatenated)
part you’re seeing. Seems like the heuristic implemented by @th0r in #158 does not take into account tree-shaking that has happened.Could you try specifying
optimization.concatenateModules
setting in your webpack config asfalse
and then try again? It will hide the internal parts of the concatenated modules, so you will get similar looking graph as you get withsource-map-explorer
.