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 + } +};