diff --git a/examples/common-chunk-grandchildren/README.md b/examples/common-chunk-grandchildren/README.md
new file mode 100644
index 000000000..3ee56bfb6
--- /dev/null
+++ b/examples/common-chunk-grandchildren/README.md
@@ -0,0 +1,856 @@
+This example illustrates how common modules from deep ancestors of an entry point can be split into a seperate common chunk
+
+* `pageA` and `pageB` are dynamically required
+* `pageC` and `pageA` both require the `reusableComponent`
+* `pageB` dynamically requires `PageC`
+
+You can see that webpack outputs four files/chunks:
+
+* `output.js` is the entry chunk and contains
+ * the module system
+ * chunk loading logic
+ * the entry point `example.js`
+ * module `reusableComponent`
+* `0.output.js` is an additional chunk
+ * module `pageC`
+* `1.output.js` is an additional chunk
+ * module `pageB`
+* `2.output.js` is an additional chunk
+ * module `pageA`
+
+
+# example.js
+
+``` javascript
+var main = function() {
+ console.log("Main class");
+ require.ensure([], () => {
+ const page = require("./pageA");
+ page();
+ });
+ require.ensure([], () => {
+ const page = require("./pageB");
+ page();
+ });
+};
+
+main();
+```
+
+# pageA.js
+
+``` javascript
+var reusableComponent = require("./reusableComponent");
+
+module.exports = function() {
+ console.log("Page A");
+ reusableComponent();
+};
+```
+
+# pageB.js
+
+``` javascript
+module.exports = function() {
+ console.log("Page B");
+ require.ensure([], ()=>{
+ const page = require("./pageC");
+ page();
+ });
+};
+```
+
+# pageC.js
+
+``` javascript
+var reusableComponent = require("./reusableComponent");
+
+module.exports = function() {
+ console.log("Page C");
+ reusableComponent();
+};
+```
+
+# reusableComponent.js
+
+``` javascript
+module.exports = function() {
+ console.log("reusable Component");
+};
+```
+
+# webpack.config.js
+
+``` javascript
+"use strict";
+const webpack = require("../../");
+const path = require("path");
+
+module.exports = [
+ {
+ entry: {
+ main: ["./example.js"]
+ },
+ output: {
+ path: path.resolve(__dirname, "js"),
+ filename: "output.js"
+ },
+ plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "main",
+ minChunks: 2,
+ children: true,
+ deepChildren: true,
+ })
+ ]
+ },
+ {
+ entry: {
+ main: ["./example.js"]
+ },
+ output: {
+ path: path.resolve(__dirname, "js"),
+ filename: "asyncoutput.js"
+ },
+ plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "main",
+ minChunks: 2,
+ async: true,
+ children: true,
+ deepChildren: true,
+ })
+ ]
+ }
+];
+```
+
+# js/output.js
+
+/******/ (function(modules) { /* webpackBootstrap */ })
+
+``` javascript
+/******/ (function(modules) { // webpackBootstrap
+/******/ // install a JSONP callback for chunk loading
+/******/ var parentJsonpFunction = window["webpackJsonp"];
+/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
+/******/ // add "moreModules" to the modules object,
+/******/ // then flag all "chunkIds" as loaded and fire callback
+/******/ var moduleId, chunkId, i = 0, resolves = [], result;
+/******/ for(;i < chunkIds.length; i++) {
+/******/ chunkId = chunkIds[i];
+/******/ if(installedChunks[chunkId]) {
+/******/ resolves.push(installedChunks[chunkId][0]);
+/******/ }
+/******/ installedChunks[chunkId] = 0;
+/******/ }
+/******/ for(moduleId in moreModules) {
+/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
+/******/ modules[moduleId] = moreModules[moduleId];
+/******/ }
+/******/ }
+/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
+/******/ while(resolves.length) {
+/******/ resolves.shift()();
+/******/ }
+/******/
+/******/ };
+/******/
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // objects to store loaded and loading chunks
+/******/ var installedChunks = {
+/******/ 3: 0
+/******/ };
+/******/
+/******/ // 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;
+/******/ }
+/******/
+/******/ // This file contains only the entry chunk.
+/******/ // The chunk loading function for additional chunks
+/******/ __webpack_require__.e = function requireEnsure(chunkId) {
+/******/ var installedChunkData = installedChunks[chunkId];
+/******/ if(installedChunkData === 0) {
+/******/ return new Promise(function(resolve) { resolve(); });
+/******/ }
+/******/
+/******/ // a Promise means "currently loading".
+/******/ if(installedChunkData) {
+/******/ return installedChunkData[2];
+/******/ }
+/******/
+/******/ // setup Promise in chunk cache
+/******/ var promise = new Promise(function(resolve, reject) {
+/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
+/******/ });
+/******/ installedChunkData[2] = promise;
+/******/
+/******/ // start chunk loading
+/******/ var head = document.getElementsByTagName('head')[0];
+/******/ var script = document.createElement('script');
+/******/ script.type = 'text/javascript';
+/******/ script.charset = 'utf-8';
+/******/ script.async = true;
+/******/ script.timeout = 120000;
+/******/
+/******/ if (__webpack_require__.nc) {
+/******/ script.setAttribute("nonce", __webpack_require__.nc);
+/******/ }
+/******/ script.src = __webpack_require__.p + "" + chunkId + ".output.js";
+/******/ var timeout = setTimeout(onScriptComplete, 120000);
+/******/ script.onerror = script.onload = onScriptComplete;
+/******/ function onScriptComplete() {
+/******/ // avoid mem leaks in IE.
+/******/ script.onerror = script.onload = null;
+/******/ clearTimeout(timeout);
+/******/ var chunk = installedChunks[chunkId];
+/******/ if(chunk !== 0) {
+/******/ if(chunk) {
+/******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
+/******/ }
+/******/ installedChunks[chunkId] = undefined;
+/******/ }
+/******/ };
+/******/ head.appendChild(script);
+/******/
+/******/ return promise;
+/******/ };
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // 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 = "js/";
+/******/
+/******/ // on error function for async loading
+/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+```
+
+
+
+``` javascript
+/******/ ([
+/* 0 */
+/*!**************************!*\
+ !*** multi ./example.js ***!
+ \**************************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = __webpack_require__(/*! ./example.js */1);
+
+
+/***/ }),
+/* 1 */
+/*!********************!*\
+ !*** ./example.js ***!
+ \********************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var main = function() {
+ console.log("Main class");
+ __webpack_require__.e/* require.ensure */(2).then((() => {
+ const page = __webpack_require__(/*! ./pageA */ 3);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+ __webpack_require__.e/* require.ensure */(1).then((() => {
+ const page = __webpack_require__(/*! ./pageB */ 4);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+};
+
+main();
+
+
+/***/ }),
+/* 2 */
+/*!******************************!*\
+ !*** ./reusableComponent.js ***!
+ \******************************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports) {
+
+module.exports = function() {
+ console.log("reusable Component");
+};
+
+
+/***/ })
+/******/ ]);
+```
+
+# js/0.output.js
+
+``` javascript
+webpackJsonp([0],{
+
+/***/ 5:
+/*!******************!*\
+ !*** ./pageC.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var reusableComponent = __webpack_require__(/*! ./reusableComponent */ 2);
+
+module.exports = function() {
+ console.log("Page C");
+ reusableComponent();
+};
+
+
+/***/ })
+
+});
+```
+
+# js/1.output.js
+
+``` javascript
+webpackJsonp([1],{
+
+/***/ 4:
+/*!******************!*\
+ !*** ./pageB.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = function() {
+ console.log("Page B");
+ __webpack_require__.e/* require.ensure */(0).then((()=>{
+ const page = __webpack_require__(/*! ./pageC */ 5);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+};
+
+
+/***/ })
+
+});
+```
+
+# js/2.output.js
+
+``` javascript
+webpackJsonp([2],{
+
+/***/ 3:
+/*!******************!*\
+ !*** ./pageA.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var reusableComponent = __webpack_require__(/*! ./reusableComponent */ 2);
+
+module.exports = function() {
+ console.log("Page A");
+ reusableComponent();
+};
+
+
+/***/ })
+
+});
+```
+
+# js/asyncoutput.js
+
+``` javascript
+/******/ (function(modules) { // webpackBootstrap
+/******/ // install a JSONP callback for chunk loading
+/******/ var parentJsonpFunction = window["webpackJsonp"];
+/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
+/******/ // add "moreModules" to the modules object,
+/******/ // then flag all "chunkIds" as loaded and fire callback
+/******/ var moduleId, chunkId, i = 0, resolves = [], result;
+/******/ for(;i < chunkIds.length; i++) {
+/******/ chunkId = chunkIds[i];
+/******/ if(installedChunks[chunkId]) {
+/******/ resolves.push(installedChunks[chunkId][0]);
+/******/ }
+/******/ installedChunks[chunkId] = 0;
+/******/ }
+/******/ for(moduleId in moreModules) {
+/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
+/******/ modules[moduleId] = moreModules[moduleId];
+/******/ }
+/******/ }
+/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
+/******/ while(resolves.length) {
+/******/ resolves.shift()();
+/******/ }
+/******/
+/******/ };
+/******/
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // objects to store loaded and loading chunks
+/******/ var installedChunks = {
+/******/ 4: 0
+/******/ };
+/******/
+/******/ // 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;
+/******/ }
+/******/
+/******/ // This file contains only the entry chunk.
+/******/ // The chunk loading function for additional chunks
+/******/ __webpack_require__.e = function requireEnsure(chunkId) {
+/******/ var installedChunkData = installedChunks[chunkId];
+/******/ if(installedChunkData === 0) {
+/******/ return new Promise(function(resolve) { resolve(); });
+/******/ }
+/******/
+/******/ // a Promise means "currently loading".
+/******/ if(installedChunkData) {
+/******/ return installedChunkData[2];
+/******/ }
+/******/
+/******/ // setup Promise in chunk cache
+/******/ var promise = new Promise(function(resolve, reject) {
+/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
+/******/ });
+/******/ installedChunkData[2] = promise;
+/******/
+/******/ // start chunk loading
+/******/ var head = document.getElementsByTagName('head')[0];
+/******/ var script = document.createElement('script');
+/******/ script.type = 'text/javascript';
+/******/ script.charset = 'utf-8';
+/******/ script.async = true;
+/******/ script.timeout = 120000;
+/******/
+/******/ if (__webpack_require__.nc) {
+/******/ script.setAttribute("nonce", __webpack_require__.nc);
+/******/ }
+/******/ script.src = __webpack_require__.p + "" + chunkId + ".asyncoutput.js";
+/******/ var timeout = setTimeout(onScriptComplete, 120000);
+/******/ script.onerror = script.onload = onScriptComplete;
+/******/ function onScriptComplete() {
+/******/ // avoid mem leaks in IE.
+/******/ script.onerror = script.onload = null;
+/******/ clearTimeout(timeout);
+/******/ var chunk = installedChunks[chunkId];
+/******/ if(chunk !== 0) {
+/******/ if(chunk) {
+/******/ chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));
+/******/ }
+/******/ installedChunks[chunkId] = undefined;
+/******/ }
+/******/ };
+/******/ head.appendChild(script);
+/******/
+/******/ return promise;
+/******/ };
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // 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 = "js/";
+/******/
+/******/ // on error function for async loading
+/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/*!**************************!*\
+ !*** multi ./example.js ***!
+ \**************************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = __webpack_require__(/*! ./example.js */1);
+
+
+/***/ }),
+/* 1 */
+/*!********************!*\
+ !*** ./example.js ***!
+ \********************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var main = function() {
+ console.log("Main class");
+ Promise.all/* require.ensure */([__webpack_require__.e(0), __webpack_require__.e(2)]).then((() => {
+ const page = __webpack_require__(/*! ./pageA */ 2);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+ __webpack_require__.e/* require.ensure */(1).then((() => {
+ const page = __webpack_require__(/*! ./pageB */ 3);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+};
+
+main();
+
+
+/***/ })
+/******/ ]);
+```
+
+# js/0.asyncoutput.js
+
+``` javascript
+webpackJsonp([0],{
+
+/***/ 4:
+/*!******************************!*\
+ !*** ./reusableComponent.js ***!
+ \******************************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports) {
+
+module.exports = function() {
+ console.log("reusable Component");
+};
+
+
+/***/ })
+
+});
+```
+
+# js/1.asyncoutput.js
+
+``` javascript
+webpackJsonp([1],{
+
+/***/ 3:
+/*!******************!*\
+ !*** ./pageB.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+module.exports = function() {
+ console.log("Page B");
+ Promise.all/* require.ensure */([__webpack_require__.e(0), __webpack_require__.e(3)]).then((()=>{
+ const page = __webpack_require__(/*! ./pageC */ 5);
+ page();
+ }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
+};
+
+
+/***/ })
+
+});
+```
+
+# js/2.asyncoutput.js
+
+``` javascript
+webpackJsonp([2],{
+
+/***/ 2:
+/*!******************!*\
+ !*** ./pageA.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var reusableComponent = __webpack_require__(/*! ./reusableComponent */ 4);
+
+module.exports = function() {
+ console.log("Page A");
+ reusableComponent();
+};
+
+
+/***/ })
+
+});
+```
+
+# js/3.asyncoutput.js
+
+``` javascript
+webpackJsonp([3],{
+
+/***/ 5:
+/*!******************!*\
+ !*** ./pageC.js ***!
+ \******************/
+/*! no static exports found */
+/*! all exports used */
+/***/ (function(module, exports, __webpack_require__) {
+
+var reusableComponent = __webpack_require__(/*! ./reusableComponent */ 4);
+
+module.exports = function() {
+ console.log("Page C");
+ reusableComponent();
+};
+
+
+/***/ })
+
+});
+```
+
+# Info
+
+## Uncompressed
+
+```
+Hash: 777f989f517d7831d56f1d268f47bc8703ad9f73
+Version: webpack 3.6.0
+Child
+ Hash: 777f989f517d7831d56f
+ Asset Size Chunks Chunk Names
+ 0.output.js 388 bytes 0 [emitted]
+ 1.output.js 481 bytes 1 [emitted]
+ 2.output.js 388 bytes 2 [emitted]
+ output.js 6.95 kB 3 [emitted] main
+ Entrypoint main = output.js
+ chunk {0} 0.output.js 142 bytes {3} [rendered]
+ > [4] ./pageB.js 3:1-6:3
+ [5] ./pageC.js 142 bytes {0} [built]
+ cjs require ./pageC [4] ./pageB.js 4:15-33
+ chunk {1} 1.output.js 140 bytes {3} [rendered]
+ > [1] ./example.js 7:1-10:3
+ [4] ./pageB.js 140 bytes {1} [built]
+ cjs require ./pageB [1] ./example.js 8:15-33
+ chunk {2} 2.output.js 142 bytes {3} [rendered]
+ > [1] ./example.js 3:1-6:3
+ [3] ./pageA.js 142 bytes {2} [built]
+ cjs require ./pageA [1] ./example.js 4:15-33
+ chunk {3} output.js (main) 333 bytes [entry] [rendered]
+ > main [0] multi ./example.js
+ [0] multi ./example.js 28 bytes {3} [built]
+ [1] ./example.js 233 bytes {3} [built]
+ single entry ./example.js [0] multi ./example.js main:100000
+ [2] ./reusableComponent.js 72 bytes {3} [built]
+ cjs require ./reusableComponent [3] ./pageA.js 1:24-54
+ cjs require ./reusableComponent [5] ./pageC.js 1:24-54
+Child
+ Hash: 1d268f47bc8703ad9f73
+ Asset Size Chunks Chunk Names
+ 0.asyncoutput.js 314 bytes 0 [emitted]
+ 1.asyncoutput.js 522 bytes 1 [emitted]
+ 2.asyncoutput.js 388 bytes 2 [emitted]
+ 3.asyncoutput.js 388 bytes 3 [emitted]
+ asyncoutput.js 6.7 kB 4 [emitted] main
+ Entrypoint main = asyncoutput.js
+ chunk {0} 0.asyncoutput.js 72 bytes {4} [rendered]
+ > async commons [1] ./example.js 3:1-6:3
+ > async commons [3] ./pageB.js 3:1-6:3
+ [4] ./reusableComponent.js 72 bytes {0} [built]
+ cjs require ./reusableComponent [2] ./pageA.js 1:24-54
+ cjs require ./reusableComponent [5] ./pageC.js 1:24-54
+ chunk {1} 1.asyncoutput.js 140 bytes {4} [rendered]
+ > [1] ./example.js 7:1-10:3
+ [3] ./pageB.js 140 bytes {1} [built]
+ cjs require ./pageB [1] ./example.js 8:15-33
+ chunk {2} 2.asyncoutput.js 142 bytes {4} [rendered]
+ > [1] ./example.js 3:1-6:3
+ [2] ./pageA.js 142 bytes {2} [built]
+ cjs require ./pageA [1] ./example.js 4:15-33
+ chunk {3} 3.asyncoutput.js 142 bytes {1} [rendered]
+ > [3] ./pageB.js 3:1-6:3
+ [5] ./pageC.js 142 bytes {3} [built]
+ cjs require ./pageC [3] ./pageB.js 4:15-33
+ chunk {4} asyncoutput.js (main) 261 bytes [entry] [rendered]
+ > main [0] multi ./example.js
+ [0] multi ./example.js 28 bytes {4} [built]
+ [1] ./example.js 233 bytes {4} [built]
+ single entry ./example.js [0] multi ./example.js main:100000
+```
+
+## Minimized (uglify-js, no zip)
+
+```
+Hash: 777f989f517d7831d56f1d268f47bc8703ad9f73
+Version: webpack 3.6.0
+Child
+ Hash: 777f989f517d7831d56f
+ Asset Size Chunks Chunk Names
+ 0.output.js 98 bytes 0 [emitted]
+ 1.output.js 340 bytes 1 [emitted]
+ 2.output.js 98 bytes 2 [emitted]
+ output.js 6.46 kB 3 [emitted] main
+ Entrypoint main = output.js
+ chunk {0} 0.output.js 142 bytes {3} [rendered]
+ > [4] ./pageB.js 3:1-6:3
+ [5] ./pageC.js 142 bytes {0} [built]
+ cjs require ./pageC [4] ./pageB.js 4:15-33
+ chunk {1} 1.output.js 140 bytes {3} [rendered]
+ > [1] ./example.js 7:1-10:3
+ [4] ./pageB.js 140 bytes {1} [built]
+ cjs require ./pageB [1] ./example.js 8:15-33
+ chunk {2} 2.output.js 142 bytes {3} [rendered]
+ > [1] ./example.js 3:1-6:3
+ [3] ./pageA.js 142 bytes {2} [built]
+ cjs require ./pageA [1] ./example.js 4:15-33
+ chunk {3} output.js (main) 333 bytes [entry] [rendered]
+ > main [0] multi ./example.js
+ [0] multi ./example.js 28 bytes {3} [built]
+ [1] ./example.js 233 bytes {3} [built]
+ single entry ./example.js [0] multi ./example.js main:100000
+ [2] ./reusableComponent.js 72 bytes {3} [built]
+ cjs require ./reusableComponent [3] ./pageA.js 1:24-54
+ cjs require ./reusableComponent [5] ./pageC.js 1:24-54
+
+ ERROR in 1.output.js from UglifyJs
+ Unexpected token: punc ()) [1.output.js:8,53]
+
+ ERROR in output.js from UglifyJs
+ Unexpected token: punc ()) [output.js:161,53]
+Child
+ Hash: 1d268f47bc8703ad9f73
+ Asset Size Chunks Chunk Names
+ 0.asyncoutput.js 93 bytes 0 [emitted]
+ 1.asyncoutput.js 381 bytes 1 [emitted]
+ 2.asyncoutput.js 98 bytes 2 [emitted]
+ 3.asyncoutput.js 98 bytes 3 [emitted]
+ asyncoutput.js 6.37 kB 4 [emitted] main
+ Entrypoint main = asyncoutput.js
+ chunk {0} 0.asyncoutput.js 72 bytes {4} [rendered]
+ > async commons [1] ./example.js 3:1-6:3
+ > async commons [3] ./pageB.js 3:1-6:3
+ [4] ./reusableComponent.js 72 bytes {0} [built]
+ cjs require ./reusableComponent [2] ./pageA.js 1:24-54
+ cjs require ./reusableComponent [5] ./pageC.js 1:24-54
+ chunk {1} 1.asyncoutput.js 140 bytes {4} [rendered]
+ > [1] ./example.js 7:1-10:3
+ [3] ./pageB.js 140 bytes {1} [built]
+ cjs require ./pageB [1] ./example.js 8:15-33
+ chunk {2} 2.asyncoutput.js 142 bytes {4} [rendered]
+ > [1] ./example.js 3:1-6:3
+ [2] ./pageA.js 142 bytes {2} [built]
+ cjs require ./pageA [1] ./example.js 4:15-33
+ chunk {3} 3.asyncoutput.js 142 bytes {1} [rendered]
+ > [3] ./pageB.js 3:1-6:3
+ [5] ./pageC.js 142 bytes {3} [built]
+ cjs require ./pageC [3] ./pageB.js 4:15-33
+ chunk {4} asyncoutput.js (main) 261 bytes [entry] [rendered]
+ > main [0] multi ./example.js
+ [0] multi ./example.js 28 bytes {4} [built]
+ [1] ./example.js 233 bytes {4} [built]
+ single entry ./example.js [0] multi ./example.js main:100000
+
+ ERROR in 1.asyncoutput.js from UglifyJs
+ Unexpected token: punc ()) [1.asyncoutput.js:8,94]
+
+ ERROR in asyncoutput.js from UglifyJs
+ Unexpected token: punc ()) [asyncoutput.js:161,94]
+```
diff --git a/examples/common-chunk-grandchildren/build.js b/examples/common-chunk-grandchildren/build.js
new file mode 100644
index 000000000..7492e9f9f
--- /dev/null
+++ b/examples/common-chunk-grandchildren/build.js
@@ -0,0 +1,2 @@
+global.NO_TARGET_ARGS = true;
+require("../build-common");
diff --git a/examples/common-chunk-grandchildren/example.js b/examples/common-chunk-grandchildren/example.js
new file mode 100644
index 000000000..2af65a301
--- /dev/null
+++ b/examples/common-chunk-grandchildren/example.js
@@ -0,0 +1,13 @@
+var main = function() {
+ console.log("Main class");
+ require.ensure([], () => {
+ const page = require("./pageA");
+ page();
+ });
+ require.ensure([], () => {
+ const page = require("./pageB");
+ page();
+ });
+};
+
+main();
diff --git a/examples/common-chunk-grandchildren/pageA.js b/examples/common-chunk-grandchildren/pageA.js
new file mode 100644
index 000000000..38883d275
--- /dev/null
+++ b/examples/common-chunk-grandchildren/pageA.js
@@ -0,0 +1,6 @@
+var reusableComponent = require("./reusableComponent");
+
+module.exports = function() {
+ console.log("Page A");
+ reusableComponent();
+};
diff --git a/examples/common-chunk-grandchildren/pageB.js b/examples/common-chunk-grandchildren/pageB.js
new file mode 100644
index 000000000..be3dadf11
--- /dev/null
+++ b/examples/common-chunk-grandchildren/pageB.js
@@ -0,0 +1,7 @@
+module.exports = function() {
+ console.log("Page B");
+ require.ensure([], ()=>{
+ const page = require("./pageC");
+ page();
+ });
+};
diff --git a/examples/common-chunk-grandchildren/pageC.js b/examples/common-chunk-grandchildren/pageC.js
new file mode 100644
index 000000000..b50b26fb0
--- /dev/null
+++ b/examples/common-chunk-grandchildren/pageC.js
@@ -0,0 +1,6 @@
+var reusableComponent = require("./reusableComponent");
+
+module.exports = function() {
+ console.log("Page C");
+ reusableComponent();
+};
diff --git a/examples/common-chunk-grandchildren/reusableComponent.js b/examples/common-chunk-grandchildren/reusableComponent.js
new file mode 100644
index 000000000..2f9489236
--- /dev/null
+++ b/examples/common-chunk-grandchildren/reusableComponent.js
@@ -0,0 +1,3 @@
+module.exports = function() {
+ console.log("reusable Component");
+};
diff --git a/examples/common-chunk-grandchildren/template.md b/examples/common-chunk-grandchildren/template.md
new file mode 100644
index 000000000..3516598b0
--- /dev/null
+++ b/examples/common-chunk-grandchildren/template.md
@@ -0,0 +1,124 @@
+This example illustrates how common modules from deep ancestors of an entry point can be split into a seperate common chunk
+
+* `pageA` and `pageB` are dynamically required
+* `pageC` and `pageA` both require the `reusableComponent`
+* `pageB` dynamically requires `PageC`
+
+You can see that webpack outputs four files/chunks:
+
+* `output.js` is the entry chunk and contains
+ * the module system
+ * chunk loading logic
+ * the entry point `example.js`
+ * module `reusableComponent`
+* `0.output.js` is an additional chunk
+ * module `pageC`
+* `1.output.js` is an additional chunk
+ * module `pageB`
+* `2.output.js` is an additional chunk
+ * module `pageA`
+
+
+# example.js
+
+``` javascript
+{{example.js}}
+```
+
+# pageA.js
+
+``` javascript
+{{pageA.js}}
+```
+
+# pageB.js
+
+``` javascript
+{{pageB.js}}
+```
+
+# pageC.js
+
+``` javascript
+{{pageC.js}}
+```
+
+# reusableComponent.js
+
+``` javascript
+{{reusableComponent.js}}
+```
+
+# webpack.config.js
+
+``` javascript
+{{webpack.config.js}}
+```
+
+# js/output.js
+
+``` javascript
+{{js/output.js}}
+```
+
+# js/0.output.js
+
+``` javascript
+{{js/0.output.js}}
+```
+
+# js/1.output.js
+
+``` javascript
+{{js/1.output.js}}
+```
+
+# js/2.output.js
+
+``` javascript
+{{js/2.output.js}}
+```
+
+# js/asyncoutput.js
+
+``` javascript
+{{js/asyncoutput.js}}
+```
+
+# js/0.asyncoutput.js
+
+``` javascript
+{{js/0.asyncoutput.js}}
+```
+
+# js/1.asyncoutput.js
+
+``` javascript
+{{js/1.asyncoutput.js}}
+```
+
+# js/2.asyncoutput.js
+
+``` javascript
+{{js/2.asyncoutput.js}}
+```
+
+# js/3.asyncoutput.js
+
+``` javascript
+{{js/3.asyncoutput.js}}
+```
+
+# Info
+
+## Uncompressed
+
+```
+{{stdout}}
+```
+
+## Minimized (uglify-js, no zip)
+
+```
+{{min:stdout}}
+```
diff --git a/examples/common-chunk-grandchildren/webpack.config.js b/examples/common-chunk-grandchildren/webpack.config.js
new file mode 100644
index 000000000..7b2696d7b
--- /dev/null
+++ b/examples/common-chunk-grandchildren/webpack.config.js
@@ -0,0 +1,41 @@
+"use strict";
+const webpack = require("../../");
+const path = require("path");
+
+module.exports = [
+ {
+ entry: {
+ main: ["./example.js"]
+ },
+ output: {
+ path: path.resolve(__dirname, "js"),
+ filename: "output.js"
+ },
+ plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "main",
+ minChunks: 2,
+ children: true,
+ deepChildren: true,
+ })
+ ]
+ },
+ {
+ entry: {
+ main: ["./example.js"]
+ },
+ output: {
+ path: path.resolve(__dirname, "js"),
+ filename: "asyncoutput.js"
+ },
+ plugins: [
+ new webpack.optimize.CommonsChunkPlugin({
+ name: "main",
+ minChunks: 2,
+ async: true,
+ children: true,
+ deepChildren: true,
+ })
+ ]
+ }
+];
diff --git a/examples/template-common.js b/examples/template-common.js
index 80ee3b8cc..fb848b644 100644
--- a/examples/template-common.js
+++ b/examples/template-common.js
@@ -2,6 +2,7 @@
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
+"use strict";
const fs = require("fs");
const path = require("path");
diff --git a/lib/Compilation.js b/lib/Compilation.js
index 6bd97a5a4..54c60d4be 100644
--- a/lib/Compilation.js
+++ b/lib/Compilation.js
@@ -584,8 +584,8 @@ class Compilation extends Tapable {
chunk.entryModule = module;
self.assignIndex(module);
self.assignDepth(module);
- self.processDependenciesBlockForChunk(module, chunk);
});
+ self.processDependenciesBlocksForChunks(self.chunks.slice());
self.sortModules(self.modules);
self.applyPlugins0("optimize");
@@ -852,11 +852,16 @@ class Compilation extends Tapable {
}
}
- processDependenciesBlockForChunk(module, chunk) {
- let block = module;
- const initialChunk = chunk;
+ processDependenciesBlocksForChunks(inputChunks) {
const chunkDependencies = new Map(); // Map>
+ const queue = inputChunks.map(chunk => ({
+ block: chunk.entryModule,
+ module: chunk.entryModule,
+ chunk: chunk
+ }));
+
+ let block, module, chunk;
const iteratorBlock = b => {
let c;
if(!b.chunks) {
@@ -901,12 +906,6 @@ class Compilation extends Tapable {
}
};
- const queue = [{
- block,
- module,
- chunk
- }];
-
while(queue.length) {
const queueItem = queue.pop();
block = queueItem.block;
@@ -926,13 +925,12 @@ class Compilation extends Tapable {
}
}
- chunk = initialChunk;
- let chunks = new Set();
- const queue2 = [{
+ const queue2 = inputChunks.map(chunk => ({
chunk,
- chunks
- }];
+ chunks: new Set()
+ }));
+ let chunks;
const filterFn = dep => {
if(chunks.has(dep.chunk)) return false;
for(const chunk of chunks) {
diff --git a/lib/UmdMainTemplatePlugin.js b/lib/UmdMainTemplatePlugin.js
index 2cdf45ec4..b108a5467 100644
--- a/lib/UmdMainTemplatePlugin.js
+++ b/lib/UmdMainTemplatePlugin.js
@@ -42,7 +42,7 @@ class UmdMainTemplatePlugin {
apply(compilation) {
const mainTemplate = compilation.mainTemplate;
compilation.templatesPlugin("render-with-entry", (source, chunk, hash) => {
- let externals = chunk.getModules().filter(m => m.external);
+ let externals = chunk.getModules().filter(m => m.external && (m.type === "umd" || m.type === "umd2"));
const optionalExternals = [];
let requiredExternals = [];
if(this.optionalAmdExternalAsGlobal) {
diff --git a/lib/dependencies/AMDDefineDependencyParserPlugin.js b/lib/dependencies/AMDDefineDependencyParserPlugin.js
index e211771fa..390c47823 100644
--- a/lib/dependencies/AMDDefineDependencyParserPlugin.js
+++ b/lib/dependencies/AMDDefineDependencyParserPlugin.js
@@ -28,6 +28,10 @@ class AMDDefineDependencyParserPlugin {
this.options = options;
}
+ newDefineDependency(range, arrayRange, functionRange, objectRange, namedModule) {
+ return new AMDDefineDependency(range, arrayRange, functionRange, objectRange, namedModule);
+ }
+
apply(parser) {
const options = this.options;
parser.plugin("call define", (expr) => {
@@ -156,7 +160,7 @@ class AMDDefineDependencyParserPlugin {
parser.walkExpression(fn || obj);
}
- const dep = new AMDDefineDependency(
+ const dep = this.newDefineDependency(
expr.range,
array ? array.range : null,
fn ? fn.range : null,
diff --git a/lib/optimize/CommonsChunkPlugin.js b/lib/optimize/CommonsChunkPlugin.js
index 30df8d20a..81f514b73 100644
--- a/lib/optimize/CommonsChunkPlugin.js
+++ b/lib/optimize/CommonsChunkPlugin.js
@@ -4,6 +4,7 @@
*/
"use strict";
let nextIdent = 0;
+
class CommonsChunkPlugin {
constructor(options) {
if(arguments.length > 1) {
@@ -31,6 +32,7 @@ The available options are:
this.minChunks = normalizedOptions.minChunks;
this.selectedChunks = normalizedOptions.selectedChunks;
this.children = normalizedOptions.children;
+ this.deepChildren = normalizedOptions.deepChildren;
this.async = normalizedOptions.async;
this.minSize = normalizedOptions.minSize;
this.ident = __filename + (nextIdent++);
@@ -76,6 +78,7 @@ You can however specify the name of the async chunk by passing the desired strin
minChunks: options.minChunks,
selectedChunks: options.chunks,
children: options.children,
+ deepChildren: options.deepChildren,
async: options.async,
minSize: options.minSize
};
@@ -104,7 +107,7 @@ You can however specify the name of the async chunk by passing the desired strin
/**
* These chunks are subject to get "common" modules extracted and moved to the common chunk
*/
- const affectedChunks = this.getAffectedChunks(compilation, chunks, targetChunk, targetChunks, idx, this.selectedChunks, this.async, this.children);
+ const affectedChunks = this.getAffectedChunks(compilation, chunks, targetChunk, targetChunks, idx, this.selectedChunks, this.async, this.deepChildren, this.children);
// bail if no chunk is affected
if(!affectedChunks) {
@@ -211,7 +214,37 @@ You can however specify the name of the async chunk by passing the desired strin
Take a look at the "name"/"names" or async/children option.`);
}
- getAffectedChunks(compilation, allChunks, targetChunk, targetChunks, currentIndex, selectedChunks, asyncOption, children) {
+ getAffectedUnnamedChunks(affectedChunks, targetChunk, asyncOption, deepChildrenOption) {
+ for(const chunk of targetChunk.chunksIterable) {
+ if(chunk.isInitial()) {
+ continue;
+ }
+ // If all the parents of a chunk are either
+ // a) the target chunk we started with
+ // b) themselves affected chunks
+ // we can assume that this chunk is an affected chunk too, as there is no way a chunk that
+ // isn't only depending on the target chunk is a parent of the chunk tested
+ if(asyncOption || chunk.parents.every((parentChunk) => parentChunk === targetChunk || affectedChunks.has(parentChunk))) {
+ // This check not only dedupes the affectedChunks but also guarantees we avoid endless loops
+ if(!affectedChunks.has(chunk) || affectedChunks.values().next().value === chunk) {
+ // We mutate the affected chunks before going deeper, so the deeper levels and other branches
+ // Have the information of this chunk being affected for their assertion if a chunk should
+ // not be affected
+ affectedChunks.add(chunk);
+
+ // We recurse down to all the children of the chunk, applying the same assumption.
+ // This guarantees that if a chunk should be an affected chunk,
+ // at the latest the last connection to the same chunk meets the
+ // condition to add it to the affected chunks.
+ if(deepChildrenOption === true) {
+ this.getAffectedUnnamedChunks(affectedChunks, chunk, asyncOption, deepChildrenOption);
+ }
+ }
+ }
+ }
+ }
+
+ getAffectedChunks(compilation, allChunks, targetChunk, targetChunks, currentIndex, selectedChunks, asyncOption, deepChildrenOption, children) {
const asyncOrNoSelectedChunk = children || asyncOption;
if(Array.isArray(selectedChunks)) {
@@ -223,17 +256,9 @@ Take a look at the "name"/"names" or async/children option.`);
}
if(asyncOrNoSelectedChunk) {
- return targetChunk.getChunks().filter((chunk) => {
- // we only are interested in on-demand chunks
- if(chunk.isInitial())
- return false;
-
- // we can only move modules from this chunk if the "commonChunk" is the only parent
- if(!asyncOption)
- return chunk.getNumberOfParents() === 1;
-
- return true;
- });
+ let affectedChunks = new Set();
+ this.getAffectedUnnamedChunks(affectedChunks, targetChunk, asyncOption, deepChildrenOption);
+ return Array.from(affectedChunks);
}
/**
diff --git a/test/cases/chunks/circular-correctness/index.js b/test/cases/chunks/circular-correctness/index.js
new file mode 100644
index 000000000..5db11228d
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/index.js
@@ -0,0 +1,13 @@
+it("should handle circular chunks correctly", function(done) {
+ import(/* webpackChunkName: "a" */"./module-a").then(function(result) {
+ return result.default();
+ }).then(function(result2) {
+ result2.default().should.be.eql("x");
+ done();
+ }).catch(function(e) {
+ done(e);
+ });
+ const couldBe = function() {
+ return import(/* webpackChunkName: "b" */"./module-b");
+ };
+});
diff --git a/test/cases/chunks/circular-correctness/module-a.js b/test/cases/chunks/circular-correctness/module-a.js
new file mode 100644
index 000000000..e1b8b5a9a
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-a.js
@@ -0,0 +1,3 @@
+export default function() {
+ return import(/* webpackChunkName: "c" */"./module-c");
+};
diff --git a/test/cases/chunks/circular-correctness/module-a2.js b/test/cases/chunks/circular-correctness/module-a2.js
new file mode 100644
index 000000000..ca02e52b6
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-a2.js
@@ -0,0 +1 @@
+export default "a2";
diff --git a/test/cases/chunks/circular-correctness/module-b.js b/test/cases/chunks/circular-correctness/module-b.js
new file mode 100644
index 000000000..35d4adf20
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-b.js
@@ -0,0 +1,5 @@
+import "./module-x";
+
+export default function() {
+ return import(/* webpackChunkName: "c" */"./module-c");
+};
diff --git a/test/cases/chunks/circular-correctness/module-b2.js b/test/cases/chunks/circular-correctness/module-b2.js
new file mode 100644
index 000000000..e8a4051b1
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-b2.js
@@ -0,0 +1 @@
+export default "b2";
diff --git a/test/cases/chunks/circular-correctness/module-c.js b/test/cases/chunks/circular-correctness/module-c.js
new file mode 100644
index 000000000..258728f8d
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-c.js
@@ -0,0 +1,9 @@
+import x from "./module-x";
+
+export default function() {
+ if(Math.random() < -1) {
+ import(/* webpackChunkName: "a" */"./module-a");
+ import(/* webpackChunkName: "b" */"./module-b");
+ }
+ return x;
+}
diff --git a/test/cases/chunks/circular-correctness/module-x.js b/test/cases/chunks/circular-correctness/module-x.js
new file mode 100644
index 000000000..3fd5ecc7a
--- /dev/null
+++ b/test/cases/chunks/circular-correctness/module-x.js
@@ -0,0 +1 @@
+export default "x";
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/index.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/index.js
new file mode 100644
index 000000000..098d5781e
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/index.js
@@ -0,0 +1,12 @@
+it("should correctly include indirect children in common chunk", function(done) {
+ Promise.all([
+ import('./pageA'),
+ import('./pageB')
+ ]).then((imports) => {
+ imports[0].default.should.be.eql("reuse");
+ imports[1].default.should.be.eql("reuse");
+ done();
+ }).catch(e => {
+ done(e);
+ })
+});
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/pageA.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageA.js
new file mode 100644
index 000000000..841402d2f
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageA.js
@@ -0,0 +1,3 @@
+var reusableComponent = require("./reusableComponent");
+
+export default reusableComponent.default;
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/pageB.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageB.js
new file mode 100644
index 000000000..0522aa472
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageB.js
@@ -0,0 +1 @@
+module.exports = import('./pageC')
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/pageC.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageC.js
new file mode 100644
index 000000000..841402d2f
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/pageC.js
@@ -0,0 +1,3 @@
+var reusableComponent = require("./reusableComponent");
+
+export default reusableComponent.default;
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/reusableComponent.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/reusableComponent.js
new file mode 100644
index 000000000..99de57881
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/reusableComponent.js
@@ -0,0 +1,3 @@
+const reuse = "reuse";
+
+export default reuse;
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/second.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/second.js
new file mode 100644
index 000000000..c661ef828
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/second.js
@@ -0,0 +1,8 @@
+it("should handle indirect children with multiple parents correctly", function(done) {
+ import('./pageB').then(b => {
+ b.default.should.be.eql("reuse");
+ done()
+ }).catch(e => {
+ done();
+ })
+})
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/test.config.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/test.config.js
new file mode 100644
index 000000000..4c2a87b6f
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/test.config.js
@@ -0,0 +1,8 @@
+module.exports = {
+ findBundle: function(i, options) {
+ return [
+ "./main.js",
+ "./misc.js",
+ ];
+ }
+};
diff --git a/test/configCases/commons-chunk-plugin/move-to-grandparent/webpack.config.js b/test/configCases/commons-chunk-plugin/move-to-grandparent/webpack.config.js
new file mode 100644
index 000000000..78c0a2c38
--- /dev/null
+++ b/test/configCases/commons-chunk-plugin/move-to-grandparent/webpack.config.js
@@ -0,0 +1,19 @@
+var CommonsChunkPlugin = require("../../../../lib/optimize/CommonsChunkPlugin");
+
+module.exports = {
+ entry: {
+ main: "./index",
+ misc: "./second",
+ },
+ output: {
+ filename: "[name].js"
+ },
+ plugins: [
+ new CommonsChunkPlugin({
+ name: "main",
+ minChunks: 2,
+ children: true,
+ deepChildren: true,
+ })
+ ]
+};
diff --git a/test/configCases/externals/non-umd-externals-umd/index.js b/test/configCases/externals/non-umd-externals-umd/index.js
new file mode 100644
index 000000000..9ef058ee9
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd/index.js
@@ -0,0 +1,23 @@
+require("should");
+var fs = require("fs");
+var path = require("path");
+
+it("should correctly import a UMD external", function() {
+ var external = require("external0");
+ external.should.be.eql("module 0");
+});
+
+it("should contain `require()` statements for the UMD external", function() {
+ var source = fs.readFileSync(path.join(__dirname, "bundle0.js"), "utf-8");
+ source.should.containEql("require(\"external0\")");
+});
+
+it("should correctly import a non-UMD external", function() {
+ var external = require("external1");
+ external.should.be.eql("abc");
+});
+
+it("should not contain `require()` statements for the non-UMD external", function() {
+ var source = fs.readFileSync(path.join(__dirname, "bundle0.js"), "utf-8");
+ source.should.not.containEql("require(\"'abc'\")");
+});
diff --git a/test/configCases/externals/non-umd-externals-umd/test.config.js b/test/configCases/externals/non-umd-externals-umd/test.config.js
new file mode 100644
index 000000000..680a119a5
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd/test.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ modules: {
+ external0: "module 0"
+ }
+};
diff --git a/test/configCases/externals/non-umd-externals-umd/webpack.config.js b/test/configCases/externals/non-umd-externals-umd/webpack.config.js
new file mode 100644
index 000000000..acbfaa925
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd/webpack.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ output: {
+ libraryTarget: "umd"
+ },
+ externals: {
+ external0: "external0",
+ external1: "var 'abc'"
+ },
+ node: {
+ __dirname: false,
+ __filename: false
+ }
+};
diff --git a/test/configCases/externals/non-umd-externals-umd2/index.js b/test/configCases/externals/non-umd-externals-umd2/index.js
new file mode 100644
index 000000000..fdb4a1f50
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd2/index.js
@@ -0,0 +1,23 @@
+require("should");
+var fs = require("fs");
+var path = require("path");
+
+it("should correctly import a UMD2 external", function() {
+ var external = require("external0");
+ external.should.be.eql("module 0");
+});
+
+it("should contain `require()` statements for the UMD2 external", function() {
+ var source = fs.readFileSync(path.join(__dirname, "bundle0.js"), "utf-8");
+ source.should.containEql("require(\"external0\")");
+});
+
+it("should correctly import a non-UMD2 external", function() {
+ var external = require("external1");
+ external.should.be.eql("abc");
+});
+
+it("should not contain `require()` statements for the non-UMD2 external", function() {
+ var source = fs.readFileSync(path.join(__dirname, "bundle0.js"), "utf-8");
+ source.should.not.containEql("require(\"'abc'\")");
+});
diff --git a/test/configCases/externals/non-umd-externals-umd2/test.config.js b/test/configCases/externals/non-umd-externals-umd2/test.config.js
new file mode 100644
index 000000000..680a119a5
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd2/test.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ modules: {
+ external0: "module 0"
+ }
+};
diff --git a/test/configCases/externals/non-umd-externals-umd2/webpack.config.js b/test/configCases/externals/non-umd-externals-umd2/webpack.config.js
new file mode 100644
index 000000000..edca25ee9
--- /dev/null
+++ b/test/configCases/externals/non-umd-externals-umd2/webpack.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ output: {
+ libraryTarget: "umd2"
+ },
+ externals: {
+ external0: "external0",
+ external1: "var 'abc'"
+ },
+ node: {
+ __dirname: false,
+ __filename: false
+ }
+};
diff --git a/test/statsCases/circular-correctness/expected.txt b/test/statsCases/circular-correctness/expected.txt
new file mode 100644
index 000000000..1ec9ed685
--- /dev/null
+++ b/test/statsCases/circular-correctness/expected.txt
@@ -0,0 +1,8 @@
+chunk {0} 0.bundle.js (b) 49 bytes {2} {3} [rendered]
+ [2] (webpack)/test/statsCases/circular-correctness/module-b.js 49 bytes {0} [built]
+chunk {1} 1.bundle.js (a) 49 bytes {2} {3} [rendered]
+ [0] (webpack)/test/statsCases/circular-correctness/module-a.js 49 bytes {1} [built]
+chunk {2} 2.bundle.js (c) 98 bytes {0} {1} [rendered]
+ [1] (webpack)/test/statsCases/circular-correctness/module-c.js 98 bytes {2} [built]
+chunk {3} bundle.js (main) 98 bytes [entry] [rendered]
+ [3] (webpack)/test/statsCases/circular-correctness/index.js 98 bytes {3} [built]
\ No newline at end of file
diff --git a/test/statsCases/circular-correctness/index.js b/test/statsCases/circular-correctness/index.js
new file mode 100644
index 000000000..ad3fdb303
--- /dev/null
+++ b/test/statsCases/circular-correctness/index.js
@@ -0,0 +1,2 @@
+import(/* webpackChunkName: "a" */"./module-a");
+import(/* webpackChunkName: "b" */"./module-b");
diff --git a/test/statsCases/circular-correctness/module-a.js b/test/statsCases/circular-correctness/module-a.js
new file mode 100644
index 000000000..00f49659c
--- /dev/null
+++ b/test/statsCases/circular-correctness/module-a.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "c" */"./module-c");
diff --git a/test/statsCases/circular-correctness/module-a2.js b/test/statsCases/circular-correctness/module-a2.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/statsCases/circular-correctness/module-b.js b/test/statsCases/circular-correctness/module-b.js
new file mode 100644
index 000000000..00f49659c
--- /dev/null
+++ b/test/statsCases/circular-correctness/module-b.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "c" */"./module-c");
diff --git a/test/statsCases/circular-correctness/module-b2.js b/test/statsCases/circular-correctness/module-b2.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/statsCases/circular-correctness/module-c.js b/test/statsCases/circular-correctness/module-c.js
new file mode 100644
index 000000000..ad3fdb303
--- /dev/null
+++ b/test/statsCases/circular-correctness/module-c.js
@@ -0,0 +1,2 @@
+import(/* webpackChunkName: "a" */"./module-a");
+import(/* webpackChunkName: "b" */"./module-b");
diff --git a/test/statsCases/circular-correctness/webpack.config.js b/test/statsCases/circular-correctness/webpack.config.js
new file mode 100644
index 000000000..fa898887b
--- /dev/null
+++ b/test/statsCases/circular-correctness/webpack.config.js
@@ -0,0 +1,14 @@
+module.exports = {
+ entry: "./index",
+ output: {
+ filename: "bundle.js"
+ },
+ stats: {
+ hash: false,
+ timings: false,
+ assets: false,
+ chunks: true,
+ chunkModules: true,
+ modules: false
+ }
+};
diff --git a/test/statsCases/graph-correctness-entries/e1.js b/test/statsCases/graph-correctness-entries/e1.js
new file mode 100644
index 000000000..c16911388
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/e1.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "a" */"./module-a");
diff --git a/test/statsCases/graph-correctness-entries/e2.js b/test/statsCases/graph-correctness-entries/e2.js
new file mode 100644
index 000000000..00f49659c
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/e2.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "c" */"./module-c");
diff --git a/test/statsCases/graph-correctness-entries/expected.txt b/test/statsCases/graph-correctness-entries/expected.txt
new file mode 100644
index 000000000..69cc11941
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/expected.txt
@@ -0,0 +1,15 @@
+chunk {0} 0.js (c) 49 bytes {2} {3} [rendered]
+ [1] (webpack)/test/statsCases/graph-correctness-entries/module-c.js 49 bytes {0} [built]
+ import() ./module-c [3] (webpack)/test/statsCases/graph-correctness-entries/module-b.js 1:0-47
+ import() ./module-c [4] (webpack)/test/statsCases/graph-correctness-entries/e2.js 1:0-47
+chunk {1} 1.js (a) 49 bytes {0} {4} [rendered]
+ [0] (webpack)/test/statsCases/graph-correctness-entries/module-a.js 49 bytes {1} [built]
+ import() ./module-a [1] (webpack)/test/statsCases/graph-correctness-entries/module-c.js 1:0-47
+ import() ./module-a [2] (webpack)/test/statsCases/graph-correctness-entries/e1.js 1:0-47
+chunk {2} 2.js (b) 49 bytes {1} [rendered]
+ [3] (webpack)/test/statsCases/graph-correctness-entries/module-b.js 49 bytes {2} [built]
+ import() ./module-b [0] (webpack)/test/statsCases/graph-correctness-entries/module-a.js 1:0-47
+chunk {3} e2.js (e2) 49 bytes [entry] [rendered]
+ [4] (webpack)/test/statsCases/graph-correctness-entries/e2.js 49 bytes {3} [built]
+chunk {4} e1.js (e1) 49 bytes [entry] [rendered]
+ [2] (webpack)/test/statsCases/graph-correctness-entries/e1.js 49 bytes {4} [built]
\ No newline at end of file
diff --git a/test/statsCases/graph-correctness-entries/module-a.js b/test/statsCases/graph-correctness-entries/module-a.js
new file mode 100644
index 000000000..3d4670c07
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/module-a.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "b" */"./module-b");
diff --git a/test/statsCases/graph-correctness-entries/module-b.js b/test/statsCases/graph-correctness-entries/module-b.js
new file mode 100644
index 000000000..00f49659c
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/module-b.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "c" */"./module-c");
diff --git a/test/statsCases/graph-correctness-entries/module-c.js b/test/statsCases/graph-correctness-entries/module-c.js
new file mode 100644
index 000000000..c16911388
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/module-c.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "a" */"./module-a");
diff --git a/test/statsCases/graph-correctness-entries/webpack.config.js b/test/statsCases/graph-correctness-entries/webpack.config.js
new file mode 100644
index 000000000..1caf9866c
--- /dev/null
+++ b/test/statsCases/graph-correctness-entries/webpack.config.js
@@ -0,0 +1,18 @@
+module.exports = {
+ entry: {
+ e1: "./e1",
+ e2: "./e2"
+ },
+ output: {
+ filename: "[name].js"
+ },
+ stats: {
+ hash: false,
+ timings: false,
+ assets: false,
+ chunks: true,
+ chunkModules: true,
+ modules: false,
+ reasons: true
+ }
+};
diff --git a/test/statsCases/graph-correctness-modules/e1.js b/test/statsCases/graph-correctness-modules/e1.js
new file mode 100644
index 000000000..3a1a4a3b1
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/e1.js
@@ -0,0 +1,2 @@
+import "./module-x";
+import(/* webpackChunkName: "a" */"./module-a");
diff --git a/test/statsCases/graph-correctness-modules/e2.js b/test/statsCases/graph-correctness-modules/e2.js
new file mode 100644
index 000000000..beec427df
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/e2.js
@@ -0,0 +1,2 @@
+import "./module-x";
+import(/* webpackChunkName: "c" */"./module-c");
diff --git a/test/statsCases/graph-correctness-modules/expected.txt b/test/statsCases/graph-correctness-modules/expected.txt
new file mode 100644
index 000000000..99adda24a
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/expected.txt
@@ -0,0 +1,26 @@
+chunk {0} 0.js (y) 0 bytes {4} {5} [rendered]
+ [1] (webpack)/test/statsCases/graph-correctness-modules/module-y.js 0 bytes {0} [built]
+ import() ./module-y [0] (webpack)/test/statsCases/graph-correctness-modules/module-x.js 1:0-47
+chunk {1} 1.js (c) 49 bytes {3} {4} [rendered]
+ [3] (webpack)/test/statsCases/graph-correctness-modules/module-c.js 49 bytes {1} [built]
+ import() ./module-c [5] (webpack)/test/statsCases/graph-correctness-modules/module-b.js 1:0-47
+ import() ./module-c [6] (webpack)/test/statsCases/graph-correctness-modules/e2.js 2:0-47
+chunk {2} 2.js (a) 49 bytes {1} {5} [rendered]
+ [2] (webpack)/test/statsCases/graph-correctness-modules/module-a.js 49 bytes {2} [built]
+ import() ./module-a [3] (webpack)/test/statsCases/graph-correctness-modules/module-c.js 1:0-47
+ import() ./module-a [4] (webpack)/test/statsCases/graph-correctness-modules/e1.js 2:0-47
+chunk {3} 3.js (b) 179 bytes {2} [rendered]
+ [5] (webpack)/test/statsCases/graph-correctness-modules/module-b.js 179 bytes {3} [built]
+ import() ./module-b [2] (webpack)/test/statsCases/graph-correctness-modules/module-a.js 1:0-47
+chunk {4} e2.js (e2) 119 bytes [entry] [rendered]
+ [0] (webpack)/test/statsCases/graph-correctness-modules/module-x.js 49 bytes {4} {5} [built]
+ harmony import ./module-x [4] (webpack)/test/statsCases/graph-correctness-modules/e1.js 1:0-20
+ import() ./module-x [5] (webpack)/test/statsCases/graph-correctness-modules/module-b.js 2:0-20
+ harmony import ./module-x [6] (webpack)/test/statsCases/graph-correctness-modules/e2.js 1:0-20
+ [6] (webpack)/test/statsCases/graph-correctness-modules/e2.js 70 bytes {4} [built]
+chunk {5} e1.js (e1) 119 bytes [entry] [rendered]
+ [0] (webpack)/test/statsCases/graph-correctness-modules/module-x.js 49 bytes {4} {5} [built]
+ harmony import ./module-x [4] (webpack)/test/statsCases/graph-correctness-modules/e1.js 1:0-20
+ import() ./module-x [5] (webpack)/test/statsCases/graph-correctness-modules/module-b.js 2:0-20
+ harmony import ./module-x [6] (webpack)/test/statsCases/graph-correctness-modules/e2.js 1:0-20
+ [4] (webpack)/test/statsCases/graph-correctness-modules/e1.js 70 bytes {5} [built]
\ No newline at end of file
diff --git a/test/statsCases/graph-correctness-modules/module-a.js b/test/statsCases/graph-correctness-modules/module-a.js
new file mode 100644
index 000000000..3d4670c07
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/module-a.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "b" */"./module-b");
diff --git a/test/statsCases/graph-correctness-modules/module-b.js b/test/statsCases/graph-correctness-modules/module-b.js
new file mode 100644
index 000000000..fdee113dc
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/module-b.js
@@ -0,0 +1,2 @@
+import(/* webpackChunkName: "c" */"./module-c");
+import("./module-x"); // This should not create a chunk, because module-x is in both entrypoints (in every path to this module-b)
diff --git a/test/statsCases/graph-correctness-modules/module-c.js b/test/statsCases/graph-correctness-modules/module-c.js
new file mode 100644
index 000000000..c16911388
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/module-c.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "a" */"./module-a");
diff --git a/test/statsCases/graph-correctness-modules/module-x.js b/test/statsCases/graph-correctness-modules/module-x.js
new file mode 100644
index 000000000..5379126ff
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/module-x.js
@@ -0,0 +1 @@
+import(/* webpackChunkName: "y" */"./module-y");
diff --git a/test/statsCases/graph-correctness-modules/module-y.js b/test/statsCases/graph-correctness-modules/module-y.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/test/statsCases/graph-correctness-modules/webpack.config.js b/test/statsCases/graph-correctness-modules/webpack.config.js
new file mode 100644
index 000000000..1caf9866c
--- /dev/null
+++ b/test/statsCases/graph-correctness-modules/webpack.config.js
@@ -0,0 +1,18 @@
+module.exports = {
+ entry: {
+ e1: "./e1",
+ e2: "./e2"
+ },
+ output: {
+ filename: "[name].js"
+ },
+ stats: {
+ hash: false,
+ timings: false,
+ assets: false,
+ chunks: true,
+ chunkModules: true,
+ modules: false,
+ reasons: true
+ }
+};