webpack: webpack@4 Tree Shaking doesn't eliminate code between multiple entry points in same configuration object.
Do you want to request a feature or report a bug?
Bug.
What is the current behavior?
By way of example, I have:
util.js
: ES exports two methods:red
andblue
via two scenarios:one-file
(all code within one file) andre-export
(methods re-exported from external files)app1.js
: Imports and uses onlyred
app2.js
: Imports and uses onlyblue
Expected: If I build webpack bundles for both apps, app1
will have red
and blue
will be eliminated. Conversely, app2
will have blue
and red
will be eliminated:
Actual: Depends on config
- Array of Configs: If there is an array of configurations with a single entry point each for
app1.js
andapp2.js
the correct unused methods are dropped. - Multiple Entry Points: If there is a single configuration object with multiple entry points for both
app1.js
andapp2.js
, then neitherblue
norred
are eliminated in either bundle. I would consider this a bug. Perhaps there is some known thing for “multiple entry points get any esnext exports used by any other entry point”, but that seems weird.
If the current behavior is a bug, please provide the steps to reproduce.
We made a repository with passing and failing mocha tests to show the situation: https://github.com/FormidableLabs/webpack-tree-shaking-multiple-entry-points.
Reproduction:
$ git clone https://github.com/FormidableLabs/webpack-tree-shaking-multiple-entry-points.git
$ cd webpack-tree-shaking-multiple-entry-points
$ yarn install
$ yarn run build
$ yarn run test
The test output is:
tree shaking in webpack4
array
one-file
✓ app1 should have red, not blue
✓ app2 should have blue, not red
re-export
✓ app1 should have red, not blue
✓ app2 should have blue, not red
multiple-entries
one-file
1) app1 should have red, not blue
2) app2 should have blue, not red
re-export
3) app1 should have red, not blue
4) app2 should have blue, not red
4 passing (22ms)
4 failing
The failing ones show that the ES methods that should be dropped aren’t.
What is the expected behavior?
See above section on current behavior
.
If this is a feature request, what is motivation or use case for changing the behavior?
Nope, it’s a bug.
Please mention other relevant information such as the browser version, Node.js version, webpack version and Operating System.
$ node --version
v8.4.0
$ $ yarn list webpack
yarn list v1.3.2
└─ webpack@4.0.0-beta.1
$ uname -a
Darwin small.lan 16.7.0 Darwin Kernel Version 16.7.0: Thu Jan 11 22:59:40 PST 2018; root:xnu-3789.73.8~1/RELEASE_X86_64 x86_64
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 52
- Comments: 52 (13 by maintainers)
Happy issue 2nd year anniversary! Any update?
@vankop – Yay! 🎉
I upgraded from beta 22 to 24 in https://github.com/FormidableLabs/webpack-tree-shaking-multiple-entry-points/tree/chore/webpack5
and finally had the comparison tests pass (for array of configs which always worked vs single configs which never did until now):
Thanks!
Or to restate the bug in the simplest terms:
If
app1
uses only ESM importred
, andapp2
uses only ESM importblue
, with both entry points in the above configuration, both built entry point bundles contain the code of bothred
andblue
.My original error reproduction repository encompassed both “one-file” tree-shaking and “multiple file re-exports”. I just updated to:
and everything still fails tree-shaking for both of those as expected: https://github.com/FormidableLabs/webpack-tree-shaking-multiple-entry-points (see
src/one-file
vs.src/re-export
)Would love a webpack core team member to see if there’s anything that can be done about this very, very longstanding issue. Thanks!
Sorry it’s convoluted – the
scenarios
array is purely for my convenience to make sure that the exports coming from the same file vs. other imported files doesn’t make any difference. (The scenarios confirm there is indeed no difference – we get DCE failures both ways).Single Config Fails DCE
Here’s what doesn’t work and what I’m guessing is the preferred way to declare entry points:
That fails for DCE / tree shaking.
Array Config Works for DCE
This is what works, although I don’t like it:
And if indeed this version de-optimizes and everything then we probably have an even bigger bug as you now have to choose between (1) DCE not working completely and (2) other de-optimizations 😉
We also see this issue (in webpack 4.17.0) and I agree, that most of the non SPA apps should suffer from this issue.
I’m wondering if it should end up at least in webpack 5.0 milestone, if the problem is in webpack core and can not be easily fixed.
Also stumbled on this.
@sokra on #5954 mentions to use multiple compilation passes, but that’s absurd.
For instance, I’m using webpack-manifest-plugin and
optimization.runtimeChunk
and multi compilation messes it all up.On top of that, as mentioned by @loveky in #5954, with multiple compilations, there is also a problem with vendor extraction.
Multi compilation entry points will result in multiple vendor bundles.
It raises a question, what if rules for extraction are dynamic and lead to different vendor bundles (which webpack 4 with
splitChunks
actually has by default)? All of a sudden dead code elimination becomes dead code generation.This seems to be a critical bug to me.
Sadly, the project we are working on is just being started to be rebuilt, we are strictly tied to multi page approach. The frontend is tightly coupled to backend and vice versa. Not to mention that current frontend is legacy AF. We are incrementally working on core updates, but that means that we are tied to multi page application and therefore multiple entry points. 😦
P.S.
It adds up enormously if using lots of modules, especially third party ones. And from what I’ve gathered, TS and DCE have been created in response to the observed pattern of heavily modular/CBD approach and code reusability.
How it is now, it kind of beats the purpose.
P.P.S.
Haha, skimmed through the code to find what could cause this, but damn webpack is deep. 😄 Will give it another shot later on too. This MUST get resolved!
Unless everyone is working with micro-services and have separated EVERYTHING, I feel like this, in reality, affects more people than they know.
For instance, one entry for client-side, one for administration; both depend on the same base set of modules, but not strictly 1:1… I can see how code from administration spills into client-side and vice versa, not nice. That’s just one extra use case.
Should work with
webpack@next
. Feel free to report new issue with reproducible repo.@TrySound but then you would need to support webpack and rollup builds within apps, which would be very unconvenient
plus, it is not only HMR which rollup is missing, there are many webpack goodies like different kinds of imports, like resolve weak etc which allows server side rendering together with code splitting which would not be possible otherwise. for instance - https://www.smooth-code.com/open-source/loadable-components/docs/server-side-rendering/ - which requires webpack plugin to work
I am sure there are much more other webpack features we don’t even consider, so again, using rollup for apps is not an option for me and for many people
The issues also seems to occur when chunk-splitting. Even if there is only one entry in the webpack config.
The generated chunk file contains both
a
andb
This bug still very much exists as of Webpack 5.36.1 when setting
optimization.runtimeChunk
to'single'
.@theKashey pity, Rollup doesn’t have all Webpack features I guess, especially advanced HMR, so I cannot switch
Anyway, without it code splitting is rather useless because usually vendor files are much bigger than app files and really splitting vendor modules would gain the biggest benefits. Wondering whether this feature is on Webpack roadmap?
To add a data point for this:
We have 258 entry points in one of our applications and compiling them using one-config-object-per-entry (rather than one config with the
entry
field resolving to all entry points) reduces our total asset size by 697KB (714030 bytes).( getting ahead of @sokra 's auto-closing bot on this one )
Unfortunately, after upgrading my bug reproduction samples to
webpack@3.5.2
the bug still very much still exists. I’m guessing it’s still a fundamental issue to webpack core.The awkward thing with this issue, is that it’s likely many folks out their aren’t actually auditing their bundles to check that tree shaking is working as expected. (And in all fairness it’s hard to do outside of trivial examples that you can manually inspect like I have here.)
/cc @TheLarkInn (would love some issue love on this one…)