This POC explore 2 scenarios:
Go to the app
project and install the dependencies:
cd app
pnpm install
Build the app:
pnpm build
Go to the app project dist
folder and open the main.js
file.
The POC include package-1
, package-2
, package-3
and a random CJS module called to-array
to prove that ESM and CJS packages can be bundled together. Those 3 packages are imported into a non "module" application called app
and bundled with Webpack 5.
This project contains 2 functions, add
and subtract
splitted in 2 distinct files.
The project index.js
file export everything from add.js
and substract.js
.
export * from "./add.js";
export * from "./subtract.js";
This project contains 2 functions, multiple
and divide
. All the code is in a single index.js
file.
export function multiple(x, y) {
return x * y;
}
export function divide(x, y) {
return x / y;
}
This project contains 2 functions, areEqual
and areNotEqual
splitted in 2 distinct files. The only difference with this project is that it's imported via a packed .tgz
file.
This project is a single index.js
file importing and using the package-1
, package-2
, package-3
and to-array
projects.
In the Getting Started section we executed the build
command to generate a main.js
in the dist
folder.
Since the app
project index.js
only import respectively the subtract
, divide
and areNotEqual
functions from package-1
, package-2
and package-3
we expect that Webpack ESM treeshaking will not bundled the add
, multiply
and areEqual
functions.
By looking in the main.js
file, we can see that the multiply
function is added, however the add
and areEqual
functions are not.
The conclusion here is that Webpack ESM treeshaking do work as expected but the modules must be in distinct files.
Since the to-array
function is also added to the main.js
file, we can conclude that Webpack can handle ESM and CJS packages in the same app.
If you have doubts about the validity of the resulting file, you can execute it with the following command:
node dist/main.js
Go to the package-4
project, install the dependencies and build the library:
cd package-4
pnpm install
pnpm build
Then, go to the package-5
project, install the dependencies and build the library:
cd package-5
pnpm install
pnpm build
Then, go to the app-ts-react
application and install the dependencies:
cd app-ts-react
pnpm install
Build the app:
pnpm build
Go to the app project dist
folder and open the main.js
file.
The POC include package-4
and package-5
packages. Those 2 packages are imported into a non "module" application called app-ts-react
and bundled with Webpack 5.
This project contains 2 TypeScript functions, isUndefined
and isNotUndefined
splitted in 2 distinct files.
Both function are exported from an index.ts
file.
The files are transpiled to JavaScript and their types declaration are available in collocated .d.ts
files.
This project contains 2 React components written in TS, Page1
and Page2
splitted in 2 distinct files.
Both components are exported from an index.ts
file.
The files are transpiled to JavaScript and their types declaration are available in collocated .d.ts
files.
This project is a single index.ts file importing and using the package-4
and package-5
projects. The app renders a single Page2
React components.
At first, package-4
behave similarly to what has been observed previously with package-1
and package-3
in Understanding the content of the main.js file but package-5
did not.
Even if app-ts-react
index file only imported Page2
, the Page1
component was still being added to the bundle.
After reading Webpack tree-shaking documentation, turns out that to be tree-shaken, the library project package.json
file must define a sideEffects
field with the false
value or an array of files having side effects.
Files with side effects will be excluded from the tree-shaking. If the field is omitted, React code is not tree-shaken.
After adding the sideEffects: false
field, only the Page2
component is added to the bundle.
Tree-shaking ESM code with Webpack 5 mostly work as expected if the code is properly splitted in distinct files and the sideEffects
field is added to the libraries package.json
file.
There are 2 layers of tree shaking with Webpack:
- The first one is with the
sideEffects
field and depends on the code being properly splitted in distinct files. This layer cannot eliminate in-file unused code. - The second one depends on the Terser plugin and can eliminate in-file unused code. It won't eliminate unused imports thought.
According to this article, the most efficient way to tree shake would be in 3 steps:
- Execute Terser on the code to eliminate in-file dead code
- Execute webpack on the code to tree shake the modules/imports
- Execute Terser in the code to minize the code
The Terser plugin won't be executed when in development but the sideEffects
optimization will be applied. Therefore, it's super important to correctly use the sideEffects
field and that the code is splitted properly in distinct files.
More information is available here.