-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Description
Version
Test Case
https://github.com/billyjanitsch/react-router-tree-shaking
Steps to reproduce
Clone the repo above, run npm run build, then observe the output in dist/main.js.
Expected Behavior
The biggest gains to be had from tree-shaking in react-router/history are probably removing whichever of browser/hash/memory histories are not used by the app. For example, if only BrowserRouter is imported from react-router-dom, one would expect the hash and memory routers, along with their associated histories, to be tree-shaken.
Actual Behavior
Neither of the unused histories are tree-shaken. You can verify this by searching for hashType in the build output, which only appears in createHashHistory and therefore should have been tree-shaken from the bundle. As a result, the output bundle is quite large: importing only BrowserRouter yields a 33.2 kB bundle, whereas importing the entire library yields a barely-larger 37.9 kB bundle.
I believe this happens because webpack is generally not good at tree-shaking unused parts of a module -- it's much better at tree-shaking entire unused modules (particularly when sideEffects: false has been set). For example, if createHashHistory lived in a separate module, webpack would know that the module import was unused and prune the entire module.
What was the motivation for bundling history, react-router, and react-router-dom into single files on npm? I understand why it's more efficient to do this for CJS modules, but for ESM, it seems strictly better to leave the bundling to the bundler, since modern bundlers are capable of inlining ESM without adding glue code, with the advantage that they can be smarter about pruning entire modules.
(Another theory is that this re-export is the problem.)