New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid unnecessary facade dependency inlining #3552
Conversation
test/chunking-form/samples/entry-point-without-own-code/_expected/amd/m1.js
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments:
So far as I can see, there are exactly two issues solved:
- If a namespace object is included for an entry point that is imported somewhere else, a facade is created. This skips having an empty import to a chunk that just contains a namespace object.
- If an entry point has more than one user-defined facade but the entry point itself just contains reexports, the empty import is removed.
I think those issues may be solved more elegantly and with less effort, but I am not sure yet. As for why I think this is about it and why there are probably not other improvements: Chunk dependencies are already very much pruned a-priori based on actual side-effects and configured moduleSideEffects
. The rather elaborate logic for this is located here: https://github.com/rollup/rollup/blob/master/src/Module.ts#L341-L388
Most notably, I do not expect ever for an unused external import to remain in a chunk if it has no side-effects. And if there are just imports without a binding from a module, a chunk will not get a dependency on that module, which in the end means that a chunk will never depend on a side-effect-free chunk unless a binding is imported. The only exception here are facade chunks, but for me it makes much more sense then to move this logic here https://github.com/rollup/rollup/blob/master/src/Chunk.ts#L119-L133
but maybe I am wrong. So the logic could be: If a module only exports stuff from other chunks than the facaded chunk and no module in the facaded chunk has a side-effect, then instead of adding this chunk as a dependency to the facade chunk, we should copy the dependency list of the facaded chunk to the facade chunk.
Also there was no reason to undeprecate pureExternalModules
as this is completely superseded by the much more powerful moduleSideEffects
. The moved tests are useful, but instead they should be copied and be modified to use moduleSideEffects
instead. For some reason, I mostly created function
tests for this feature, some more form tests would help definitely Also, some of your test results are wrong, at least for the array test, other
MUST be included as an empty import.
I see that the basis use case of chunk pruning is actually solved looking at eg: In this case, let me ask your suggestion how you would solve the original test case then? |
One benefit to this PR approach though might be that by handling this sort of "wiring up" only at the end of the optimization process, it may enable more optimizations to happen in the middle that eg change the wiring. |
Also, I think this PR might solve some preserveModules bugs where bindings wouldn't have otherwise been available due to the lack of inlineChunkModules expanding dependencies to include them as I did see some cases where not running that full expansion made the render phase fail. |
Thank you for your contribution! ❤️You can try out this pull request locally by installing Rollup via
or load it into the REPL: |
Codecov Report
@@ Coverage Diff @@
## master #3552 +/- ##
=======================================
Coverage 96.29% 96.29%
=======================================
Files 176 176
Lines 6043 6048 +5
Branches 1781 1783 +2
=======================================
+ Hits 5819 5824 +5
Misses 112 112
Partials 112 112
Continue to review full report at Codecov.
|
I pushed an alternative solution to your branch that solves all your issues in mostly six lines of code + some refactoring by fixing what was actually broken, namely
So what I did is I reused the logic that determines the actually necessary dependencies after tree-shaking when side-effects are taken into account (or rather the result of the first run as it is cached). The only dependencies we need to have are then the relevant dependencies of the original module. If one of them ends up being in the chunk of the original module, then of course we have a dependency on that chunk. This would include also all side-effects only dependencies of the original module. The only dependency that is not covered if the original module itself has a side-effect, which is why in this case, we always add a dependency on the original chunk.
I do not think there is any more optimization potential on module level here, at least to my understanding the current algorithm is pretty much maxed out since #3354 except for the fact that too many facades were created. But I think this is solved now as well. The next quantum leap would be statement level chunking, and I have some vague ideas building on the current algorithm, but so far this is still mostly future talk as the next big things I want to focus on is moving the chunking to the generate phase and fixing the hashing for good. |
098e325
to
010518c
Compare
010518c
to
d0312ff
Compare
If you have a failing test case, please post it. I would be rather surprised by this as facades are not used for preserve modules, but we had some default export issues here which should be solved in recent Rollup. |
Thanks @lukastaegert for offering the suggested approach here, this looks great and is a huge help to have a solution to this problem. Yes I do see that side effects fully affect the chunking algorithm, which I hadn't realised you had implemented already. I must admit I don't see where in #3354 any side effect or binding checks are being done though, but did follow that refactoring and the entry nesting concept is great. I wonder if it is possible to define entry nesting hierarchies at the entry configuration level? Sounds like it would be similar to the manual chunks work, but with less "manual" work!
|
GitHub won't let me approve my own PR, but this has my 👍 |
description: 'alias module dependency inlining', | ||
options: { | ||
input: { | ||
'main1-alias.js': 'main1.js', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering if you have any ideas how we could make the aliases be the later entry points in order, rather than the first entry points in order as works here?
It's not a big thing, but would be nice to just lock this behaviour down either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a small change so that both the order in the bundle object and the order of facade creation respects the order configured by the user. I.e. the first chunk for a given entry becomes the actual chunk and later ones become facades, and the order between those is preserved.
Previously, later entries would "overtake" earlier entries because loading the later ones would just return the module instance of the earlier modules, making them "load faster" and receive their name earlier.
I just tried out disabling the inlining and all works well actually - seems it must have been due to an unrelated code path. |
0ea592c
to
1c97de9
Compare
Which is because I messed up here, it is actually that much more aptly named #3369. I sounds that it is just about avoiding imports, but it actually builds an optimized graph based on tree-shaking and side-effect information. I think this example sums it up really well. |
Ah I see, I hadn't realised you'd done all that already! Yes that was exactly what this PR was attempting with binding and side effect checks. Thanks for working these through @lukastaegert. |
This PR contains:
Are tests included?
Breaking Changes?
List any relevant issue numbers:
Description
This removes the unnecessary dependency inlining that is applying for cases of facade modules referencing other facade modules, thanks to @lukastaegert :)