Webpack 2 circa November 2016

Zach Wegrzyniak
3 min readNov 20, 2016

--

As of writing, Webpack 2 is still currently in beta (webpack@2.1.0-beta.27). The developers have hinted that a release is coming soon though. Here’s a look at getting set up with the current version of the config file and babel, and what that gives you.

The biggest new feature of Webpack 2 is tree-shaking. That is, if you only use a portion of a module’s exports, you shouldn’t have to include the entirety of the module at run time.

// exporter-cjs.js with CommonJS exports
module.exports.timesTwo = x => x * 2
module.exports.addOne = x => x + 1

When we go import exporter.js like so

// importer-cjs.js with CommonJS exports
const exporter = require('./exporter')
const result = exporter.timesTwo(2) // 4

And installing webpack with npm install webpack@beta --save and running with node_modules/.bin/webpackon this webpack.config.js

module.exports = {
entry: {
cjs: './importer-cjs',
es: './importer'
},
output: {
filename: '[name]-bundle.js'
}
}

Our output bundle is this, starting at line 67 past the webpack bootstrapping

/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
const exporter = __webpack_require__(1)const result = exporter.timesTwo(2) // 4/***/ },
/* 1 */
/***/ function(module, exports) {
// exporter.js in CommonJS
exports.timesTwo = x => x * 2
exports.addOne = x => x + 1/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__(0);/***/ }
/******/ ]);

Webpack boils down each of the various types of imports it supports (ES, CommonJS, AMD) into an internal webpack require. importer.js and exporter.js are module 0 and 1 respectively. Modules are ordered by occurrence, so if a module is imported many times, it’ll be imported earlier on.

Adding in babel after npm install babel-core babel-loader babel-preset-env will give us a config that looks like the following:

module.exports = {
entry: {
cjs: './importer-cjs',
es: './importer'
},
output: {
filename: '[name]-bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
query: {
presets: [
['env', {modules: false}] // Stops babel from transpiling imports to CommonJS so webpack can work its magic.
]
}
}
]
}
}

As an aside, babel-preset-env is billed as an autoprefixer for browser support. Since I have to support IE in a lot of projects still, I’ve been using it recently.

Let’s see what ES modules can do for us.

// exporter.js
export const timesTwo = x => x * 2
export const plusOne = x => x + 1

and

// importer.js
import * as exporter from './exporter'
exporter.timesTwo(4) // 8

Now gives us

/******/ ([
/* 0 */,
/* 1 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(exports, "a", function() { return timesTwo; });
/* unused harmony export addOne */
// exporter.js in CommonJS
var timesTwo = function timesTwo(x) {
return x * 2;
};
var addOne = function addOne(x) {
return x + 1;
};
/***/ },
/* 2 */,
/* 3 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__exporter__ = __webpack_require__(1);
var result = __WEBPACK_IMPORTED_MODULE_0__exporter__["a" /* timesTwo */](4); // 8/***/ }
/******/ ]);

Note: ES module exports are mangled by default and that webpack distinguishes between ‘binding’ and ‘immutable’ ES module exports, and it seems that immutable exports produce more compact code. binding exports are export const addOne = x => x + 1 and immutable exports are export function addOne(x) { return x + 1 }. For output, the two forms produce

__webpack_require__.d(exports, "a", function() { return timesTwo; });// Much shorter immutable export
exports["a"] = timesTwo;

Webpack can now see that addOne in exporter.js is never used, so it leaves a comment to show that./* Unused harmony export addOne */ on top of the comment, uglify (what webpack uses by default for minification) can now remove that function from the minified output.

If we run uglify with --compress, we can see that addOne is dropped for our ES module bundle.

node_modules/.bin/uglifyjs es-bundle.js --compress
WARN: Dropping unused variable addOne [es-bundle.js:80,4]

One other neat thing about ES modules, is that if an import doesn’t exist as an export, webpack can pick it up and warn about it.

WARNING in ./importer.js
4:0 export 'foo' was not found in './exporter'

All told, Webpack 2 is shaping up nicely. Happy tree shaking.

--

--