12

I'm trying to find a way to tree-shake my modules and use Babel with Webpack.

If I take the example code from the webpack documentation (https://webpack.js.org/guides/tree-shaking/) and run it, the modules/functions/other exports that are not used are marked as unused harmony exports, which is the expected outcome. After running webpack with the -p argument (production), webpack uses UglifyJS to remove the dead and unused code (to tree-shake).

Now, if I add babel-loader to my webpack config file, my ES2015 modules are transpiled but now are not marked as unused exports anymore.

So for example:

math.js

export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

app.js (my entry file)

import {square} from './math.js'

Running this through webpack WITHOUT babel-loader, the cube function will get marked as unused and removed after compiling for production (-p).

Running this through webpack WITH babel-loader, the cube function will not be marked as unused and will stay in the compiled bundle.

What am I missing?

Edit

Here's a demo repo that can reproduce the situation

https://github.com/Milanzor/babel-and-treeshaking-question

Update

If I add a .babelrc:

{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "entry",
      "debug": true,
      "targets": {
        "browsers": ["last 2 versions"]
      }
    }]
  ]
}

I get the same result, but if I add modules: false to the preset-env options, Babel doesn't compile the modules to ES5 and Webpack marks the modules as unused again.

Conclusion

I need to find a way to tell Webpack that my modules are transpiled with Babel, or I need to find a way to tell Babel to scan for unused codes itself.

Milanzor
  • 1,920
  • 14
  • 22
  • 1
    Are you using `modules: false` on your Babel preset so that module syntax isn't transformed? – loganfsmyth Dec 05 '17 at 23:04
  • @loganfsmyth Nope, I wish I was! I made a demo repo to show my setup: https://github.com/Milanzor/babel-and-treeshaking-question – Milanzor Dec 06 '17 at 07:37
  • @loganfsmyth To test, adding `modules: false` to babel's options, it works again (see the Update in my question). What causes Webpack to not see the unused code after it's transpiled to ES5 through babel? – Milanzor Dec 06 '17 at 13:08

1 Answers1

32

Webpack's built-in tree shaking works on ES6 module syntax only. If you're using Babel's defaults settings, Babel will compile ES6 modules to CommonJS modules, leaving nothing for Webpack to work with.

Generally people using Webpack will want to pass modules: false to the preset that they are using for ES6 (probably preset-env?), thus doing

{
  presets: [
    ['env', { modules: false }],
  ],
}

alternatively you could consider using a plugin like https://github.com/indutny/webpack-common-shake to enable tree-shaking for CommonJS modules.

Update

If you're using Babel 7 (and thus @babel/preset-env), the modules option is now automatically false when used in Webpack, so this explicit configuration should no longer be needed.

loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
  • Awesome, thanks for the reply. When setting `modules` to `false`will mean my modules aren't transpiled and will not run in non mainstream browsers, so pretty much why I use babel in the first place. I'll have a look at the common-shake plugin, thanks for the link! – Milanzor Dec 06 '17 at 14:18
  • 2
    @myself, `will not run in mainstream browsers` => false, I don't need to transpile my modules to ES5 with Babel, Webpack transpiles so they run in browsers! This is the missing link, `modules: false` so Babel leaves my modules for Webpack to picked. Thanks! – Milanzor Dec 06 '17 at 17:02
  • @nodenoob I had to find out why I wanted to use Babel as a loader, because I'm new to Webpack and Babel, but I realised that without Babel, Webpack does not compile ES6 classes and other ES6 functions to browser-supported code. So all I want Babel to do is make my code work in older browsers, but not wreck it so bad that tree-shaking doesn't work anymore. So `modules: false` solves this for me. Do correct me if I'm wrong, trying to learn :) – Milanzor Dec 06 '17 at 17:32
  • Yep! `modules: false` makes Babel pass just the module syntax through to Webpack so it can process everything better. – loganfsmyth Dec 06 '17 at 23:38
  • 3
    @Milanzor I have never in my whole Stackoverflow lifetime seen a better example of someone @ myself'ing themselves! – Pat Needham Oct 22 '18 at 18:35
  • @PatNeedham haha, self-reflection is key! – Milanzor Oct 23 '18 at 06:45
  • Thanks for this, it’s really helped. – Nick Taras Jun 25 '20 at 20:40