Repository: blacksonic/babel-webpack-tree-shaking Branch: master Commit: dcbbc1a1b336 Files: 15 Total size: 16.2 KB Directory structure: gitextract_1cto7w5u/ ├── .babelrc ├── .gitignore ├── app/ │ ├── car.js │ └── engine.js ├── dist/ │ ├── car.bundle.js │ ├── car.es2015.bundle.js │ ├── car.es2015.prod.bundle.js │ └── car.prod.bundle.js ├── index.html ├── package.json ├── readme.md ├── webpack.config.js ├── webpack.es2015.config.js ├── webpack.es2015.prod.config.js └── webpack.prod.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ ["env", { "loose": true, "modules": false }] ], "plugins": ["transform-runtime"] } ================================================ FILE: .gitignore ================================================ node_modules .idea ================================================ FILE: app/car.js ================================================ import { V8Engine } from './engine'; class SportsCar { constructor(engine) { this.engine = engine; } toString() { return this.engine.toString() + ' Sports Car'; } } console.log(new SportsCar(new V8Engine()).toString()); ================================================ FILE: app/engine.js ================================================ export class V6Engine { toString() { return 'V6'; } } export class V8Engine { toString() { return 'V8'; } } export function getVersion() { return '1.0'; } ================================================ FILE: dist/car.bundle.js ================================================ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 2); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; exports.__esModule = true; exports.default = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* unused harmony export V6Engine */ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return V8Engine; }); /* unused harmony export getVersion */ /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__ = __webpack_require__(0); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__); var V6Engine = function () { function V6Engine() { __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(this, V6Engine); } V6Engine.prototype.toString = function toString() { return 'V6'; }; return V6Engine; }(); var V8Engine = function () { function V8Engine() { __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(this, V8Engine); } V8Engine.prototype.toString = function toString() { return 'V8'; }; return V8Engine; }(); function getVersion() { return '1.0'; } /***/ }), /* 2 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__ = __webpack_require__(0); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck__); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__engine__ = __webpack_require__(1); var SportsCar = function () { function SportsCar(engine) { __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_classCallCheck___default()(this, SportsCar); this.engine = engine; } SportsCar.prototype.toString = function toString() { return this.engine.toString() + ' Sports Car'; }; return SportsCar; }(); console.log(new SportsCar(new __WEBPACK_IMPORTED_MODULE_1__engine__["a" /* V8Engine */]()).toString()); /***/ }) /******/ ]); ================================================ FILE: dist/car.es2015.bundle.js ================================================ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 1); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; /* unused harmony export getVersion */ class V6Engine { toString() { return 'V6'; } } /* unused harmony export V6Engine */ class V8Engine { toString() { return 'V8'; } } /* harmony export (immutable) */ __webpack_exports__["a"] = V8Engine; function getVersion() { return '1.0'; } /***/ }), /* 1 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__engine__ = __webpack_require__(0); class SportsCar { constructor(engine) { this.engine = engine; } toString() { return this.engine.toString() + ' Sports Car'; } } console.log(new SportsCar(new __WEBPACK_IMPORTED_MODULE_0__engine__["a" /* V8Engine */]()).toString()); /***/ }) /******/ ]); ================================================ FILE: dist/car.es2015.prod.bundle.js ================================================ (function(a){function b(d){if(c[d])return c[d].exports;var e=c[d]={i:d,l:!1,exports:{}};return a[d].call(e.exports,e,e.exports,b),e.l=!0,e.exports}var c={};return b.m=a,b.c=c,b.i=function(a){return a},b.d=function(a,c,d){b.o(a,c)||Object.defineProperty(a,c,{configurable:!1,enumerable:!0,get:d})},b.n=function(a){var c=a&&a.__esModule?function(){return a['default']}:function(){return a};return b.d(c,'a',c),c},b.o=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)},b.p='',b(b.s=1)})([function(a,b){'use strict';b.a=class{toString(){return'V8'}}},function(a,b,c){'use strict';Object.defineProperty(b,'__esModule',{value:!0});var d=c(0);console.log(new class{constructor(a){this.engine=a}toString(){return this.engine.toString()+' Sports Car'}}(new d.a()).toString())}]); ================================================ FILE: dist/car.prod.bundle.js ================================================ !function(n){function t(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return n[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var r={};t.m=n,t.c=r,t.i=function(n){return n},t.d=function(n,r,e){t.o(n,r)||Object.defineProperty(n,r,{configurable:!1,enumerable:!0,get:e})},t.n=function(n){var r=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(r,"a",r),r},t.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},t.p="",t(t.s=2)}([function(n,t,r){"use strict";t.__esModule=!0,t.default=function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}},function(n,t,r){"use strict";r.d(t,"a",function(){return u});var e=r(0),o=r.n(e),u=(function(){function n(){o()(this,n)}n.prototype.toString=function(){return"V6"}}(),function(){function n(){o()(this,n)}return n.prototype.toString=function(){return"V8"},n}())},function(n,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var e=r(0),o=r.n(e),u=r(1),i=function(){function n(t){o()(this,n),this.engine=t}return n.prototype.toString=function(){return this.engine.toString()+" Sports Car"},n}();console.log(new i(new u.a).toString())}]); ================================================ FILE: index.html ================================================ Tree-shaking example with Babel and Webpack ================================================ FILE: package.json ================================================ { "name": "babel-webpack-tree-shaking", "version": "1.0.0", "description": "Tree-shaking example with Babel and Webpack", "main": "dist/car.es2015.prod.bundle.js", "scripts": { "webpack": "webpack --config webpack.config.js", "webpack-prod": "webpack --config webpack.prod.config.js", "webpack-es2015": "webpack --config webpack.es2015.config.js", "webpack-es2015-prod": "webpack --config webpack.es2015.prod.config.js", "serve": "http-server -a 0.0.0.0 -p 9000" }, "repository": { "type": "git", "url": "git+https://github.com/blacksonic/babel-webpack-tree-shaking.git" }, "keywords": [ "tree-shaking", "babel", "webpack" ], "author": { "name": "blacksonic", "email": "soos.gabor86@gmail.com" }, "license": "ISC", "dependencies": {}, "devDependencies": { "babel-core": "~6.25.0", "babel-loader": "~7.0.0", "babel-plugin-transform-runtime": "~6.23.0", "babel-preset-env": "~1.5.2", "babili": "~0.1.3", "babili-webpack-plugin": "~0.1.1", "http-server": "~0.10.0", "webpack": "~2.6.1" } } ================================================ FILE: readme.md ================================================ # Tree-shaking example with Babel and Webpack [![devDependency Status](https://david-dm.org/blacksonic/babel-webpack-tree-shaking/dev-status.svg)](https://david-dm.org/blacksonic/babel-webpack-tree-shaking?type=dev) This repository shows how to configure Babel and Webpack to enable tree-shaking. It will eliminate dead code if they have ES2015 module format. The source code can be found in the ```app/``` folder, where the main file ```car.js``` doesn't use all the dependencies from ```engine.js```. The built and transpiled files can be found in the ```/dist``` folder. Webpack only marks unused code at bundling and leaves the removing part to minifiers. This is why all the code is included in development builds in contrary to Rollup. If you want a more detailed explanation, [read this article](https://blog.craftlab.hu/how-to-do-proper-tree-shaking-in-webpack-2-e27852af8b21). ### Webpack + Babel + UglifyJS Can only remove unused functions and variables. For development build run ```npm run webpack``` (ES2015 -> ES5 bundle). For production build run ```npm run webpack-prod``` (ES2015 -> ES5 bundle -> UglifyJS). When transpiling classes with Babel, it generates an IIFE with an assignment to the prototype. It is considered as a side effect by UglifyJS and it skips removing of it. See the issues below: - [Webpack issue](https://github.com/webpack/webpack/issues/2899) and a [more detailed one](https://github.com/webpack/webpack/issues/2867) - [UglifyJS issue](https://github.com/mishoo/UglifyJS2/issues/1261) ### Webpack + Babili Can remove unused classes, functions and variables. For development build run ```npm run webpack-es2015``` (ES2015 -> ES2015 bundle). For production build run ```npm run webpack-es2015-prod``` (ES2015 -> ES2015 bundle -> Babili). It uses the [Babili](https://github.com/babel/babili) ES6+ aware minifier and solves the issues what UglifyJS has. ================================================ FILE: webpack.config.js ================================================ 'use strict'; let path = require('path'); module.exports = { entry: { 'car': './app/car.js' }, output: { path: path.resolve(process.cwd(), 'dist'), filename: '[name].bundle.js' }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader' } ] }, resolve: { modules: [ path.join(process.cwd(), 'app'), 'node_modules' ], extensions: ['.js', '.json'] }, devtool: false }; ================================================ FILE: webpack.es2015.config.js ================================================ 'use strict'; let path = require('path'); module.exports = { entry: { 'car.es2015': './app/car.js' }, output: { path: path.resolve(process.cwd(), 'dist'), filename: '[name].bundle.js' }, module: { rules: [] }, plugins: [], resolve: { modules: [ path.join(process.cwd(), 'app'), 'node_modules' ], extensions: ['.js', '.json'] }, devtool: false }; ================================================ FILE: webpack.es2015.prod.config.js ================================================ 'use strict'; let BabiliPlugin = require("babili-webpack-plugin"); let path = require('path'); module.exports = { entry: { 'car.es2015.prod': './app/car.js' }, output: { path: path.resolve(process.cwd(), 'dist'), filename: '[name].bundle.js' }, module: { rules: [] }, plugins: [ new BabiliPlugin() ], resolve: { modules: [ path.join(process.cwd(), 'app'), 'node_modules' ], extensions: ['.js', '.json'] }, devtool: false }; ================================================ FILE: webpack.prod.config.js ================================================ 'use strict'; let webpack = require('webpack'); let path = require('path'); module.exports = { entry: { 'car.prod': './app/car.js' }, output: { path: path.resolve(process.cwd(), 'dist'), filename: '[name].bundle.js' }, module: { rules: [ { test: /\.js$/, loader: 'babel-loader' } ] }, plugins: [ new webpack.LoaderOptionsPlugin({ minimize: true, debug: false }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: true }, output: { comments: false }, sourceMap: false }) ], resolve: { modules: [ path.join(process.cwd(), 'app'), 'node_modules' ], extensions: ['.js', '.json'] }, devtool: false };