Manual chunks support - #2084
Conversation
|
Also note that the name of the manual chunk is the "chunk alias". So that setting |
eeb26a6 to
7f7b717
Compare
8b8429c to
67521f1
Compare
edd4931 to
2712e30
Compare
67521f1 to
aa5c557
Compare
aa5c557 to
cec83e3
Compare
|
Rebased and ready for review. |
lukastaegert
left a comment
There was a problem hiding this comment.
Just finished a first review. Definitely a very useful feature. Two things I noticed:
- Currently, entry points and manual chunks cannot match (see my comment). Maybe this should be changed?
- I find the new logic to be a little hard to read, also with the (ab)use of chunk aliases as flags. Maybe it is possible to extract some of this logic, especially from
buildChunks, into separate functions?
|
|
||
| export interface InputOptions { | ||
| input: string | string[] | { [entryAlias: string]: string }; | ||
| manualChunks: string[] | { [chunkAlias: string]: string }; |
There was a problem hiding this comment.
This looks like manualChunks could be an array as well. However when specifying something like manualChunks: ['file.js'] via config file, I get a type error. This should either be fixed (and tested) or removed.
There was a problem hiding this comment.
Good catch, will remove this. Yes manual chunks must be named (and it would be string[][] anyway if we did permit an array form).
There was a problem hiding this comment.
(Correct the object case to be string[] too!)
| let entryAndManualChunkIds = entryModuleIds.concat([]); | ||
| if (manualChunks) { | ||
| Object.keys(manualChunks).forEach(name => { | ||
| const chunk = manualChunks[name]; |
There was a problem hiding this comment.
chunk should probably be something better describing its content like ids or chunkIds.
| entryModules, | ||
| !preserveModules | ||
| !preserveModules, | ||
| manualChunkModules |
There was a problem hiding this comment.
With the recent additions, the complex buildChunks has grown a lot more complex with lots of local variables with similar names and unclear scopes floating around. manualChunkModules could be avoided by extracting its generation into e.g. a helper function getManualChunkModules(manualChunks, entryAndManualChunkIds, entryAndChunkModules).
| manualChunkModules | ||
| ); | ||
|
|
||
| if (entryModuleAliases) { |
There was a problem hiding this comment.
One important use case I see for this feature is to inline all modules imported by the "first" entry point in a base chunk so that initially, no secondary chunks need to be loaded. To specify this, one would probably write something like
{
input: ['main1', 'main2'],
manualChunks:{
initial: ['main1']
},
...
}Currently, this will throw a "duplicate entry points" error since analyseExecution will already assign an alias to this chunk which is considered to be the relevant flag for this error. One solution would be to not list main1 in the input list but then the initial chunk can have exports that are not part of the main1 interface as the chunk is considered internal.
Ideally, I would assume we check for duplicate entry points only within input and manualChunks separately but allow a manual chunk to be the same as an input entry point. In case those match up and no additional facade is necessary, I would further expect the resulting chunk to receive its name via input (otherwise, the non-facade chunk should probably get the manual chunk name).
As this will probably make the existing logic even more complex, maybe there is a way to extract more of this chunking logic out of buildChunks into separate helpers.
There was a problem hiding this comment.
You've brought up an interesting case, and this is really good to poke holes in the model. The difficulty with supporting this case is there are a few questions in relating it to the aliasing model:
- Should the chunk alias for
main1be calledmain1orinitial? - Should the chunk name for
mainuse thechunkNamesrule or theentryNamesrule? Since it is defined as both a manual chunk and an entry, we don't currently have a way to deal with this.
Note from a chunking point of view (naming aside), this scenario is equivalent to just having:
{
input: ['main2'],
manualChunks:{
initial: ['main1']
},
...
}One alternative might be to have the manual chunk called initial get created, and then have the input become simply a reexporting facade module pointing to the manual chunk? That might be a way around the naming model issues here.
There was a problem hiding this comment.
In fact the above would provide a general solution to the duplicate entry error, allowing us to have a way of always dealing with this case, removing the error entirely.
There was a problem hiding this comment.
To summarize the approach taken in the latest commit:
Should the chunk alias for main1 be called main1 or initial?
The manual chunk is created, and then seen to be an entry module facade - therefore it takes the place of main1, and gets called main1. If the chunk had had other modules in it then a new reexporting facade would have been used to the chunk.
Should the chunk name for main use the chunkNames rule or the entryNames rule? Since it is defined as both a manual chunk and an entry, we don't currently have a way to deal with this.
When a chunk corresponds to an entry point facacde as in this case, the manual chunk name does not apply at all, with the entry naming scheme precedence.
There was a problem hiding this comment.
This will still require two separate downloads but I guess this is certainly the cleanest approach 👍
…entry points, fix interfaces
|
I know the abuse of chunk aliases as flags is bad for readability, but that's why I carefully commented and I just hate adding a new property to Module when it can be avoided... memory footprint matters on objects like Module that are instantiated a lot... I know it's not much of a difference but I still prefer not to add a new property when possible. If you think it would be clearer I can add a redundant boolean flag though. I've added changes with the feedback and to remove the duplicate entry point error in the chunk scenario, replacing this with facade creation, while retaining the duplicate entry point error for actual duplicate entry points. |
lukastaegert
left a comment
There was a problem hiding this comment.
Looks really good now! I also very much like the split of buildChunks 👍
Will merge this now.
|
This looks great! Is there a complete worked example on how to use this for generating a vendor.js? |
|
Unfortunately not yet to my knowledge. This is a config that should be working (also using the new object form for input): rollup.rollup({
input: {
index: './main.js'
},
experimentalCodeSplitting: true,
manualChunks: {
'vendor': ['react', 'some-other-big-dependency']
},
output: {
dir: 'dist',
format: 'es'
}
})Basically you list all dependencies you want in your vendor bundle in a comma separated list. Note that order matters here i.e. if one of your dependencies creates a global that another dependency relies on, it should go first in the manual chunk. I.e., manual chunks will also slightly change the execution order. |
|
I tried using this new option here I also It fails with: |
This provides support for a new
manualChunksinput option, with the work based off of the chunk-naming branch (so that should land first, and I will ensure this is rebased to follow changes there prior to landing).The usage is:
babelabove will include all dependencies of Babel.page1-dep.jshappened to use a lodash module that is shared with Babel, thenpage1-depwould delegate to the common chunk.common-d8f87s9d.jsandpage1-common-s98ff7s9.jsfiles. The filenames of manual chunks can be customized just like other chunks through thechunkNamesoption as in the chunk naming PR.I think this might actually be a pretty good default... but let's experiment with some real world usage and see how it goes.
Since this is all under
--experimentalCodeSplittingwe can always change the approach if we find limitations in the wild.Quite excited for this one :)