Document why tree shaking is not performed on async chunks
Feature to document
Tree shaking aka dead code elimination
Details
According to this comment, tree shaking is not supported for async chunks, but there's no mention of this in the tree shaking guide.
Additional information
I've read this comment from @sokra but it's quite terse and since the OP deleted his repo, it's not clear to me: does webpack always disable tree shaking for async chunks, or only in case when there are 2+ async chunks which share the same module, say foo.js? Are there any other gotchas to know about?
I can send a patch but I need to understand this better.
According to this comment, tree shaking is not supported for async chunks
One can't say this in general.
Here are two cases were tree-shaking may not work as expected from user perspective, but there are reasons why it works this way:
import()
import("./a") disables tree-shaking for (and only for) exports of module ./a. Why? Because the returned promise resolves to the namespace object for module a from which all exports can be accessed. webpack doesn't know which export is used and has to include all of them.
Note that tree-shaking is only disabled for exports of module a and will still work for module referenced from module a. When module references only a single exports of module b this will work as expected.
modules in multiple async chunks with difference exports used.
Assuming a module X is used from module A and B. Module A only uses export D and module B only used export E. In this case module X is generated with both export D and E. This is true even if module A is only in chunk Y and module B is only in chunk Z. In this case chunk Y and chunk Z may contain the source code of module X. One might think chunk Y could contain module X with only export D and chunk Z could contain module X with only export E, but this is not correct in most cases. The reason for this is that webpack can't assume that only chunk Y OR chunk Z is loaded. It has to assume that is can happen that chunk Y AND chunk Z are loaded at the same time. In this case there has to be only a single instance of module X and module X has to be only evaluated/executed as single time. If chunk Y and Z would contain different source code of module X we can't enter a state where module X is only executed as single time and contains both exports. That's why all chunks has to include the same source code for module X.
Note that here one can't say that tree-shaking is disabled. It's more like all used exports of the whole application are merged. So in our scenario module X could contain another export F which would be dropped as unused.
Makes sense, thanks for explanations! 👍
So, in your example above, if module B doesn't exist and module A looks like this:
module-A.js
import('./module-X').then(({ exportD }) => ...);
If module-X is only being used by module-A, could a feature be added to webpack to statically analyse this and remove exportE?
How about this pattern:
// page-1.js define everything in same file
export const route = {
path: '/page-1',
...other
};
export function Component(){
return h('div')
}
// route.js
import { route } from './page-1.js'; // tree shake, route only
const router = [
{
...route,
// tree shake, Component only
component: import('./page-1.js').then(({ Component }) => Component);
}
]
or top-level-await:
const { Component } = await import('./page-1.js')
can a comment annotation be supported? like:
import(/* webpackOnlyImport: A0 */'./A').then(({ A0 }) => ...);
Yes that was added in the meantime. It's called /* webpackExports: "AO" */
Is there any workaround to get this working in webpack 4?