Skip to content

Commit

Permalink
Prevent empty chunks and thoroughly improve experimentalMinChunkSize (#…
Browse files Browse the repository at this point in the history
…4989)

* Tests

* Automatically merge empty chunks if possible

* Improve performance with BigInt

* Rename variable

* Track atoms over side effects

* Ignore reexports when finding facades

* Use size increase to determine best merge target

* Move atomsByEntry to a more convenient place

* Respect side effects of external dependencies

* Set default of experimentalMinChunkSize to 1 and fix calculations

* Improve size calculation

* Abort search if perfect match is found

* Improve coverage
  • Loading branch information
lukastaegert committed May 17, 2023
1 parent 5b29267 commit 9f05b91
Show file tree
Hide file tree
Showing 364 changed files with 1,476 additions and 869 deletions.
8 changes: 4 additions & 4 deletions docs/configuration-options/index.md
Expand Up @@ -2339,13 +2339,13 @@ This option will only log top-level statements, though. Sometimes, e.g. in case
| -------: | :---------------------------------- |
| Type: | `number` |
| CLI: | `--experimentalMinChunkSize <size>` |
| Default: | `0` |
| Default: | `1` |
Set a minimal chunk size target in Byte for code-splitting setups. When this value is greater than `0`, Rollup will try to merge any chunk that does not have side effects when executed, i.e. any chunk that only contains function definitions etc., and is below this size limit into another chunk that is likely to be loaded under similar conditions.
Set a minimal chunk size target in Byte for code-splitting setups. When this value is set to the default of `1`, Rollup will try to merge chunks that do not contain code except imports and reexports into other chunks. A merge will only be performed if it does not change what side effects are executed when any entry is loaded. For the value of `1`, only merges are permitted that do no increase the amount of code loaded for any entry.
This will mean that the generated bundle will possibly load code that is not required yet in order to reduce the number of chunks. The condition for the merged chunks to be side effect free ensures that this does not change behaviour.
Larger values will try to merge any chunk below the limit into other chunks. In that case, it is accepted that entries may load some unnecessary code. The algorithm always tries to merge in a way that minimizes the amount of unnecessary code, though.
Unfortunately, due to the way chunking works, chunk size is measured before any chunk rendering plugins like minifiers ran, which means you should use a high enough limit to take this into account.
Unfortunately, due to the way chunking works, chunk size is measured before any chunk rendering plugins like minifiers ran, which means you should use a high enough limit to take this into account. When calculating the size, it will take tree-shaking of top-level statements into account, though.
### perf
Expand Down
14 changes: 12 additions & 2 deletions src/Chunk.ts
Expand Up @@ -302,14 +302,21 @@ export default class Chunk {

canModuleBeFacade(module: Module, exposedVariables: ReadonlySet<Variable>): boolean {
const moduleExportNamesByVariable = module.getExportNamesByVariable();
// All exports of this chunk need to be exposed by the candidate module
for (const exposedVariable of this.exports) {
if (!moduleExportNamesByVariable.has(exposedVariable)) {
return false;
}
}
// Additionally, we need to expose namespaces of dynamic entries that are not the facade module and exports from other entry modules
for (const exposedVariable of exposedVariables) {
if (
!(moduleExportNamesByVariable.has(exposedVariable) || exposedVariable.module === module)
!(
exposedVariable.module === module ||
moduleExportNamesByVariable.has(exposedVariable) ||
(exposedVariable instanceof SyntheticNamedExportVariable &&
moduleExportNamesByVariable.has(exposedVariable.getBaseVariable()))
)
) {
return false;
}
Expand Down Expand Up @@ -382,7 +389,10 @@ export default class Chunk {
for (const module of entryModules) {
if (module.preserveSignature) {
for (const exportedVariable of module.getExportNamesByVariable().keys()) {
exposedVariables.add(exportedVariable);
// We need to expose all entry exports from this chunk
if (this.chunkByModule.get(exportedVariable.module as Module) === this) {
exposedVariables.add(exportedVariable);
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/Module.ts
Expand Up @@ -401,6 +401,17 @@ export default class Module {
return error(properties);
}

// sum up the length of all ast nodes that are included
estimateSize(): number {
let size = 0;
for (const node of this.ast!.body) {
if (node.included) {
size += node.end - node.start;
}
}
return size;
}

getAllExportNames(): Set<string> {
if (this.allExportNames) {
return this.allExportNames;
Expand Down

0 comments on commit 9f05b91

Please sign in to comment.