CommonJs modules aren't tree-shaked by default.
Describe the bug
"commonJs" dependencies aren't tree-shakeable with CRA out of the box.
Root cause:
The default settings of TerserPlugin in Webpack library includes "passes: 2" option. The entire Terser config looks like this:
new TerserPlugin({
terserOptions: {
compress: {
passes: 2
}
}
})
It was added in scope of "commonJs" tree-shaking implementation: . As a result, any "commonJs" dependencies (which imported from "node_modules") are tree-shaked successfully using default settings of Webpack OOTB.
However, the Webpack config in Create React App doesn't include "passes" option (i.e. it's implicitly equal to 1). As a result, "commonJs" modules are not tree-shaked when code is built using CRA.
Environment
Version of CRA: create-react-app: 5.0.1 System: OS: Windows 10 10.0.19045 CPU: (8) x64 Intel(R) Core(TM) i7-10610U CPU @ 1.80GHz Binaries: Node: 18.13.0 - C:\Program Files\nodejs\node.EXE Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD npm: 8.19.3 - C:\Program Files\nodejs\npm.CMD
Steps to reproduce
Prerequisites:
npx create-react-app treeshaking-issue-demo
Steps:
- Install some deps.
npm i -D source-map-explorer
npm i -D @epam/uui-core@4.10.1-beta.1
- Go to "src/index.js" and add next couple of lines:
import { useForceUpdate } from '@epam/uui-core';
console.log(useForceUpdate);
- Build with default settings of CRA. And check bundle content.
npm run build
npx source-map-explorer 'build/**/*.js'
- Build with "passes: 2"
Go directly to node_modules, edit next file: node_modules/react-scripts/config/webpack.config.js - add
"passes": 2
option to the "terserOptions.compress". So that it looks like:
new TerserPlugin({
terserOptions: {
parse: {
// We want terser to parse ecma 8 code. However, we don't want it
// to apply any minification steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
passes: 2, /// <----- HERE
Build and check bundle content
npm run build
npx source-map-explorer 'build/**/*.js'
- Compare bundles from step 3 and step 4 If you compare bundle from step 3 and bundle from step 4, you could notice that only small part of "@epam/uui-core" library was actually added to the bundle if "passes: 2" option is specified.
Expected behavior
I would expect that default settings of CRA support tree shaking of "CommonJs" modules out of the box. More specifically: it makes sense to add "passes: 2" option mentioned above to the default Webpack config of CRA.
Actual behavior
"CommonJs" modules aren't tree shaked out of the box, and I have to use some tools (like CRACO https://craco.js.org/) in order to override default settings and add "passes": 2
option to the Terser plugin options.