-
-
Notifications
You must be signed in to change notification settings - Fork 8.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tree shaking completely broken? #2867
Comments
If you were to use this with |
@TheLarkInn yes, I tried both babel-preset-es2015-webpack and babel-preset-es2015-loose-native-modules presets. Unfortunately, the result is the same. |
What version did you jump from where it was working? |
@TheLarkInn I'm using it in a fresh project. Got curious about the production file size after adding some packages and discovered this. I've seen previously issues and topics about the size, latest in create-react-app with no file size reduction. I'll check another versions of webpack, maybe there will be difference. |
This seems to be a problem only in combination with reexports... |
Here is my small investigation. Everything was tested with the same config as above:
Simple react app: import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(
<p>Hey</p>,
document.getElementById('app')
) The production build is 146 kb. Time for tests:
function fA() {
console.log('I am a string')
} We import some of them, but use only one: import React from 'react'
import ReactDOM from 'react-dom'
import { fA, fB, fC, fD, fE, fF, fG } from './func'
fA()
ReactDOM.render(
<p>Hey</p>,
document.getElementById('app')
) The size is still 146 kb as the function is pretty small. If we use all of them - the size is 150 kb. Tree shaking is working.
import React from 'react'
const A = () => (
<div>
<p>Hey, I am a message</p>
</div>
) Lets use one component: import React from 'react'
import ReactDOM from 'react-dom'
import { A, B, C, D, E, F, G } from './comp'
ReactDOM.render(
<div>
<A />
</div>,
document.getElementById('app')
) The size is now 147 kb. With all components - 152 kb. Tree shaking is working.
export { A, B, C, D, E, F, G } from './comp' The same example as above with one component still gives us 147 kb. Tree shaking is working.
export * from './comp' Now the size with one component is 152 kb. Tree shaking is not working (exports with *). Issue is here.
import React from 'react'
import ReactDOM from 'react-dom'
import { A } from './compA'
import { B } from './compB'
ReactDOM.render(
<div>
<A />
<B />
</div>,
document.getElementById('app')
) The size with one component - 147 kb, with both - 149 kb. Now, we take random library and import it in one of the components (lets take B): import React from 'react'
import Select from 'react-select'
const B = () => (
<div>
<p>Hey, I am a message</p>
</div>
) We want to use only one component: import React from 'react'
import ReactDOM from 'react-dom'
import { A } from './compA'
import { B } from './compB'
ReactDOM.render(
<div>
<A />
</div>,
document.getElementById('app')
) Unfortunately, now the size is 185 kb. So, the tree shaking actually removes the component, but some of the imports are included in the build (even if they were not used). Same thing happens with combined / reexported / nested files. Any ideas why this happens with only some of the components and how to solve this? |
I believe I'm having the same issue - tree shaking doesn't seem to be doing much of anything. The project is https://github.com/redmountainmakers/kilntroller-ui. I haven't narrowed it down to a specific bug but here is an example of a dependency I am definitely not using anywhere: (image from the excellent [Webpack Visualizer](https://chrisbateman.github.io/webpack-visualizer/) tool) |
you could try it again with webpack beta 21, as I fixed an issue with reexports ( |
No change in bundle size for the |
@nylen @vladshcherbin have you tried turning on warnings for UglifyJS? it can tell a lot of useful information about skipped cleanup in unused classes and functions. I have a very similar ticket with Typescript, where the generated class definitions considered as code with side effects. |
@sokra @BlackSonic I actually have a very simple example repo here with webpack and rollup configs. The index file imports I'll give it a try with the new versions and warnings on and will post the result. |
Created an issue in UglifyJS, i think it is the source of the problem. |
Yep, I've tested the latest versions of both webpack/rollup - no changes with tree shaking. There are indeed uglify warnings, maybe this can help. |
There we go, lot of lines like this one
These variables remain in the minified version. |
I also have a ton of warnings from uglify (over 1000 of various kinds, including some about side effects). It looks like there's not an easy solution for these issues unfortunately. In https://github.com/redmountainmakers/kilntroller-ui/tree/try/closure-compiler I tried switching to closure-compiler instead - the build was a good bit smaller but I got a bunch of errors. |
Can someone explain in simple terms? I am trying to make sense of the informations present but i have no idea what's going on. I am trying to get an outcome from three.js latest build, which finally features modules.
It seems to pretty much pull the entire lib, half a MB when it should be a few KB. |
i think webpack2 needs that to be:
|
No, webpack 2 should be able to do tree shaking with namespace imports, as The important part is that the imported file must use ES6 exports. On Fri, 2 Sep 2016 at 0:47 jonathan schatz notifications@github.com wrote:
|
I've hit the same problem: index.js import {sudoMakeMeASandwich} from './re-export';
console.log(sudoMakeMeASandwich); // make sure this one is used. re-export.js export {makeMeASandwich, sudoMakeMeASandwich} from './helpers';
export {unusedHelper} from './unused-helper'; helpers.js export const makeMeASandwich = () => 'make sandwich: operation not permitted';
export const sudoMakeMeASandwich = () => 'one open faced club sandwich coming right up'; unused-helper.js import * as unused2 from './unused-helper-2';
export const unusedHelper = unused2; unused-helper-2.js export const FOO = 'FOO'; Expected outputThe expected output is a bundle with only the following modules: index, re-export and helpers. The unused modules should not be included, as those are only re-exported by re-export.js, but this export is never used. Actual output/******/ (function(modules) { // webpackBootstrap
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__helpers__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__unused_helper__ = __webpack_require__(3);
/* unused harmony reexport makeMeASandwich */
/* harmony reexport (binding) */ __webpack_require__.d(exports, "a", function() { return __WEBPACK_IMPORTED_MODULE_0__helpers__["a"]; });
/* unused harmony reexport unusedHelper */
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* unused harmony export makeMeASandwich */
/* harmony export (binding) */ __webpack_require__.d(exports, "a", function() { return sudoMakeMeASandwich; });
var makeMeASandwich = function makeMeASandwich() {
return 'make sandwich: operation not permitted';
};
var sudoMakeMeASandwich = function sudoMakeMeASandwich() {
return 'one open faced club sandwich coming right up';
};
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(exports, "FOO", function() { return FOO; });
var FOO = 'FOO';
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__unused_helper_2__ = __webpack_require__(2);
/* unused harmony export unusedHelper */
var unusedHelper = __WEBPACK_IMPORTED_MODULE_0__unused_helper_2__;
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__re_export__ = __webpack_require__(0);
console.log(__webpack_require__.i(__WEBPACK_IMPORTED_MODULE_0__re_export__["a" /* sudoMakeMeASandwich */])());
/***/ }
/******/ ]); RollupAs a small comparison, rollup outputs only: const sudoMakeMeASandwich = () => 'one open faced club sandwich coming right up';
console.log(sudoMakeMeASandwich()); |
@bkniffler why would you need it for lodash? In lodash you can import the micromodules directly, I dont think you should import it globally |
@Vanuan Unused named exports should be removed by tree-shaking (or rather, marked as unused so that Uglify can omit it) - https://webpack.js.org/guides/tree-shaking/ I think the problem here is re-exporting. |
@weisk If you import like |
@bkniffler
|
You could also use babel-plugin-lodash to automatically transform this import { throttle } from "lodash"; to something equivalent to import throttle from "lodash/throttle"; |
Ran into the same issue with the latest rxjs version when importing like this: |
For those who follow this issue, or are subscribed to it, please note that it is now documented as "Caveats" in main documentation: https://webpack.js.org/guides/tree-shaking/#caveats |
the same to me.
|
webpack 4 allows you to opt-in into more aggressive tree shaking. |
@sokra show a working example please for proof. Similar to the example in the first message of the issue, where an external package is used. |
https://github.com/sokra/react-rollup-webpack-test Note that instead of adding the |
@sokra very interesting, indeed. 👍 Is there any guide/article about added |
Documentation is in progress. It's still in alpha... |
Because of webpack/webpack#2867, webpack doesn't remove all unused code when a user imports a single component from the library. 'date-fns' Webpack 4 fixes that for libraries that explicitly anounce thmeselves to be side effect free: https://github.com/webpack/webpack/tree/next/examples/side-effects.
Because of webpack/webpack#2867, webpack doesn't always remove all unused code when a user imports a single component from the library. Webpack 4 fixes that for libraries that explicitly anounce thmeselves to be side effect free: https://github.com/webpack/webpack/tree/next/examples/side-effects.
Because of webpack/webpack#2867, webpack doesn't remove all unused code when a user imports a single component from the library. 'date-fns' Webpack 4 fixes that for libraries that explicitly anounce thmeselves to be side effect free: https://github.com/webpack/webpack/tree/next/examples/side-effects.
I'm using webpack@3.10.0 and correctly configured babel-loader to avoid I've trying different webpack config combinations and what I discovered is that tree-shaking does NOT work when using My 2 big libs that won't tree-shake are |
Since this has been implemented in webpack 4 (#next branch), and landed, I'm going to close and lock this issue. Please submit a new issue, with a reproducible case and repo included if you believe there is something specifically that is not getting shaken by webpack. Otherwise please submit issues to relative projects (uglify, etc.), where appropriate. |
updated with the latest versions (29.06.2017)
Webpack - 3.0.0
Babel-preset-env - 1.5.2
Node - 8.1.2
OS X - 10.12.5
Current
Tree shaking is not removing imports, not used in the app.
Expected
Tree shaking, removing not used imports.
The test case is a simple app with React & React Router v4.
Link
component is not used, but the whole router package is bundled.Webpack & Rollup test repo
The text was updated successfully, but these errors were encountered: