Merge pull request #5007 from webpack/next

webpack 3
This commit is contained in:
Tobias Koppers 2017-06-05 15:50:18 +02:00 committed by GitHub
commit 463eeb52e0
183 changed files with 2974 additions and 1132 deletions

View File

@ -4,6 +4,7 @@ language: node_js
branches:
only:
- master
- next
cache:
directories:

View File

@ -4,6 +4,7 @@
branches:
only:
- master
- next
init:
- git config --global core.autocrlf input

76
bin/webpack.js Executable file → Normal file
View File

@ -124,11 +124,21 @@ yargs.options({
group: DISPLAY_GROUP,
describe: "Display information about exports provided from modules"
},
"display-optimization-bailout": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display information about why optimization bailed out for modules"
},
"display-error-details": {
type: "boolean",
group: DISPLAY_GROUP,
describe: "Display details about errors"
},
"display": {
type: "string",
group: DISPLAY_GROUP,
describe: "Select display preset (verbose, detailed, normal, minimal, errors-only, none)"
},
"verbose": {
type: "boolean",
group: DISPLAY_GROUP,
@ -139,15 +149,7 @@ yargs.options({
var argv = yargs.argv;
if(argv.verbose) {
argv["display-reasons"] = true;
argv["display-depth"] = true;
argv["display-entrypoints"] = true;
argv["display-used-exports"] = true;
argv["display-provided-exports"] = true;
argv["display-error-details"] = true;
argv["display-modules"] = true;
argv["display-cached"] = true;
argv["display-cached-assets"] = true;
argv["display"] = "verbose";
}
var options = require("./convert-argv")(yargs, argv);
@ -181,6 +183,11 @@ function processOptions(options) {
} else if(!outputOptions) {
outputOptions = {};
}
ifArg("display", function(preset) {
outputOptions = statsPresetToOptions(preset);
});
outputOptions = Object.create(outputOptions);
if(Array.isArray(options) && !outputOptions.children) {
outputOptions.children = options.map(o => o.stats);
@ -219,36 +226,50 @@ function processOptions(options) {
outputOptions.cachedAssets = false;
ifArg("display-chunks", function(bool) {
outputOptions.modules = !bool;
outputOptions.chunks = bool;
if(bool) {
outputOptions.modules = false;
outputOptions.chunks = true;
outputOptions.chunkModules = true;
}
});
ifArg("display-entrypoints", function(bool) {
outputOptions.entrypoints = bool;
if(bool)
outputOptions.entrypoints = true;
});
ifArg("display-reasons", function(bool) {
outputOptions.reasons = bool;
if(bool)
outputOptions.reasons = true;
});
ifArg("display-depth", function(bool) {
outputOptions.depth = bool;
if(bool)
outputOptions.depth = true;
});
ifArg("display-used-exports", function(bool) {
outputOptions.usedExports = bool;
if(bool)
outputOptions.usedExports = true;
});
ifArg("display-provided-exports", function(bool) {
outputOptions.providedExports = bool;
if(bool)
outputOptions.providedExports = true;
});
ifArg("display-optimization-bailout", function(bool) {
outputOptions.optimizationBailout = bool;
});
ifArg("display-error-details", function(bool) {
outputOptions.errorDetails = bool;
if(bool)
outputOptions.errorDetails = true;
});
ifArg("display-origins", function(bool) {
outputOptions.chunkOrigins = bool;
if(bool)
outputOptions.chunkOrigins = true;
});
ifArg("display-max-modules", function(value) {
@ -272,21 +293,6 @@ function processOptions(options) {
outputOptions.maxModules = Infinity;
outputOptions.exclude = undefined;
}
} else {
if(typeof outputOptions.chunks === "undefined")
outputOptions.chunks = true;
if(typeof outputOptions.entrypoints === "undefined")
outputOptions.entrypoints = true;
if(typeof outputOptions.modules === "undefined")
outputOptions.modules = true;
if(typeof outputOptions.chunkModules === "undefined")
outputOptions.chunkModules = true;
if(typeof outputOptions.reasons === "undefined")
outputOptions.reasons = true;
if(typeof outputOptions.cached === "undefined")
outputOptions.cached = true;
if(typeof outputOptions.cachedAssets === "undefined")
outputOptions.cachedAssets = true;
}
ifArg("hide-modules", function(bool) {
@ -337,7 +343,9 @@ function processOptions(options) {
process.stdout.write(JSON.stringify(stats.toJson(outputOptions), null, 2) + "\n");
} else if(stats.hash !== lastHash) {
lastHash = stats.hash;
process.stdout.write(stats.toString(outputOptions) + "\n");
var statsString = stats.toString(outputOptions);
if(statsString)
process.stdout.write(statsString + "\n");
}
if(!options.watch && stats.hasErrors()) {
process.on("exit", function() {

View File

@ -0,0 +1,459 @@
This example demonstrates Scope Hoisting in combination with Code Splitting.
This is the dependency graph for the example: (solid lines express sync imports, dashed lines async imports)
![](graph.png)
All modules except `cjs` are EcmaScript modules. `cjs` is a CommonJs module.
The interesting thing here is that putting all modules in single scope won't work, because of multiple reasons:
* Modules `lazy`, `c`, `d` and `cjs` need to be in a separate chunk
* Module `shared` is accessed by two chunks (different scopes)
* Module `cjs` is a CommonJs module
![](graph2.png)
webpack therefore uses a approach called **"Partial Scope Hoisting"** or "Module concatenation", which chooses the largest possible subsets of ES modules which can be scope hoisted and combines them with the default webpack primitives.
![](graph3.png)
While module concatentation identifiers in modules are renamed to avoid conflicts and internal imports are simplified. External imports and exports from the root module use the existing ESM constructs.
# example.js
``` javascript
import { a, x, y } from "a";
import * as b from "b";
import("./lazy").then(function(lazy) {
console.log(a, b.a(), x, y, lazy.c, lazy.d.a, lazy.x, lazy.y);
});
```
# lazy.js
``` javascript
export * from "c";
import * as d from "d";
export { d };
```
# a.js
``` javascript
// module a
export var a = "a";
export * from "shared";
```
# b.js
``` javascript
// module b
export function a() {
return "b";
};
```
# c.js
``` javascript
// module c
import { c as e } from "cjs";
export var c = String.fromCharCode(e.charCodeAt(0) - 2);
export { x, y } from "shared";
```
# d.js
``` javascript
// module d
export var a = "d";
```
# cjs.js
``` javascript
// module cjs (commonjs)
exports.c = "e";
```
# shared.js
``` javascript
// shared module
export var x = "x";
export * from "shared2";
```
# shared2.js
``` javascript
// shared2 module
export var y = "y";
```
# webpack.config.js
``` javascript
var webpack = require("../../");
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};
```
# js/output.js
<details><summary><code>/******/ (function(modules) { /* webpackBootstrap */ })</code></summary>
``` 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 = {
/******/ 1: 0
/******/ };
/******/
/******/ var resolvedPromise = new Promise(function(resolve) { resolve(); });
/******/
/******/ // 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) {
/******/ if(installedChunks[chunkId] === 0) {
/******/ return resolvedPromise;
/******/ }
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunks[chunkId]) {
/******/ return installedChunks[chunkId][2];
/******/ }
/******/
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunks[chunkId] = [resolve, reject];
/******/ });
/******/ installedChunks[chunkId][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;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "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);
/******/ })
/************************************************************************/
```
</details>
``` javascript
/******/ ([
/* 0 */
/* unknown exports provided */
/* all exports used */
/*!********************************!*\
!*** ./example.js + 2 modules ***!
\********************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// CONCATENAMED MODULE: ./node_modules/a.js
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_shared__ = __webpack_require__(/*! shared */ 1);
// module a
var a = "a";
// CONCATENAMED MODULE: ./node_modules/b.js
// module b
function b_js_a() {
return "b";
};
// CONCATENAMED MODULE: ./example.js
__webpack_require__.e/* import() */(0).then(__webpack_require__.bind(null, /*! ./lazy */ 2)).then(function(lazy) {
console.log(a, b_js_a(), __WEBPACK_IMPORTED_MODULE_0_shared__["a"], __WEBPACK_IMPORTED_MODULE_0_shared__["b"], lazy.c, lazy.d.a, lazy.x, lazy.y);
});
/***/ }),
/* 1 */
/* exports provided: x, y */
/* exports used: x, y */
/*!********************************************!*\
!*** ./node_modules/shared.js + 1 modules ***!
\********************************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// CONCATENAMED MODULE: ./node_modules/shared2.js
// shared2 module
var y = "y";
// CONCATENAMED MODULE: ./node_modules/shared.js
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return x; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "b", function() { return y; });
// shared module
var x = "x";
/***/ })
/******/ ]);
```
# js/0.output.js
``` javascript
webpackJsonp([0],[
/* 0 */,
/* 1 */,
/* 2 */
/* exports provided: d, c, x, y */
/* all exports used */
/*!*****************************!*\
!*** ./lazy.js + 2 modules ***!
\*****************************/
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// CONCATENAMED MODULE: ./node_modules/c.js
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_cjs__ = __webpack_require__(/*! cjs */ 3);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_cjs___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_cjs__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_shared__ = __webpack_require__(/*! shared */ 1);
// module c
var c = String.fromCharCode(__WEBPACK_IMPORTED_MODULE_0_cjs__["c"].charCodeAt(0) - 2);
// CONCATENAMED MODULE: ./node_modules/d.js
var d_js_namespaceObject = {};
__webpack_require__.d(d_js_namespaceObject, "a", function() { return a; });
// module d
var a = "d";
// CONCATENAMED MODULE: ./lazy.js
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "c", function() { return c; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "x", function() { return __WEBPACK_IMPORTED_MODULE_1_shared__["a"]; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "y", function() { return __WEBPACK_IMPORTED_MODULE_1_shared__["b"]; });
/* concated harmony reexport */__webpack_require__.d(__webpack_exports__, "d", function() { return d_js_namespaceObject; });
/***/ }),
/* 3 */
/* unknown exports provided */
/* exports used: c */
/*!*****************************!*\
!*** ./node_modules/cjs.js ***!
\*****************************/
/***/ (function(module, exports) {
// module cjs (commonjs)
exports.c = "e";
/***/ })
]);
```
Minimized
``` javascript
webpackJsonp([0],[,,function(n,r,t){"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=t(3),u=(t.n(e),t(1)),c=String.fromCharCode(e.c.charCodeAt(0)-2),o={};t.d(o,"a",function(){return d});var d="d";t.d(r,"c",function(){return c}),t.d(r,"x",function(){return u.a}),t.d(r,"y",function(){return u.b}),t.d(r,"d",function(){return o})},function(n,r){r.c="e"}]);
```
# Info
## Uncompressed
```
Hash: 4091e96718e53f8b98fd
Version: webpack 2.5.1
Asset Size Chunks Chunk Names
0.output.js 1.85 kB 0 [emitted]
output.js 7.53 kB 1 [emitted] main
Entrypoint main = output.js
chunk {0} 0.output.js 276 bytes {1} [rendered]
> [4] 4:0-16
[2] ./lazy.js + 2 modules 232 bytes {0} [built]
[exports: d, c, x, y]
import() ./lazy [4] ./example.js 4:0-16
[3] ./node_modules/cjs.js 44 bytes {0} [built]
[only some exports used: c]
harmony import cjs [8] ./node_modules/c.js 2:0-29
chunk {1} output.js (main) 385 bytes [entry] [rendered]
> main [4]
[0] ./example.js + 2 modules 280 bytes {1} [built]
[1] ./node_modules/shared.js + 1 modules 105 bytes {1} [built]
[exports: x, y]
[only some exports used: x, y]
harmony import shared [6] ./node_modules/a.js 3:0-23
harmony import shared [8] ./node_modules/c.js 6:0-30
```
## Minimized (uglify-js, no zip)
```
Hash: 4091e96718e53f8b98fd
Version: webpack 2.5.1
Asset Size Chunks Chunk Names
0.output.js 379 bytes 0 [emitted]
output.js 1.73 kB 1 [emitted] main
Entrypoint main = output.js
chunk {0} 0.output.js 276 bytes {1} [rendered]
> [4] 4:0-16
[2] ./lazy.js + 2 modules 232 bytes {0} [built]
[exports: d, c, x, y]
import() ./lazy [4] ./example.js 4:0-16
[3] ./node_modules/cjs.js 44 bytes {0} [built]
[only some exports used: c]
harmony import cjs [8] ./node_modules/c.js 2:0-29
chunk {1} output.js (main) 385 bytes [entry] [rendered]
> main [4]
[0] ./example.js + 2 modules 280 bytes {1} [built]
[1] ./node_modules/shared.js + 1 modules 105 bytes {1} [built]
[exports: x, y]
[only some exports used: x, y]
harmony import shared [6] ./node_modules/a.js 3:0-23
harmony import shared [8] ./node_modules/c.js 6:0-30
```

View File

@ -0,0 +1 @@
require("../build-common");

View File

@ -0,0 +1,6 @@
import { a, x, y } from "a";
import * as b from "b";
import("./lazy").then(function(lazy) {
console.log(a, b.a(), x, y, lazy.c, lazy.d.a, lazy.x, lazy.y);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -0,0 +1,3 @@
export * from "c";
import * as d from "d";
export { d };

3
examples/scope-hoisting/node_modules/a.js generated vendored Normal file
View File

@ -0,0 +1,3 @@
// module a
export var a = "a";
export * from "shared";

4
examples/scope-hoisting/node_modules/b.js generated vendored Normal file
View File

@ -0,0 +1,4 @@
// module b
export function a() {
return "b";
};

6
examples/scope-hoisting/node_modules/c.js generated vendored Normal file
View File

@ -0,0 +1,6 @@
// module c
import { c as e } from "cjs";
export var c = String.fromCharCode(e.charCodeAt(0) - 2);
export { x, y } from "shared";

2
examples/scope-hoisting/node_modules/cjs.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
// module cjs (commonjs)
exports.c = "e";

2
examples/scope-hoisting/node_modules/d.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
// module d
export var a = "d";

3
examples/scope-hoisting/node_modules/shared.js generated vendored Normal file
View File

@ -0,0 +1,3 @@
// shared module
export var x = "x";
export * from "shared2";

2
examples/scope-hoisting/node_modules/shared2.js generated vendored Normal file
View File

@ -0,0 +1,2 @@
// shared2 module
export var y = "y";

View File

@ -0,0 +1,118 @@
This example demonstrates Scope Hoisting in combination with Code Splitting.
This is the dependency graph for the example: (solid lines express sync imports, dashed lines async imports)
![](graph.png)
All modules except `cjs` are EcmaScript modules. `cjs` is a CommonJs module.
The interesting thing here is that putting all modules in single scope won't work, because of multiple reasons:
* Modules `lazy`, `c`, `d` and `cjs` need to be in a separate chunk
* Module `shared` is accessed by two chunks (different scopes)
* Module `cjs` is a CommonJs module
![](graph2.png)
webpack therefore uses a approach called **"Partial Scope Hoisting"** or "Module concatenation", which chooses the largest possible subsets of ES modules which can be scope hoisted and combines them with the default webpack primitives.
![](graph3.png)
While module concatentation identifiers in modules are renamed to avoid conflicts and internal imports are simplified. External imports and exports from the root module use the existing ESM constructs.
# example.js
``` javascript
{{example.js}}
```
# lazy.js
``` javascript
{{lazy.js}}
```
# a.js
``` javascript
{{node_modules/a.js}}
```
# b.js
``` javascript
{{node_modules/b.js}}
```
# c.js
``` javascript
{{node_modules/c.js}}
```
# d.js
``` javascript
{{node_modules/d.js}}
```
# cjs.js
``` javascript
{{node_modules/cjs.js}}
```
# shared.js
``` javascript
{{node_modules/shared.js}}
```
# shared2.js
``` javascript
{{node_modules/shared2.js}}
```
# webpack.config.js
``` javascript
{{webpack.config.js}}
```
# js/output.js
``` javascript
{{js/output.js}}
```
# js/0.output.js
``` javascript
{{js/0.output.js}}
```
Minimized
``` javascript
{{min:js/0.output.js}}
```
# Info
## Uncompressed
```
{{stdout}}
```
## Minimized (uglify-js, no zip)
```
{{min:stdout}}
```

View File

@ -0,0 +1,7 @@
var webpack = require("../../");
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
};

View File

@ -37,17 +37,7 @@ module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
sortItems() {
super.sortItems();
if(this.chunks) {
this.chunks.sort((a, b) => {
let i = 0;
while(true) { // eslint-disable-line no-constant-condition
if(!a.modules[i] && !b.modules[i]) return 0;
if(!a.modules[i]) return -1;
if(!b.modules[i]) return 1;
if(a.modules[i].id > b.modules[i].id) return 1;
if(a.modules[i].id < b.modules[i].id) return -1;
i++;
}
});
this.chunks.sort((a, b) => a.compareTo(b));
}
}
};

View File

@ -4,6 +4,7 @@
*/
"use strict";
const util = require("util");
const compareLocations = require("./compareLocations");
let debugId = 1000;
@ -20,7 +21,8 @@ class Chunk {
this.ids = null;
this.debugId = debugId++;
this.name = name;
this.modules = [];
this._modules = new Set();
this._modulesIsSorted = true;
this.entrypoints = [];
this.chunks = [];
this.parents = [];
@ -88,7 +90,12 @@ class Chunk {
}
addModule(module) {
return this.addToCollection(this.modules, module);
if(!this._modules.has(module)) {
this._modules.add(module);
this._modulesIsSorted = false;
return true;
}
return false;
}
addBlock(block) {
@ -96,9 +103,7 @@ class Chunk {
}
removeModule(module) {
const idx = this.modules.indexOf(module);
if(idx >= 0) {
this.modules.splice(idx, 1);
if(this._modules.delete(module)) {
module.removeChunk(this);
return true;
}
@ -133,9 +138,76 @@ class Chunk {
});
}
setModules(modules) {
this._modules = new Set(modules);
this._modulesIsSorted = false;
}
getNumberOfModules() {
return this._modules.size;
}
get modulesIterable() {
return this._modules;
}
forEachModule(fn) {
this._modules.forEach(fn);
}
mapModules(fn) {
return Array.from(this._modules, fn);
}
_ensureModulesSorted() {
if(this._modulesIsSorted) return;
this._modules = new Set(Array.from(this._modules).sort((a, b) => {
if(a.identifier() > b.identifier()) return 1;
if(a.identifier() < b.identifier()) return -1;
return 0;
}));
this._modulesIsSorted = true;
}
compareTo(otherChunk) {
this._ensureModulesSorted();
otherChunk._ensureModulesSorted();
if(this._modules.size > otherChunk._modules.size) return -1;
if(this._modules.size < otherChunk._modules.size) return 1;
const a = this._modules[Symbol.iterator]();
const b = otherChunk._modules[Symbol.iterator]();
while(true) { // eslint-disable-line
const aItem = a.next();
const bItem = b.next();
if(aItem.done) return 0;
const aModuleIdentifier = aItem.value.identifier();
const bModuleIdentifier = bItem.value.identifier();
if(aModuleIdentifier > bModuleIdentifier) return -1;
if(aModuleIdentifier < bModuleIdentifier) return 1;
}
}
containsModule(module) {
return this._modules.has(module);
}
getModules() {
return Array.from(this._modules);
}
getModulesIdent() {
this._ensureModulesSorted();
let str = "";
this._modules.forEach(m => {
str += m.identifier() + "#";
});
return str;
}
remove(reason) {
// cleanup modules
this.modules.slice().forEach(module => {
// Array.from is used here to create a clone, because removeChunk modifies this._modules
Array.from(this._modules).forEach(module => {
module.removeChunk(this);
});
@ -219,9 +291,10 @@ class Chunk {
return false;
}
const otherChunkModules = otherChunk.modules.slice();
// Array.from is used here to create a clone, because moveModule modifies otherChunk._modules
const otherChunkModules = Array.from(otherChunk._modules);
otherChunkModules.forEach(module => otherChunk.moveModule(module, this));
otherChunk.modules.length = 0;
otherChunk._modules.clear();
otherChunk.parents.forEach(parentChunk => parentChunk.replaceChunk(otherChunk, this));
otherChunk.parents.length = 0;
@ -276,14 +349,14 @@ class Chunk {
}
isEmpty() {
return this.modules.length === 0;
return this._modules.size === 0;
}
updateHash(hash) {
hash.update(`${this.id} `);
hash.update(this.ids ? this.ids.join(",") : "");
hash.update(`${this.name || ""} `);
this.modules.forEach(m => m.updateHash(hash));
this._modules.forEach(m => m.updateHash(hash));
}
canBeIntegrated(otherChunk) {
@ -307,8 +380,8 @@ class Chunk {
modulesSize() {
let count = 0;
for(let i = 0; i < this.modules.length; i++) {
count += this.modules[i].size();
for(const module of this._modules) {
count += module.size();
}
return count;
}
@ -325,9 +398,8 @@ class Chunk {
let integratedModulesSize = this.modulesSize();
// only count modules that do not exist in this chunk!
for(let i = 0; i < otherChunk.modules.length; i++) {
const otherModule = otherChunk.modules[i];
if(this.modules.indexOf(otherModule) === -1) {
for(const otherModule of otherChunk._modules) {
if(!this._modules.has(otherModule)) {
integratedModulesSize += otherModule.size();
}
}
@ -356,7 +428,7 @@ class Chunk {
}
sortItems() {
this.modules.sort(byId);
this._modules = new Set(Array.from(this._modules).sort(byId));
this.origins.sort((a, b) => {
const aIdent = a.module.identifier();
const bIdent = b.module.identifier();
@ -373,7 +445,7 @@ class Chunk {
}
toString() {
return `Chunk[${this.modules.join()}]`;
return `Chunk[${Array.from(this._modules).join()}]`;
}
checkConstraints() {
@ -393,4 +465,14 @@ class Chunk {
}
}
Object.defineProperty(Chunk.prototype, "modules", {
configurable: false,
get: util.deprecate(function() {
return Array.from(this._modules);
}, "Chunk.modules is deprecated. Use Chunk.getNumberOfModules/mapModules/forEachModule/containsModule instead."),
set: util.deprecate(function(value) {
this.setModules(value);
}, "Chunk.modules is deprecated. Use Chunk.addModule/removeModule instead.")
});
module.exports = Chunk;

View File

@ -80,6 +80,7 @@ class Compilation extends Tapable {
this.children = [];
this.dependencyFactories = new Map();
this.dependencyTemplates = new Map();
this.childrenCounters = {};
}
getStats() {
@ -511,7 +512,7 @@ class Compilation extends Tapable {
if(err) return callback(err);
deps.forEach(d => {
if(d.module && d.module.removeReason(module, d)) {
module.chunks.forEach(chunk => {
module.forEachChunk(chunk => {
if(!d.module.hasReasonForChunk(chunk)) {
if(d.module.removeChunk(chunk)) {
this.removeChunkFromDependencies(d.module, chunk);
@ -568,12 +569,12 @@ class Compilation extends Tapable {
while(self.applyPluginsBailResult1("optimize-modules-basic", self.modules) ||
self.applyPluginsBailResult1("optimize-modules", self.modules) ||
self.applyPluginsBailResult1("optimize-modules-advanced", self.modules)); // eslint-disable-line no-extra-semi
self.applyPluginsBailResult1("optimize-modules-advanced", self.modules)) { /* empty */ }
self.applyPlugins1("after-optimize-modules", self.modules);
while(self.applyPluginsBailResult1("optimize-chunks-basic", self.chunks) ||
self.applyPluginsBailResult1("optimize-chunks", self.chunks) ||
self.applyPluginsBailResult1("optimize-chunks-advanced", self.chunks)); // eslint-disable-line no-extra-semi
self.applyPluginsBailResult1("optimize-chunks-advanced", self.chunks)) { /* empty */ }
self.applyPlugins1("after-optimize-chunks", self.chunks);
self.applyPluginsAsyncSeries("optimize-tree", self.chunks, self.modules, function sealPart2(err) {
@ -583,6 +584,11 @@ class Compilation extends Tapable {
self.applyPlugins2("after-optimize-tree", self.chunks, self.modules);
while(self.applyPluginsBailResult("optimize-chunk-modules-basic", self.chunks, self.modules) ||
self.applyPluginsBailResult("optimize-chunk-modules", self.chunks, self.modules) ||
self.applyPluginsBailResult("optimize-chunk-modules-advanced", self.chunks, self.modules)) { /* empty */ }
self.applyPlugins2("after-optimize-chunk-modules", self.chunks, self.modules);
const shouldRecord = self.applyPluginsBailResult("should-record") !== false;
self.applyPlugins2("revive-modules", self.modules, self.records);
@ -1017,7 +1023,7 @@ class Compilation extends Tapable {
const modules = this.modules;
for(let indexModule = 0; indexModule < modules.length; indexModule++) {
modules[indexModule].sortItems();
modules[indexModule].sortItems(false);
}
const chunks = this.chunks;
@ -1031,7 +1037,7 @@ class Compilation extends Tapable {
const modules = this.modules;
for(let indexModule = 0; indexModule < modules.length; indexModule++) {
modules[indexModule].sortItems();
modules[indexModule].sortItems(true);
}
const chunks = this.chunks;
@ -1218,8 +1224,10 @@ class Compilation extends Tapable {
return this.mainTemplate.applyPluginsWaterfall("asset-path", filename, data);
}
createChildCompiler(name, outputOptions) {
return this.compiler.createChildCompiler(this, name, outputOptions);
createChildCompiler(name, outputOptions, plugins) {
var idx = (this.childrenCounters[name] || 0);
this.childrenCounters[name] = idx + 1;
return this.compiler.createChildCompiler(this, name, idx, outputOptions, plugins);
}
checkConstraints() {

View File

@ -10,6 +10,8 @@ var Stats = require("./Stats");
var NormalModuleFactory = require("./NormalModuleFactory");
var ContextModuleFactory = require("./ContextModuleFactory");
var makePathsRelative = require("./util/identifier").makePathsRelative;
function Watching(compiler, watchOptions, handler) {
this.startTime = null;
this.invalid = false;
@ -407,7 +409,7 @@ Compiler.prototype.readRecords = function readRecords(callback) {
});
};
Compiler.prototype.createChildCompiler = function(compilation, compilerName, outputOptions, plugins) {
Compiler.prototype.createChildCompiler = function(compilation, compilerName, compilerIndex, outputOptions, plugins) {
var childCompiler = new Compiler();
if(Array.isArray(plugins)) {
plugins.forEach(plugin => childCompiler.apply(plugin));
@ -423,8 +425,23 @@ Compiler.prototype.createChildCompiler = function(compilation, compilerName, out
childCompiler.resolvers = this.resolvers;
childCompiler.fileTimestamps = this.fileTimestamps;
childCompiler.contextTimestamps = this.contextTimestamps;
if(!this.records[compilerName]) this.records[compilerName] = [];
this.records[compilerName].push(childCompiler.records = {});
var relativeCompilerName = makePathsRelative(this.context, compilerName);
if(!this.records[relativeCompilerName]) this.records[relativeCompilerName] = [];
if(this.records[relativeCompilerName][compilerIndex])
childCompiler.records = this.records[relativeCompilerName][compilerIndex];
else
this.records[relativeCompilerName].push(childCompiler.records = {});
if(this.cache) {
if(!this.cache.children) this.cache.children = {};
if(!this.cache.children[compilerName]) this.cache.children[compilerName] = [];
if(this.cache.children[compilerName][compilerIndex])
childCompiler.cache = this.cache.children[compilerName][compilerIndex];
else
this.cache.children[compilerName].push(childCompiler.cache = {});
}
childCompiler.options = Object.create(this.options);
childCompiler.options.output = Object.create(childCompiler.options.output);
for(name in outputOptions) {

View File

@ -35,7 +35,7 @@ class DefinePlugin {
}(definitions, ""));
function stringifyObj(obj) {
return "__webpack_require__.i({" + Object.keys(obj).map((key) => {
return "Object({" + Object.keys(obj).map((key) => {
const code = obj[key];
return JSON.stringify(key) + ":" + toCode(code);
}).join(",") + "})";

View File

@ -12,7 +12,7 @@ class FlagInitialModulesAsUsedPlugin {
if(!chunk.isInitial()) {
return;
}
chunk.modules.forEach((module) => {
chunk.forEachModule((module) => {
module.usedExports = true;
});
});

View File

@ -25,17 +25,23 @@ class FunctionModuleTemplatePlugin {
if(this.outputOptions.pathinfo) {
const source = new ConcatSource();
const req = module.readableIdentifier(this.requestShortener);
if(Array.isArray(module.providedExports))
source.add("/* exports provided: " + module.providedExports.join(", ") + " */\n");
else if(module.providedExports)
source.add("/* no static exports found */\n");
if(Array.isArray(module.usedExports))
source.add("/* exports used: " + module.usedExports.join(", ") + " */\n");
else if(module.usedExports)
source.add("/* all exports used */\n");
source.add("/*!****" + req.replace(/./g, "*") + "****!*\\\n");
source.add(" !*** " + req.replace(/\*\//g, "*_/") + " ***!\n");
source.add(" \\****" + req.replace(/./g, "*") + "****/\n");
if(Array.isArray(module.providedExports))
source.add("/*! exports provided: " + module.providedExports.join(", ") + " */\n");
else if(module.providedExports)
source.add("/*! no static exports found */\n");
if(Array.isArray(module.usedExports))
source.add("/*! exports used: " + module.usedExports.join(", ") + " */\n");
else if(module.usedExports)
source.add("/*! all exports used */\n");
if(module.optimizationBailout) {
module.optimizationBailout.forEach(text => {
if(typeof text === "function") text = text(this.requestShortener);
source.add(`/*! ${text} */\n`);
});
}
source.add(moduleSource);
return source;
}

View File

@ -56,7 +56,7 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
});
records.chunkModuleIds = {};
this.chunks.forEach(function(chunk) {
records.chunkModuleIds[chunk.id] = chunk.modules.map(function(m) {
records.chunkModuleIds[chunk.id] = chunk.mapModules(function(m) {
return m.id;
});
});
@ -114,11 +114,11 @@ HotModuleReplacementPlugin.prototype.apply = function(compiler) {
chunkId = isNaN(+chunkId) ? chunkId : +chunkId;
var currentChunk = this.chunks.find(chunk => chunk.id === chunkId);
if(currentChunk) {
var newModules = currentChunk.modules.filter(function(module) {
var newModules = currentChunk.getModules().filter(function(module) {
return module.hotUpdate;
});
var allModules = {};
currentChunk.modules.forEach(function(module) {
currentChunk.forEachModule(function(module) {
allModules[module.id] = true;
});
var removedModules = records.chunkModuleIds[chunkId].filter(function(id) {

View File

@ -5,6 +5,7 @@
"use strict";
const Template = require("./Template");
const Chunk = require("./Chunk");
module.exports = class HotUpdateChunkTemplate extends Template {
constructor(outputOptions) {
@ -12,11 +13,11 @@ module.exports = class HotUpdateChunkTemplate extends Template {
}
render(id, modules, removedModules, hash, moduleTemplate, dependencyTemplates) {
const modulesSource = this.renderChunkModules({
id: id,
modules: modules,
removedModules: removedModules
}, moduleTemplate, dependencyTemplates);
const hotUpdateChunk = new Chunk();
hotUpdateChunk.id = id;
hotUpdateChunk.setModules(modules);
hotUpdateChunk.removedModules = removedModules;
const modulesSource = this.renderChunkModules(hotUpdateChunk, moduleTemplate, dependencyTemplates);
const core = this.applyPluginsWaterfall("modules", modulesSource, modules, removedModules, moduleTemplate, dependencyTemplates);
const source = this.applyPluginsWaterfall("render", core, modules, removedModules, hash, id, moduleTemplate, dependencyTemplates);
return source;

View File

@ -30,21 +30,26 @@ class LibManifestPlugin {
const manifest = {
name,
type: this.options.type,
content: chunk.modules.reduce((obj, module) => {
content: chunk.mapModules(module => {
if(module.libIdent) {
const ident = module.libIdent({
context: this.options.context || compiler.options.context
});
if(ident) {
obj[ident] = {
id: module.id,
meta: module.meta,
exports: Array.isArray(module.providedExports) ? module.providedExports : undefined
return {
ident,
data: {
id: module.id,
meta: module.meta,
exports: Array.isArray(module.providedExports) ? module.providedExports : undefined
}
};
}
}
}).filter(Boolean).reduce((obj, item) => {
obj[item.ident] = item.data;
return obj;
}, {})
}, Object.create(null))
};
const content = new Buffer(JSON.stringify(manifest), "utf8"); //eslint-disable-line
compiler.outputFileSystem.mkdirp(path.dirname(targetPath), err => {

View File

@ -114,10 +114,6 @@ module.exports = class MainTemplate extends Template {
buf.push("// expose the module cache");
buf.push(`${this.requireFn}.c = installedModules;`);
buf.push("");
buf.push("// identity function for calling harmony imports with the correct context");
buf.push(`${this.requireFn}.i = function(value) { return value; };`);
buf.push("");
buf.push("// define getter function for harmony exports");
buf.push(`${this.requireFn}.d = function(exports, name, getter) {`);

View File

@ -4,12 +4,13 @@
*/
"use strict";
const util = require("util");
const DependenciesBlock = require("./DependenciesBlock");
const ModuleReason = require("./ModuleReason");
const Template = require("./Template");
function addToSet(set, items) {
for(let item of items) {
for(const item of items) {
if(set.indexOf(item) < 0)
set.push(item);
}
@ -19,6 +20,10 @@ function byId(a, b) {
return a.id - b.id;
}
function byDebugId(a, b) {
return a.debugId - b.debugId;
}
let debugId = 1000;
class Module extends DependenciesBlock {
@ -36,13 +41,17 @@ class Module extends DependenciesBlock {
this.used = null;
this.usedExports = null;
this.providedExports = null;
this.chunks = [];
this._chunks = new Set();
this._chunksIsSorted = true;
this._chunksIsSortedByDebugId = true;
this._chunksDebugIdent = undefined;
this.warnings = [];
this.dependenciesWarnings = [];
this.errors = [];
this.dependenciesErrors = [];
this.strict = false;
this.meta = {};
this.optimizationBailout = [];
}
disconnect() {
@ -55,7 +64,9 @@ class Module extends DependenciesBlock {
this.used = null;
this.usedExports = null;
this.providedExports = null;
this.chunks.length = 0;
this._chunks.clear();
this._chunksDebugIdent = undefined;
this._chunksIsSorted = this._chunksIsSortedByDebugId = false;
super.disconnect();
}
@ -65,26 +76,75 @@ class Module extends DependenciesBlock {
this.index = null;
this.index2 = null;
this.depth = null;
this.chunks.length = 0;
this._chunks.clear();
this._chunksDebugIdent = undefined;
this._chunksIsSorted = this._chunksIsSortedByDebugId = false;
super.unseal();
}
addChunk(chunk) {
let idx = this.chunks.indexOf(chunk);
if(idx < 0)
this.chunks.push(chunk);
this._chunks.add(chunk);
this._chunksDebugIdent = undefined;
this._chunksIsSorted = this._chunksIsSortedByDebugId = false;
}
removeChunk(chunk) {
let idx = this.chunks.indexOf(chunk);
if(idx >= 0) {
this.chunks.splice(idx, 1);
if(this._chunks.delete(chunk)) {
this._chunksDebugIdent = undefined;
chunk.removeModule(this);
return true;
}
return false;
}
isInChunk(chunk) {
return this._chunks.has(chunk);
}
getChunkIdsIdent() {
if(this._chunksDebugIdent !== undefined) return this._chunksDebugIdent;
this._ensureChunksSortedByDebugId();
const chunks = this._chunks;
const list = [];
for(const chunk of chunks) {
const debugId = chunk.debugId;
if(typeof debugId !== "number") {
return this._chunksDebugIdent = null;
}
list.push(debugId);
}
return this._chunksDebugIdent = list.join(",");
}
forEachChunk(fn) {
this._chunks.forEach(fn);
}
mapChunks(fn) {
return Array.from(this._chunks, fn);
}
getNumberOfChunks() {
return this._chunks.size;
}
_ensureChunksSorted() {
if(this._chunksIsSorted) return;
this._chunks = new Set(Array.from(this._chunks).sort(byId));
this._chunksIsSortedByDebugId = false;
this._chunksIsSorted = true;
}
_ensureChunksSortedByDebugId() {
if(this._chunksIsSortedByDebugId) return;
this._chunks = new Set(Array.from(this._chunks).sort(byDebugId));
this._chunksIsSorted = false;
this._chunksIsSortedByDebugId = true;
}
addReason(module, dependency) {
this.reasons.push(new ModuleReason(module, dependency));
}
@ -101,11 +161,11 @@ class Module extends DependenciesBlock {
}
hasReasonForChunk(chunk) {
for(let r of this.reasons) {
for(const r of this.reasons) {
if(r.chunks) {
if(r.chunks.indexOf(chunk) >= 0)
return true;
} else if(r.module.chunks.indexOf(chunk) >= 0)
} else if(r.module._chunks.has(chunk))
return true;
}
return false;
@ -114,9 +174,9 @@ class Module extends DependenciesBlock {
rewriteChunkInReasons(oldChunk, newChunks) {
this.reasons.forEach(r => {
if(!r.chunks) {
if(r.module.chunks.indexOf(oldChunk) < 0)
if(!r.module._chunks.has(oldChunk))
return;
r.chunks = r.module.chunks;
r.chunks = Array.from(r.module._chunks);
}
r.chunks = r.chunks.reduce((arr, c) => {
addToSet(arr, c !== oldChunk ? [c] : newChunks);
@ -158,9 +218,10 @@ class Module extends DependenciesBlock {
super.updateHash(hash);
}
sortItems() {
sortItems(sortChunks) {
super.sortItems();
this.chunks.sort(byId);
if(sortChunks)
this._ensureChunksSorted();
this.reasons.sort((a, b) => byId(a.module, b.module));
if(Array.isArray(this.usedExports)) {
this.usedExports.sort();
@ -181,6 +242,17 @@ Object.defineProperty(Module.prototype, "entry", {
throw new Error("Module.entry was removed. Use Chunk.entryModule");
}
});
Object.defineProperty(Module.prototype, "chunks", {
configurable: false,
get: util.deprecate(() => {
return Array.from(this._chunks);
}, "Module.chunks: Use Module.forEachChunk/mapChunks/getNumberOfChunks/isInChunk/addChunk/removeChunk instead"),
set() {
throw new Error("Readonly. Use Module.addChunk/removeChunk to modify chunks.");
}
});
Module.prototype.identifier = null;
Module.prototype.readableIdentifier = null;
Module.prototype.build = null;

View File

@ -112,7 +112,7 @@ ModuleFilenameHelpers.createFooter = function createFooter(module, requestShorte
"// WEBPACK FOOTER",
`// ${module.readableIdentifier(requestShortener)}`,
`// module id = ${module.id}`,
`// module chunks = ${module.chunks.map(c => c.id).join(" ")}`
`// module chunks = ${module.mapChunks(c => c.id).join(" ")}`
].join("\n");
}
};

View File

@ -74,6 +74,7 @@ class NormalModule extends Module {
this.assets = {};
this.built = false;
this._cachedSource = null;
this._dependencyTemplatesHashMap = new Map();
}
identifier() {
@ -305,9 +306,13 @@ class NormalModule extends Module {
});
}
getHashDigest() {
getHashDigest(dependencyTemplates) {
let dtId = this._dependencyTemplatesHashMap.get(dependencyTemplates);
if(dtId === undefined)
this._dependencyTemplatesHashMap.set(dependencyTemplates, dtId = this._dependencyTemplatesHashMap.size + 1);
const hash = crypto.createHash("md5");
this.updateHash(hash);
hash.update(`${dtId}`);
return hash.digest("hex");
}
@ -441,7 +446,7 @@ class NormalModule extends Module {
}
source(dependencyTemplates, outputOptions, requestShortener) {
const hashDigest = this.getHashDigest();
const hashDigest = this.getHashDigest(dependencyTemplates);
if(this._cachedSource && this._cachedSource.hash === hashDigest) {
return this._cachedSource.source;
}

View File

@ -17,10 +17,12 @@ function joinRanges(startRange, endRange) {
return [startRange[0], endRange[1]];
}
const ECMA_VERSION = 2017;
const POSSIBLE_AST_OPTIONS = [{
ranges: true,
locations: true,
ecmaVersion: 2017,
ecmaVersion: ECMA_VERSION,
sourceType: "module",
plugins: {
dynamicImport: true
@ -28,7 +30,7 @@ const POSSIBLE_AST_OPTIONS = [{
}, {
ranges: true,
locations: true,
ecmaVersion: 2017,
ecmaVersion: ECMA_VERSION,
sourceType: "script",
plugins: {
dynamicImport: true
@ -1110,6 +1112,12 @@ class Parser extends Tapable {
result = this.applyPluginsBailResult1("call " + callee.identifier, expression);
if(result === true)
return;
let identifier = callee.identifier.replace(/\.[^\.]+$/, ".*");
if(identifier !== callee.identifier) {
result = this.applyPluginsBailResult1("call " + identifier, expression);
if(result === true)
return;
}
}
if(expression.callee)
@ -1335,7 +1343,7 @@ class Parser extends Tapable {
ast = acorn.parse(source, {
ranges: true,
locations: true,
ecmaVersion: 2017,
ecmaVersion: ECMA_VERSION,
sourceType: "module",
plugins: {
dynamicImport: true
@ -1369,7 +1377,7 @@ class Parser extends Tapable {
const ast = acorn.parse("(" + source + ")", {
ranges: true,
locations: true,
ecmaVersion: 2017,
ecmaVersion: ECMA_VERSION,
sourceType: "module",
plugins: {
dynamicImport: true
@ -1401,4 +1409,6 @@ class Parser extends Tapable {
}
Parser.ECMA_VERSION = ECMA_VERSION;
module.exports = Parser;

View File

@ -82,17 +82,19 @@ class ProgressPlugin {
"optimize-chunks": [0.77, "chunk optimization"],
"optimize-chunks-advanced": [0.78, "advanced chunk optimization"],
// optimize-tree
"revive-modules": [0.80, "module reviving"],
"optimize-module-order": [0.81, "module order optimization"],
"optimize-module-ids": [0.82, "module id optimization"],
"revive-chunks": [0.83, "chunk reviving"],
"optimize-chunk-order": [0.84, "chunk order optimization"],
"optimize-chunk-ids": [0.85, "chunk id optimization"],
"before-hash": [0.86, "hashing"],
"before-module-assets": [0.87, "module assets processing"],
"before-chunk-assets": [0.88, "chunk assets processing"],
"additional-chunk-assets": [0.89, "additional chunk assets processing"],
"record": [0.90, "recording"]
"optimize-chunk-modules": [0.80, "chunk modules optimization"],
"optimize-chunk-modules-advanced": [0.81, "advanced chunk modules optimization"],
"revive-modules": [0.82, "module reviving"],
"optimize-module-order": [0.83, "module order optimization"],
"optimize-module-ids": [0.84, "module id optimization"],
"revive-chunks": [0.85, "chunk reviving"],
"optimize-chunk-order": [0.86, "chunk order optimization"],
"optimize-chunk-ids": [0.87, "chunk id optimization"],
"before-hash": [0.88, "hashing"],
"before-module-assets": [0.89, "module assets processing"],
"before-chunk-assets": [0.90, "chunk assets processing"],
"additional-chunk-assets": [0.91, "additional chunk assets processing"],
"record": [0.92, "recording"]
};
Object.keys(syncHooks).forEach(name => {
let pass = 0;

View File

@ -32,7 +32,6 @@ class RequestShortener {
this.buildinsRegExp = new RegExp("^" + buildinsRegExpString + "|(!)" + buildinsRegExpString, "g");
}
this.nodeModulesRegExp = /\/node_modules\//g;
this.indexJsRegExp = /\/index.js(!|\?|\(query\))/g;
}
@ -47,7 +46,6 @@ class RequestShortener {
request = request.replace(this.parentDirectoryRegExp, "!..");
if(!this.buildinsAsModule && this.buildinsRegExp)
request = request.replace(this.buildinsRegExp, "!(webpack)");
request = request.replace(this.nodeModulesRegExp, "/~/");
request = request.replace(this.indexJsRegExp, "$1");
return request.replace(/^!|!$/, "");
}

View File

@ -8,7 +8,7 @@ const RequestShortener = require("./RequestShortener");
const SizeFormatHelpers = require("./SizeFormatHelpers");
const formatLocation = require("./formatLocation");
const optionOrFallback = (optionValue, fallbackValue) => optionValue !== undefined ? optionValue : fallbackValue;
const optionOrFallback = (optionValue, fallbackValue) => typeof optionValue !== "undefined" ? optionValue : fallbackValue;
class Stats {
constructor(compilation) {
@ -75,50 +75,62 @@ class Stats {
options = {};
}
const optionOrLocalFallback = (v, def) =>
typeof v !== "undefined" ? v :
typeof options.all !== "undefined" ? options.all : def;
const compilation = this.compilation;
const requestShortener = new RequestShortener(optionOrFallback(options.context, process.cwd()));
const showPerformance = optionOrFallback(options.performance, true);
const showHash = optionOrFallback(options.hash, true);
const showVersion = optionOrFallback(options.version, true);
const showTimings = optionOrFallback(options.timings, true);
const showAssets = optionOrFallback(options.assets, true);
const showEntrypoints = optionOrFallback(options.entrypoints, !forToString);
const showChunks = optionOrFallback(options.chunks, true);
const showChunkModules = optionOrFallback(options.chunkModules, !!forToString);
const showChunkOrigins = optionOrFallback(options.chunkOrigins, !forToString);
const showModules = optionOrFallback(options.modules, !forToString);
const showDepth = optionOrFallback(options.depth, !forToString);
const showCachedModules = optionOrFallback(options.cached, true);
const showCachedAssets = optionOrFallback(options.cachedAssets, true);
const showReasons = optionOrFallback(options.reasons, !forToString);
const showUsedExports = optionOrFallback(options.usedExports, !forToString);
const showProvidedExports = optionOrFallback(options.providedExports, !forToString);
const showChildren = optionOrFallback(options.children, true);
const showSource = optionOrFallback(options.source, !forToString);
const showModuleTrace = optionOrFallback(options.moduleTrace, true);
const showErrors = optionOrFallback(options.errors, true);
const showErrorDetails = optionOrFallback(options.errorDetails, !forToString);
const showWarnings = optionOrFallback(options.warnings, true);
const showPerformance = optionOrLocalFallback(options.performance, true);
const showHash = optionOrLocalFallback(options.hash, true);
const showVersion = optionOrLocalFallback(options.version, true);
const showTimings = optionOrLocalFallback(options.timings, true);
const showAssets = optionOrLocalFallback(options.assets, true);
const showEntrypoints = optionOrLocalFallback(options.entrypoints, !forToString);
const showChunks = optionOrLocalFallback(options.chunks, !forToString);
const showChunkModules = optionOrLocalFallback(options.chunkModules, !!forToString);
const showChunkOrigins = optionOrLocalFallback(options.chunkOrigins, !forToString);
const showModules = optionOrLocalFallback(options.modules, true);
const showDepth = optionOrLocalFallback(options.depth, !forToString);
const showCachedModules = optionOrLocalFallback(options.cached, true);
const showCachedAssets = optionOrLocalFallback(options.cachedAssets, true);
const showReasons = optionOrLocalFallback(options.reasons, !forToString);
const showUsedExports = optionOrLocalFallback(options.usedExports, !forToString);
const showProvidedExports = optionOrLocalFallback(options.providedExports, !forToString);
const showOptimizationBailout = optionOrLocalFallback(options.optimizationBailout, !forToString);
const showChildren = optionOrLocalFallback(options.children, true);
const showSource = optionOrLocalFallback(options.source, !forToString);
const showModuleTrace = optionOrLocalFallback(options.moduleTrace, true);
const showErrors = optionOrLocalFallback(options.errors, true);
const showErrorDetails = optionOrLocalFallback(options.errorDetails, !forToString);
const showWarnings = optionOrLocalFallback(options.warnings, true);
const warningsFilter = optionOrFallback(options.warningsFilter, null);
const showPublicPath = optionOrFallback(options.publicPath, !forToString);
const excludeModules = [].concat(optionOrFallback(options.exclude, [])).map(str => {
if(typeof str !== "string") return str;
return new RegExp(`[\\\\/]${str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")}([\\\\/]|$|!|\\?)`);
const showPublicPath = optionOrLocalFallback(options.publicPath, !forToString);
const excludeModules = [].concat(optionOrFallback(options.exclude, [])).map(item => {
if(typeof item === "string") {
const regExp = new RegExp(`[\\\\/]${item.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")}([\\\\/]|$|!|\\?)`);
return ident => regExp.test(ident);
}
if(item && typeof item === "object" && typeof item.test === "function")
return ident => item.test(ident);
if(typeof item === "function")
return item;
});
const maxModules = optionOrFallback(options.maxModules, forToString ? 15 : Infinity);
const sortModules = optionOrFallback(options.modulesSort, "id");
const sortChunks = optionOrFallback(options.chunksSort, "id");
const sortAssets = optionOrFallback(options.assetsSort, "");
if(!showCachedModules) {
excludeModules.push((ident, module) => !module.built);
}
const createModuleFilter = () => {
let i = 0;
return module => {
if(!showCachedModules && !module.built) {
return false;
}
if(excludeModules.length > 0) {
const ident = requestShortener.shorten(module.resource);
const excluded = excludeModules.some(regExp => regExp.test(ident));
const excluded = excludeModules.some(fn => fn(ident, module));
if(excluded)
return false;
}
@ -280,7 +292,7 @@ class Stats {
built: !!module.built,
optional: !!module.optional,
prefetched: !!module.prefetched,
chunks: module.chunks.map(chunk => chunk.id),
chunks: module.mapChunks(chunk => chunk.id),
assets: Object.keys(module.assets || {}),
issuer: module.issuer && module.issuer.identifier(),
issuerId: module.issuer && module.issuer.id,
@ -311,6 +323,12 @@ class Stats {
if(showProvidedExports) {
obj.providedExports = Array.isArray(module.providedExports) ? module.providedExports : null;
}
if(showOptimizationBailout) {
obj.optimizationBailout = module.optimizationBailout.map(item => {
if(typeof item === "function") return item(requestShortener);
return item;
});
}
if(showDepth) {
obj.depth = module.depth;
}
@ -328,19 +346,19 @@ class Stats {
entry: chunk.hasRuntime(),
recorded: chunk.recorded,
extraAsync: !!chunk.extraAsync,
size: chunk.modules.reduce((size, module) => size + module.size(), 0),
size: chunk.mapModules(m => m.size()).reduce((size, moduleSize) => size + moduleSize, 0),
names: chunk.name ? [chunk.name] : [],
files: chunk.files.slice(),
hash: chunk.renderedHash,
parents: chunk.parents.map(c => c.id)
};
if(showChunkModules) {
obj.modules = chunk.modules
.slice()
obj.modules = chunk
.getModules()
.sort(sortByField("depth"))
.filter(createModuleFilter())
.map(fnModule);
obj.filteredModules = chunk.modules.length - obj.modules.length;
obj.filteredModules = chunk.getNumberOfModules() - obj.modules.length;
obj.modules.sort(sortByField(sortModules));
}
if(showChunkOrigins) {
@ -636,6 +654,13 @@ class Stats {
newline();
}
}
if(Array.isArray(module.optimizationBailout)) {
module.optimizationBailout.forEach(item => {
colors.normal(prefix);
colors.yellow(item);
newline();
});
}
if(module.reasons) {
module.reasons.forEach(reason => {
colors.normal(prefix);
@ -685,6 +710,35 @@ class Stats {
}
};
const processModulesList = (obj, prefix) => {
if(obj.modules) {
obj.modules.forEach(module => {
colors.normal(prefix);
if(module.id < 1000) colors.normal(" ");
if(module.id < 100) colors.normal(" ");
if(module.id < 10) colors.normal(" ");
colors.normal("[");
colors.normal(module.id);
colors.normal("] ");
colors.bold(module.name || module.identifier);
processModuleAttributes(module);
newline();
processModuleContent(module, prefix + " ");
});
if(obj.filteredModules > 0) {
colors.normal(prefix);
colors.normal(" ");
if(obj.modules.length > 0)
colors.normal(" + ");
colors.normal(obj.filteredModules);
if(obj.modules.length > 0)
colors.normal(" hidden");
colors.normal(obj.filteredModules !== 1 ? " modules" : " module");
newline();
}
}
};
if(obj.chunks) {
obj.chunks.forEach(chunk => {
colors.normal("chunk ");
@ -746,45 +800,11 @@ class Stats {
newline();
});
}
if(chunk.modules) {
chunk.modules.forEach(module => {
colors.normal(" ");
if(module.id < 1000) colors.normal(" ");
if(module.id < 100) colors.normal(" ");
if(module.id < 10) colors.normal(" ");
colors.normal("[");
colors.normal(module.id);
colors.normal("] ");
colors.bold(module.name);
processModuleAttributes(module);
newline();
processModuleContent(module, " ");
});
if(chunk.filteredModules > 0) {
colors.normal(` + ${chunk.filteredModules} hidden modules`);
newline();
}
}
processModulesList(chunk, " ");
});
}
if(obj.modules) {
obj.modules.forEach(module => {
if(module.id < 1000) colors.normal(" ");
if(module.id < 100) colors.normal(" ");
if(module.id < 10) colors.normal(" ");
colors.normal("[");
colors.normal(module.id);
colors.normal("] ");
colors.bold(module.name || module.identifier);
processModuleAttributes(module);
newline();
processModuleContent(module, " ");
});
if(obj.filteredModules > 0) {
colors.normal(` + ${obj.filteredModules} hidden modules`);
newline();
}
}
processModulesList(obj, "");
if(obj._showWarnings && obj.warnings) {
obj.warnings.forEach(warning => {
@ -827,51 +847,63 @@ class Stats {
}
static presetToOptions(name) {
//Accepted values: none, errors-only, minimal, normal, verbose
//Any other falsy value will behave as 'none', truthy values as 'normal'
const pn = (typeof name === "string") && name.toLowerCase() || name;
if(pn === "none" || !pn) {
return {
hash: false,
version: false,
timings: false,
assets: false,
entrypoints: false,
chunks: false,
chunkModules: false,
modules: false,
reasons: false,
depth: false,
usedExports: false,
providedExports: false,
children: false,
source: false,
errors: false,
errorDetails: false,
warnings: false,
publicPath: false,
performance: false
};
} else {
return {
hash: pn !== "errors-only" && pn !== "minimal",
version: pn === "verbose",
timings: pn !== "errors-only" && pn !== "minimal",
assets: pn === "verbose",
entrypoints: pn === "verbose",
chunks: pn !== "errors-only",
chunkModules: pn === "verbose",
//warnings: pn !== "errors-only",
errorDetails: pn !== "errors-only" && pn !== "minimal",
reasons: pn === "verbose",
depth: pn === "verbose",
usedExports: pn === "verbose",
providedExports: pn === "verbose",
colors: true,
performance: true
};
// Accepted values: none, errors-only, minimal, normal, detailed, verbose
// Any other falsy value will behave as 'none', truthy values as 'normal'
const pn = (typeof name === "string") && name.toLowerCase() || name || "none";
switch(pn) {
case "none":
return {
all: false
};
case "verbose":
return {
entrypoints: true,
modules: false,
chunks: true,
chunkModules: true,
chunkOrigins: true,
depth: true,
reasons: true,
usedExports: true,
providedExports: true,
optimizationBailout: true,
errorDetails: true,
publicPath: true,
exclude: () => false,
maxModules: Infinity,
};
case "detailed":
return {
entrypoints: true,
chunks: true,
chunkModules: false,
chunkOrigins: true,
depth: true,
usedExports: true,
providedExports: true,
optimizationBailout: true,
errorDetails: true,
publicPath: true,
exclude: () => false,
maxModules: Infinity,
};
case "minimal":
return {
all: false,
modules: true,
maxModules: 0,
errors: true,
warnings: true,
};
case "errors-only":
return {
all: false,
errors: true,
moduleTrace: true,
};
default:
return {};
}
}
static getChildOptions(options, idx) {

View File

@ -99,12 +99,12 @@ module.exports = class Template extends Tapable {
renderChunkModules(chunk, moduleTemplate, dependencyTemplates, prefix) {
if(!prefix) prefix = "";
var source = new ConcatSource();
if(chunk.modules.length === 0) {
if(chunk.getNumberOfModules() === 0) {
source.add("[]");
return source;
}
var removedModules = chunk.removedModules;
var allModules = chunk.modules.map(function(module) {
var allModules = chunk.mapModules(function(module) {
return {
id: module.id,
source: moduleTemplate.render(module, dependencyTemplates, chunk)
@ -118,7 +118,7 @@ module.exports = class Template extends Tapable {
});
});
}
var bounds = this.getModulesArrayBounds(chunk.modules);
var bounds = this.getModulesArrayBounds(allModules);
if(bounds) {
// Render a spare array

View File

@ -32,7 +32,7 @@ class UmdMainTemplatePlugin {
apply(compilation) {
const mainTemplate = compilation.mainTemplate;
compilation.templatesPlugin("render-with-entry", function(source, chunk, hash) {
let externals = chunk.modules.filter(m => m.external);
let externals = chunk.getModules().filter(m => m.external);
const optionalExternals = [];
let requiredExternals = [];
if(this.optionalAmdExternalAsGlobal) {

View File

@ -28,6 +28,7 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
this.set("module.wrappedContextRecursive", true);
this.set("module.wrappedContextCritical", false);
this.set("module.strictExportPresence", false);
this.set("module.strictThisContextOnImports", false);
this.set("module.unsafeCache", true);

View File

@ -13,6 +13,7 @@ const HarmonyModulesHelpers = require("./HarmonyModulesHelpers");
module.exports = class HarmonyImportDependencyParserPlugin {
constructor(moduleOptions) {
this.strictExportPresence = moduleOptions.strictExportPresence;
this.strictThisContextOnImports = moduleOptions.strictThisContextOnImports;
}
apply(parser) {
@ -52,22 +53,38 @@ module.exports = class HarmonyImportDependencyParserPlugin {
parser.state.current.addDependency(dep);
return true;
});
parser.plugin("call imported var", (expr) => {
const args = expr.arguments;
const fullExpr = expr;
expr = expr.callee;
const name = expr.name;
const settings = parser.state.harmonySpecifier[`$${name}`];
const dep = new HarmonyImportSpecifierDependency(settings[0], settings[1], settings[2], name, expr.range, this.strictExportPresence);
dep.directImport = true;
dep.callArgs = args;
dep.call = fullExpr;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
if(args)
parser.walkExpressions(args);
return true;
});
if(this.strictThisContextOnImports) {
// only in case when we strictly follow the spec we need a special case here
parser.plugin("call imported var.*", (expr) => {
const name = expr.callee.object.name;
const settings = parser.state.harmonySpecifier[`$${name}`];
if(settings[2] !== null)
return false;
const dep = new HarmonyImportSpecifierDependency(settings[0], settings[1], expr.callee.property.name || expr.callee.property.value, name, expr.callee.range, this.strictExportPresence);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.namespaceObjectAsContext = true;
dep.loc = expr.callee.loc;
parser.state.current.addDependency(dep);
return true;
});
parser.plugin("call imported var", (expr) => {
const args = expr.arguments;
const fullExpr = expr;
expr = expr.callee;
const name = expr.name;
const settings = parser.state.harmonySpecifier[`$${name}`];
const dep = new HarmonyImportSpecifierDependency(settings[0], settings[1], settings[2], name, expr.range, this.strictExportPresence);
dep.directImport = true;
dep.callArgs = args;
dep.call = fullExpr;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
if(args)
parser.walkExpressions(args);
return true;
});
}
parser.plugin("hot accept callback", (expr, requests) => {
const dependencies = requests
.filter(request => HarmonyModulesHelpers.checkModuleVar(parser.state, request))

View File

@ -14,6 +14,10 @@ class HarmonyImportSpecifierDependency extends NullDependency {
this.name = name;
this.range = range;
this.strictExportPresence = strictExportPresence;
this.namespaceObjectAsContext = false;
this.callArgs = undefined;
this.call = undefined;
this.directImport = undefined;
}
get type() {
@ -24,7 +28,7 @@ class HarmonyImportSpecifierDependency extends NullDependency {
if(!this.importDependency.module) return null;
return {
module: this.importDependency.module,
importedNames: this.id ? [this.id] : true
importedNames: this.id && !this.namespaceObjectAsContext ? [this.id] : true
};
}
@ -93,7 +97,7 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
}
if(dep.call && dep.id) {
return `${shortHandPrefix}__webpack_require__.i(${importedVar}${importedVarSuffix})`;
return `${shortHandPrefix}Object(${importedVar}${importedVarSuffix})`;
}
return `${shortHandPrefix}${importedVar}${importedVarSuffix}`;

View File

@ -6,18 +6,6 @@
const identifierUtils = require("../util/identifier");
function toIndexOf(list) {
return function(item) {
return list.indexOf(item);
};
}
function toChunkModuleIndices(modules) {
return function(idx) {
return modules[idx];
};
}
function moveModuleBetween(oldChunk, newChunk) {
return function(module) {
oldChunk.moveModule(module, newChunk);
@ -62,17 +50,19 @@ class AggressiveSplittingPlugin {
const splitData = usedSplits[j];
for(let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const chunkModuleNames = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));
if(chunkModuleNames.length < splitData.modules.length)
if(chunk.getNumberOfModules() < splitData.modules.length)
continue;
const moduleIndicies = splitData.modules.map(toIndexOf(chunkModuleNames));
const hasAllModules = moduleIndicies.every((idx) => {
return idx >= 0;
const nameToModuleMap = new Map();
chunk.forEachModule(m => {
const name = identifierUtils.makePathsRelative(compiler.context, m.identifier());
nameToModuleMap.set(name, m);
});
if(hasAllModules) {
if(chunkModuleNames.length > splitData.modules.length) {
const selectedModules = moduleIndicies.map(toChunkModuleIndices(chunk.modules));
const selectedModules = splitData.modules.map(name => nameToModuleMap.get(name));
if(selectedModules.every(Boolean)) {
if(chunk.getNumberOfModules() > splitData.modules.length) {
const newChunk = compilation.addChunk();
selectedModules.forEach(moveModuleBetween(chunk, newChunk));
chunk.split(newChunk);
@ -101,9 +91,9 @@ class AggressiveSplittingPlugin {
for(let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const size = chunk.size(this.options);
if(size > maxSize && chunk.modules.length > 1) {
if(size > maxSize && chunk.getNumberOfModules() > 1) {
const newChunk = compilation.addChunk();
const modules = chunk.modules
const modules = chunk.getModules()
.filter(isNotAEntryModule(chunk.entryModule))
.sort((a, b) => {
a = a.identifier();
@ -131,13 +121,13 @@ class AggressiveSplittingPlugin {
break;
}
}
if(newChunk.modules.length > 0) {
if(newChunk.getNumberOfModules() > 0) {
chunk.split(newChunk);
chunk.name = null;
newChunk.origins = chunk.origins.map(copyWithReason);
chunk.origins = chunk.origins.map(copyWithReason);
compilation._aggressiveSplittingSplits = (compilation._aggressiveSplittingSplits || []).concat({
modules: newChunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()))
modules: newChunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()))
});
return true;
} else {
@ -154,7 +144,7 @@ class AggressiveSplittingPlugin {
if(chunk.hasEntryModule()) return;
const size = chunk.size(this.options);
const incorrectSize = size < minSize;
const modules = chunk.modules.map(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));
const modules = chunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier()));
if(typeof chunk._fromAggressiveSplittingIndex === "undefined") {
if(incorrectSize) return;
chunk.recorded = true;

View File

@ -275,7 +275,7 @@ Take a look at the "name"/"names" or async/children option.`);
// count how many chunks contain a module
const commonModulesToCountMap = usedChunks.reduce((map, chunk) => {
for(let module of chunk.modules) {
for(const module of chunk.modulesIterable) {
const count = map.has(module) ? map.get(module) : 0;
map.set(module, count + 1);
}

View File

@ -0,0 +1,517 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const Module = require("../Module");
const Template = require("../Template");
const Parser = require("../Parser");
const acorn = require("acorn");
const escope = require("escope");
const ReplaceSource = require("webpack-sources/lib/ReplaceSource");
const ConcatSource = require("webpack-sources/lib/ConcatSource");
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency");
const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
const HarmonyModulesHelpers = require("../dependencies/HarmonyModulesHelpers");
function getFinalName(info, exportName, moduleToInfoMap, requestShortener) {
const directExport = info.exportMap.get(exportName);
if(directExport) {
if(exportName === true)
info.needNamespaceObject = true;
return info.internalNames.get(directExport);
}
const reexport = info.reexportMap.get(exportName);
if(reexport) {
const refInfo = moduleToInfoMap.get(reexport.module);
if(refInfo) {
// module is in the concatenation
return getFinalName(refInfo, reexport.exportName, moduleToInfoMap, requestShortener);
} else {
const dep = reexport.dependency;
const importedModule = reexport.module;
const exportName = reexport.exportName;
const isHarmonyModule = importedModule && (!importedModule.meta || importedModule.meta.harmonyModule);
const importedVar = dep.importedVar;
const used = importedModule.isUsed(exportName);
if(!used) return "/* unused reexport */undefined";
if(!isHarmonyModule && exportName === "default") {
return `${importedVar}_default.a`;
}
return `${importedVar}[${JSON.stringify(used)}]`;
}
}
throw new Error(`Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(requestShortener)}"` +
` (known exports: ${Array.from(info.exportMap.keys()).join(" ")}, ` +
`known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`);
}
function getSymbolsFromScope(s, untilScope) {
const allUsedNames = new Set();
let scope = s;
while(scope) {
if(untilScope === scope) break;
scope.variables.forEach(variable => allUsedNames.add(variable.name));
scope = scope.upper;
}
return allUsedNames;
}
function reduceSet(a, b) {
for(const item of b)
a.add(item);
return a;
}
class ConcatenatedModule extends Module {
constructor(rootModule, modules) {
super();
this.rootModule = rootModule;
this.modules = modules;
this.usedExports = rootModule.usedExports;
this.providedExports = rootModule.providedExports;
this.optimizationBailout = rootModule.optimizationBailout;
this.used = rootModule.used;
this._chunks = new Set(rootModule._chunks);
this.index = rootModule.index;
this.index2 = rootModule.index2;
this.depth = rootModule.depth;
this.built = modules.some(m => m.built);
this.cacheable = modules.every(m => m.cacheable);
const modulesSet = new Set(modules);
this.reasons = rootModule.reasons.filter(reason => !modulesSet.has(reason.module));
this.dependencies = [].concat.apply([], modules.map(m => m.dependencies.filter(dep => !modulesSet.has(dep.module))));
this.meta = rootModule.meta;
this.dependenciesWarnings = modules.reduce((w, m) => m.dependenciesWarnings.forEach(x => w.push(x)), []);
this.dependenciesErrors = modules.reduce((w, m) => m.dependenciesErrors.forEach(x => w.push(x)), []);
this.warnings = modules.reduce((w, m) => m.warnings.forEach(x => w.push(x)), []);
this.errors = modules.reduce((w, m) => m.errors.forEach(x => w.push(x)), []);
this.moduleArgument = rootModule.moduleArgument;
this.exportsArgument = rootModule.exportsArgument;
this.strict = true;
modules.forEach((m, idx) => {
for(const dep of m.dependencies.filter(dep => !modulesSet.has(dep.module))) {
this.dependencies.push(dep);
}
});
}
identifier() {
return this.modules.map(m => m.identifier()).join(" ");
}
readableIdentifier(requestShortener) {
return this.rootModule.readableIdentifier(requestShortener) + ` + ${this.modules.length - 1} modules`;
}
libIdent(options) {
return this.rootModule.libIdent(options);
}
nameForCondition() {
return this.rootModule.nameForCondition();
}
build(options, compilation, resolver, fs, callback) {
throw new Error("Cannot build this module. It should be already built.");
}
size() {
// Guess size from embedded modules
return this.modules.reduce((sum, m) => sum + m.size(), 0);
}
source(dependencyTemplates, outputOptions, requestShortener) {
const modulesSet = new Set();
this.modules.forEach(m => modulesSet.add(m));
// Metainfo for each module
const modulesWithInfo = this.modules.map((m, idx) => {
const exportMap = new Map();
const reexportMap = new Map();
m.dependencies.forEach(dep => {
if(dep instanceof HarmonyExportSpecifierDependency) {
exportMap.set(dep.name, dep.id);
} else if(dep instanceof HarmonyExportExpressionDependency) {
exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__");
} else if(dep instanceof HarmonyExportImportedSpecifierDependency) {
const exportName = dep.name;
const importName = dep.id;
const importModule = dep.importDependency.module;
const innerReexport = modulesSet.has(importModule);
if(exportName && importName) {
reexportMap.set(exportName, {
module: importModule,
exportName: importName,
dependency: dep
});
} else if(exportName) {
reexportMap.set(exportName, {
module: importModule,
exportName: true,
dependency: dep
});
} else if(Array.isArray(importModule.providedExports)) {
var activeExports = new Set(HarmonyModulesHelpers.getActiveExports(dep.originModule, dep));
importModule.providedExports.forEach(name => {
if(activeExports.has(name) || name === "default")
return;
reexportMap.set(name, {
module: importModule,
exportName: name,
dependency: dep
});
});
} else if(innerReexport) {
throw new Error(`Module "${importModule.readableIdentifier(requestShortener)}" doesn't provide static exports for "export *" in ${m.readableIdentifier(requestShortener)}`);
}
}
});
return {
module: m,
index: idx,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
internalNames: new Map(),
exportMap: exportMap,
reexportMap: reexportMap,
needCompatibilityFlag: false,
needNamespaceObject: false
};
});
// Create mapping from module to info
const moduleToInfoMap = new Map();
modulesWithInfo.forEach(m => moduleToInfoMap.set(m.module, m));
// Configure template decorators for dependencies
const innerDependencyTemplates = new Map(dependencyTemplates);
innerDependencyTemplates.set(HarmonyImportSpecifierDependency, new HarmonyImportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportSpecifierDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyImportDependency, new HarmonyImportDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportSpecifierDependency),
this.rootModule
));
innerDependencyTemplates.set(HarmonyExportExpressionDependency, new HarmonyExportExpressionDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportExpressionDependency),
this.rootModule,
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyExportImportedSpecifierDependency, new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyExportImportedSpecifierDependency),
this.rootModule,
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyCompatibilityDependency, new HarmonyCompatibilityDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyCompatibilityDependency),
this.rootModule,
moduleToInfoMap
));
// Generate source code and analyse scopes
// Prepare a ReplaceSource for the final source
modulesWithInfo.forEach(info => {
const m = info.module;
const source = m.source(innerDependencyTemplates, outputOptions, requestShortener);
const code = source.source();
const ast = acorn.parse(code, {
ranges: true,
locations: true,
ecmaVersion: Parser.ECMA_VERSION,
sourceType: "module"
});
const scopeManager = escope.analyze(ast, {
ecmaVersion: 6,
sourceType: "module",
optimistic: true,
ignoreEval: true,
impliedStrict: true
});
const globalScope = scopeManager.acquire(ast);
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(m.source(innerDependencyTemplates, outputOptions, requestShortener));
info.source = resultSource;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
});
// List of all used names to avoid conflicts
const allUsedNames = new Set(["__WEBPACK_MODULE_DEFAULT_EXPORT__", "defaultExport", "Object"]);
// get all global names
modulesWithInfo.forEach(info => {
info.globalScope.through.forEach(reference => {
const name = reference.identifier.name;
if(/^__WEBPACK_MODULE_REFERENCE__\d+_(\d+|ns)__$/.test(name)) {
for(const s of getSymbolsFromScope(reference.from, info.moduleScope)) {
allUsedNames.add(s);
}
} else {
allUsedNames.add(name);
}
});
});
modulesWithInfo.forEach(info => {
const namespaceObjectName = this.findNewName("namespaceObject", allUsedNames, null, info.module.readableIdentifier(requestShortener));
info.internalNames.set(namespaceObjectName, namespaceObjectName);
info.exportMap.set(true, namespaceObjectName);
info.moduleScope.variables.forEach(variable => {
const name = variable.name;
if(allUsedNames.has(name)) {
const symbolsInReferences = variable.references.map(ref => getSymbolsFromScope(ref.from, info.moduleScope)).reduce(reduceSet, new Set());
const newName = this.findNewName(name, allUsedNames, symbolsInReferences, info.module.readableIdentifier(requestShortener));
allUsedNames.add(newName);
info.internalNames.set(name, newName);
const source = info.source;
const allIdentifiers = new Set(variable.references.map(r => r.identifier).concat(variable.identifiers));
for(const identifier of allIdentifiers) {
const r = identifier.range;
source.replace(r[0], r[1] - 1, newName);
}
} else {
allUsedNames.add(name);
info.internalNames.set(name, name);
}
});
});
modulesWithInfo.forEach(info => {
info.globalScope.through.forEach(reference => {
const name = reference.identifier.name;
const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_(\d+|ns)__$/.exec(name);
if(match) {
const referencedModule = modulesWithInfo[+match[1]];
let exportName;
if(match[2] === "ns") {
exportName = true;
} else {
const exportIdx = +match[2];
exportName = referencedModule.module.providedExports[exportIdx];
}
const finalName = getFinalName(referencedModule, exportName, moduleToInfoMap, requestShortener);
if(!finalName)
throw new Error(`Cannot map to variable name in module ${info.module.resource} (export '${exportName}')`);
const r = reference.identifier.range;
const source = info.source;
source.replace(r[0], r[1] - 1, finalName);
}
});
});
const result = new ConcatSource();
if(moduleToInfoMap.get(this.rootModule).needCompatibilityFlag) {
result.add(`Object.defineProperty(${this.rootModule.exportsArgument || "exports"}, "__esModule", { value: true });\n`);
}
modulesWithInfo.forEach(info => {
result.add(`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(requestShortener)}\n`);
if(info.needNamespaceObject) {
const name = info.exportMap.get(true);
const nsObj = [`var ${name} = {};`];
for(const exportName of info.module.providedExports) {
const finalName = getFinalName(info, exportName, moduleToInfoMap, requestShortener);
nsObj.push(`__webpack_require__.d(${name}, ${JSON.stringify(exportName)}, function() { return ${finalName}; });`);
}
result.add(nsObj.join("\n") + "\n");
}
result.add(info.source);
});
return result;
}
findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
let name = oldName;
if(name === "__WEBPACK_MODULE_DEFAULT_EXPORT__")
name = "defaultExport";
const splittedInfo = extraInfo.split("/");
while(splittedInfo.length) {
name = splittedInfo.pop() + "_" + name;
const nameIdent = Template.toIdentifier(name);
if(!usedNamed1.has(nameIdent) && (!usedNamed2 || !usedNamed2.has(nameIdent))) return nameIdent;
}
while(usedNamed1.has(name = name + "_") || (usedNamed2 && usedNamed2.has(name))) { /* do nothing */ }
return name;
}
}
class HarmonyImportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.importDependency.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
if(!Array.isArray(module.providedExports))
throw new Error(`Module ${module.resource} has no static exports ${module.providedExports}`);
let content;
if(dep.id === null) {
content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`;
} else {
const exportIdx = (module.providedExports).indexOf(dep.id);
content = exportIdx === -1 ? "undefined" : `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportIdx}__`;
}
if(dep.shorthand) {
content = dep.name + ": " + content;
}
source.replace(dep.range[0], dep.range[1] - 1, content);
}
}
class HarmonyImportDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
source.replace(dep.range[0], dep.range[1] - 1, "");
}
}
class HarmonyExportSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
}
}
}
class HarmonyExportExpressionDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
} else {
const content = "/* harmony default export */ var __WEBPACK_MODULE_DEFAULT_EXPORT__ = ";
if(dep.range) {
source.replace(dep.rangeStatement[0], dep.range[0] - 1, content + "(");
source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");");
return;
}
source.replace(dep.rangeStatement[0], dep.rangeStatement[1] - 1, content);
}
}
}
class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
getExports(dep) {
const active = HarmonyModulesHelpers.isActive(dep.originModule, dep);
if(!active) return [];
const importModule = dep.importDependency.module;
if(dep.id) {
// export { named } from "module"
return [{
name: dep.name,
id: dep.id,
module: importModule
}];
}
if(dep.name) {
// export * as abc from "module"
return [{
name: dep.name,
id: true,
module: importModule
}];
}
// export * from "module"
const activeExports = new Set(HarmonyModulesHelpers.getActiveExports(dep.originModule, dep));
return importModule.providedExports.filter(exp => exp !== "default" && !activeExports.has(exp)).map(exp => {
return {
name: exp,
id: exp,
module: importModule
};
});
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
if(this.modulesMap.get(dep.importDependency.module)) {
const exportDefs = this.getExports(dep);
exportDefs.forEach(def => {
const info = this.modulesMap.get(def.module);
const used = dep.originModule.isUsed(def.name);
if(!used) {
source.insert(-1, `/* unused concated harmony import ${dep.name} */\n`);
}
let finalName;
if(def.id === true) {
finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`;
} else {
const exportIdx = def.module.providedExports.indexOf(def.id);
finalName = exportIdx < 0 ? "undefined" : `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportIdx}__`;
}
const exportsName = this.rootModule.exportsArgument || "exports";
const content = `/* concated harmony reexport */__webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${finalName}; });\n`;
source.insert(-1, content);
});
} else {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
}
}
}
}
class HarmonyCompatibilityDependencyConcatenatedTemplate {
constructor(originalTemplate, rootModule, modulesMap) {
this.originalTemplate = originalTemplate;
this.rootModule = rootModule;
this.modulesMap = modulesMap;
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.modulesMap.get(this.rootModule).needCompatibilityFlag = true;
}
}
}
module.exports = ConcatenatedModule;

View File

@ -8,16 +8,19 @@ class EnsureChunkConditionsPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
const triesMap = new Map();
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
let changed = false;
chunks.forEach((chunk) => {
chunk.modules.slice().forEach((module) => {
chunk.forEachModule((module) => {
if(!module.chunkCondition) return;
if(!module.chunkCondition(chunk)) {
const usedChunks = module._EnsureChunkConditionsPlugin_usedChunks = (module._EnsureChunkConditionsPlugin_usedChunks || []).concat(chunk);
let usedChunks = triesMap.get(module);
if(!usedChunks) triesMap.set(module, usedChunks = new Set());
usedChunks.add(chunk);
const newChunks = [];
chunk.parents.forEach((parent) => {
if(usedChunks.indexOf(parent) < 0) {
if(!usedChunks.has(parent)) {
parent.addModule(module);
newChunks.push(parent);
}

View File

@ -17,13 +17,13 @@ class FlagIncludedChunksPlugin {
// instead of swapping A and B just bail
// as we loop twice the current A will be B and B then A
if(chunkA.modules.length < chunkB.modules.length) return;
if(chunkA.getNumberOfModules() < chunkB.getNumberOfModules()) return;
if(chunkB.modules.length === 0) return;
if(chunkB.getNumberOfModules() === 0) return;
// is chunkB in chunkA?
for(let i = 0; i < chunkB.modules.length; i++) {
if(chunkA.modules.indexOf(chunkB.modules[i]) < 0) return;
for(const m of chunkB.modulesIterable) {
if(!chunkA.containsModule(m)) return;
}
chunkA.ids.push(chunkB.id);
});

View File

@ -4,23 +4,18 @@
*/
"use strict";
function getChunkIdentifier(chunk) {
return chunk.modules.map((m) => {
return m.identifier();
}).sort().join(", ");
}
class MergeDuplicateChunksPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-chunks-basic", (chunks) => {
const map = {};
const map = Object.create(null);
chunks.slice().forEach((chunk) => {
if(chunk.hasRuntime() || chunk.hasEntryModule()) return;
const ident = getChunkIdentifier(chunk);
if(map[ident]) {
if(map[ident].integrate(chunk, "duplicate"))
const ident = chunk.getModulesIdent();
const otherChunk = map[ident];
if(otherChunk) {
if(otherChunk.integrate(chunk, "duplicate"))
chunks.splice(chunks.indexOf(chunk), 1);
return;
}

View File

@ -0,0 +1,292 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const ConcatenatedModule = require("./ConcatenatedModule");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
class ModuleConcatenationPlugin {
constructor(options) {
if(typeof options !== "object") options = {};
this.options = options;
}
apply(compiler) {
compiler.plugin("compilation", (compilation, params) => {
params.normalModuleFactory.plugin("parser", (parser, parserOptions) => {
parser.plugin("call eval", () => {
parser.state.module.meta.hasEval = true;
});
});
const bailoutReasonMap = new Map();
function setBailoutReason(module, reason) {
bailoutReasonMap.set(module, reason);
module.optimizationBailout.push(reason);
}
function getBailoutReason(module, requestShortener) {
const reason = bailoutReasonMap.get(module);
if(typeof reason === "function") return reason(requestShortener);
return reason;
}
compilation.plugin("optimize-chunk-modules", (chunks, modules) => {
chunks.forEach(chunk => {
const relevantModules = [];
const possibleInners = new Set();
for(const module of chunk.modulesIterable) {
// Only harmony modules are valid for optimization
if(!module.meta || !module.meta.harmonyModule) {
continue;
}
// Module must not be in other chunks
// TODO add an option to allow module to be in other entry points
if(module.getNumberOfChunks() !== 1) {
setBailoutReason(module, "ModuleConcatenation: module is in multiple chunks");
continue;
}
// Because of variable renaming we can't use modules with eval
if(module.meta && module.meta.hasEval) {
setBailoutReason(module, "ModuleConcatenation: eval is used in the module");
continue;
}
relevantModules.push(module);
// Module must not be the entry points
if(chunk.entryModule === module) {
setBailoutReason(module, "ModuleConcatenation (inner): module is an entrypoint");
continue;
}
// Exports must be known (and not dynamic)
if(!Array.isArray(module.providedExports)) {
setBailoutReason(module, "ModuleConcatenation (inner): exports are not known");
continue;
}
// Using dependency variables is not possible as this wraps the code in a function
if(module.variables.length > 0) {
setBailoutReason(module, "ModuleConcatenation (inner): dependency variables are used (i. e. ProvidePlugin)");
continue;
}
// Module must only be used by Harmony Imports
const nonHarmonyReasons = module.reasons.filter(reason => !(reason.dependency instanceof HarmonyImportDependency));
if(nonHarmonyReasons.length > 0) {
const importingModules = new Set(nonHarmonyReasons.map(r => r.module));
setBailoutReason(module, (requestShortener) => {
const names = Array.from(importingModules).map(m => m.readableIdentifier(requestShortener));
return `ModuleConcatenation (inner): module is used with non-harmony imports from ${names.join(", ")}`;
});
continue;
}
possibleInners.add(module);
}
// sort by depth
// modules with lower depth are more likly suited as roots
// this improves performance, because modules already selected as inner are skipped
relevantModules.sort((a, b) => {
return a.depth - b.depth;
});
const concatConfigurations = [];
const usedAsInner = new Set();
for(const currentRoot of relevantModules) {
// when used by another configuration as inner:
// the other configuration is better and we can skip this one
if(usedAsInner.has(currentRoot))
continue;
// create a configuration with the root
const currentConfiguration = new ConcatConfiguration(currentRoot);
// cache failures to add modules
const failureCache = new Map();
// try to add all imports
for(const imp of this.getImports(currentRoot)) {
const problem = this.tryToAdd(currentConfiguration, imp, possibleInners, failureCache);
if(problem) {
failureCache.set(imp, problem);
currentConfiguration.addWarning(imp, problem);
}
}
if(!currentConfiguration.isEmpty()) {
concatConfigurations.push(currentConfiguration);
for(const module of currentConfiguration.modules) {
if(module !== currentConfiguration.rootModule)
usedAsInner.add(module);
}
}
}
// HACK: Sort configurations by length and start with the longest one
// to get the biggers groups possible. Used modules are marked with usedModules
// TODO: Allow to reuse existing configuration while trying to add dependencies.
// This would improve performance. O(n^2) -> O(n)
concatConfigurations.sort((a, b) => {
return b.modules.size - a.modules.size;
});
const usedModules = new Set();
for(const concatConfiguration of concatConfigurations) {
if(usedModules.has(concatConfiguration.rootModule))
continue;
const orderedModules = new Set();
this.addInOrder(concatConfiguration.rootModule, concatConfiguration.modules, orderedModules);
const newModule = new ConcatenatedModule(concatConfiguration.rootModule, Array.from(orderedModules));
for(const warning of concatConfiguration.warnings) {
newModule.optimizationBailout.push((requestShortener) => {
const reason = getBailoutReason(warning[0], requestShortener);
const reasonPrefix = reason ? `: ${reason}` : "";
if(warning[0] === warning[1])
return `ModuleConcatenation: Cannot concat with ${warning[0].readableIdentifier(requestShortener)}${reasonPrefix}`;
else
return `ModuleConcatenation: Cannot concat with ${warning[0].readableIdentifier(requestShortener)} because of ${warning[1].readableIdentifier(requestShortener)}${reasonPrefix}`;
});
}
for(const m of orderedModules) {
usedModules.add(m);
chunk.removeModule(m);
}
chunk.addModule(newModule);
compilation.modules.push(newModule);
if(chunk.entryModule === concatConfiguration.rootModule)
chunk.entryModule = newModule;
newModule.reasons.forEach(reason => reason.dependency.module = newModule);
newModule.dependencies.forEach(dep => {
if(dep.module) {
dep.module.reasons.forEach(reason => {
if(reason.dependency === dep)
reason.module = newModule;
});
}
});
}
compilation.modules = compilation.modules.filter(m => !usedModules.has(m));
});
});
});
}
getImports(module) {
return Array.from(new Set(module.dependencies
// Only harmony Dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module)
// Dependencies are simple enough to concat them
.filter(dep => {
return !module.dependencies.some(d =>
d instanceof HarmonyExportImportedSpecifierDependency &&
d.importDependency === dep &&
!d.id &&
!Array.isArray(dep.module.providedExports)
);
})
// Take the imported module
.map(dep => dep.module)
));
}
tryToAdd(config, module, possibleModules, failureCache) {
const cacheEntry = failureCache.get(module);
if(cacheEntry) {
return cacheEntry;
}
// Already added?
if(config.has(module)) {
return null;
}
// Not possible to add?
if(!possibleModules.has(module)) {
return module;
}
// Clone config to make experimental changes
const testConfig = config.clone();
// Add the module
testConfig.add(module);
// Every module which depends on the added module must be in the configuration too.
for(const reason of module.reasons) {
const problem = this.tryToAdd(testConfig, reason.module, possibleModules, failureCache);
if(problem) {
failureCache.set(module, problem); // cache failures for performance
return problem;
}
}
// Eagerly try to add imports too if possible
for(const imp of this.getImports(module)) {
const problem = this.tryToAdd(testConfig, imp, possibleModules, failureCache);
if(problem) {
config.addWarning(module, problem);
}
}
// Commit experimental changes
config.set(testConfig);
return null;
}
addInOrder(module, unorderedSet, orderedSet) {
if(orderedSet.has(module)) return;
if(!unorderedSet.has(module)) return;
orderedSet.add(module);
for(const imp of this.getImports(module))
this.addInOrder(imp, unorderedSet, orderedSet);
orderedSet.delete(module);
orderedSet.add(module);
}
}
class ConcatConfiguration {
constructor(rootModule) {
this.rootModule = rootModule;
this.modules = new Set([rootModule]);
this.warnings = new Map();
}
add(module) {
this.modules.add(module);
}
has(module) {
return this.modules.has(module);
}
isEmpty() {
return this.modules.size === 1;
}
addWarning(module, problem) {
this.warnings.set(module, problem);
}
clone() {
const clone = new ConcatConfiguration(this.rootModule);
for(const module of this.modules)
clone.add(module);
for(const pair of this.warnings)
clone.addWarning(pair[0], pair[1]);
return clone;
}
set(config) {
this.rootModule = config.rootModule;
this.modules = new Set(config.modules);
this.warnings = new Map(config.warnings);
}
}
module.exports = ModuleConcatenationPlugin;

View File

@ -15,98 +15,84 @@ class OccurrenceOrderPlugin {
const preferEntry = this.preferEntry;
compiler.plugin("compilation", (compilation) => {
compilation.plugin("optimize-module-order", (modules) => {
function entryChunks(m) {
return m.chunks.map((c) => {
const sum = (c.isInitial() ? 1 : 0) + (c.entryModule === m ? 1 : 0);
return sum;
}).reduce((a, b) => {
return a + b;
}, 0);
const occursInInitialChunksMap = new Map();
const occursInAllChunksMap = new Map();
const initialChunkChunkMap = new Map();
const entryCountMap = new Map();
modules.forEach(m => {
let initial = 0;
let entry = 0;
m.forEachChunk(c => {
if(c.isInitial()) initial++;
if(c.entryModule === m) entry++;
});
initialChunkChunkMap.set(m, initial);
entryCountMap.set(m, entry);
});
const countOccursInEntry = (sum, r) => {
if(!r.module) return sum;
return sum + initialChunkChunkMap.get(r.module);
};
const countOccurs = (sum, r) => {
if(!r.module) return sum;
return sum + r.module.getNumberOfChunks();
};
if(preferEntry) {
modules.forEach(m => {
const result = m.reasons.reduce(countOccursInEntry, 0) + initialChunkChunkMap.get(m) + entryCountMap.get(m);
occursInInitialChunksMap.set(m, result);
});
}
function occursInEntry(m) {
if(typeof m.__OccurenceOrderPlugin_occursInEntry === "number") return m.__OccurenceOrderPlugin_occursInEntry;
const result = m.reasons.map((r) => {
if(!r.module) return 0;
return entryChunks(r.module);
}).reduce((a, b) => {
return a + b;
}, 0) + entryChunks(m);
return m.__OccurenceOrderPlugin_occursInEntry = result;
}
modules.forEach(m => {
const result = m.reasons.reduce(countOccurs, 0) + m.getNumberOfChunks() + entryCountMap.get(m);
occursInAllChunksMap.set(m, result);
});
function occurs(m) {
if(typeof m.__OccurenceOrderPlugin_occurs === "number") return m.__OccurenceOrderPlugin_occurs;
const result = m.reasons.map((r) => {
if(!r.module) return 0;
return r.module.chunks.length;
}).reduce((a, b) => {
return a + b;
}, 0) + m.chunks.length + m.chunks.filter((c) => {
return c.entryModule === m;
}).length;
return m.__OccurenceOrderPlugin_occurs = result;
}
modules.sort((a, b) => {
if(preferEntry) {
const aEntryOccurs = occursInEntry(a);
const bEntryOccurs = occursInEntry(b);
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
}
const aOccurs = occurs(a);
const bOccurs = occurs(b);
const aOccurs = occursInAllChunksMap.get(a);
const bOccurs = occursInAllChunksMap.get(b);
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
if(a.identifier() > b.identifier()) return 1;
if(a.identifier() < b.identifier()) return -1;
if(a.index > b.index) return 1;
if(a.index < b.index) return -1;
return 0;
});
// TODO refactor to Map
modules.forEach((m) => {
m.__OccurenceOrderPlugin_occursInEntry = undefined;
m.__OccurenceOrderPlugin_occurs = undefined;
});
});
compilation.plugin("optimize-chunk-order", (chunks) => {
function occursInEntry(c) {
if(typeof c.__OccurenceOrderPlugin_occursInEntry === "number") return c.__OccurenceOrderPlugin_occursInEntry;
const result = c.parents.filter((p) => {
return p.isInitial();
}).length;
return c.__OccurenceOrderPlugin_occursInEntry = result;
}
const occursInInitialChunksMap = new Map();
chunks.forEach(c => {
const result = c.parents.reduce((sum, p) => {
if(p.isInitial()) return sum + 1;
return sum;
}, 0);
return occursInInitialChunksMap.set(c, result);
});
function occurs(c) {
return c.blocks.length;
}
chunks.forEach((c) => {
c.modules.sort((a, b) => {
if(a.identifier() > b.identifier()) return 1;
if(a.identifier() < b.identifier()) return -1;
return 0;
});
});
chunks.sort((a, b) => {
const aEntryOccurs = occursInEntry(a);
const bEntryOccurs = occursInEntry(b);
const aEntryOccurs = occursInInitialChunksMap.get(a);
const bEntryOccurs = occursInInitialChunksMap.get(b);
if(aEntryOccurs > bEntryOccurs) return -1;
if(aEntryOccurs < bEntryOccurs) return 1;
const aOccurs = occurs(a);
const bOccurs = occurs(b);
if(aOccurs > bOccurs) return -1;
if(aOccurs < bOccurs) return 1;
if(a.modules.length > b.modules.length) return -1;
if(a.modules.length < b.modules.length) return 1;
for(let i = 0; i < a.modules.length; i++) {
if(a.modules[i].identifier() > b.modules[i].identifier()) return -1;
if(a.modules[i].identifier() < b.modules[i].identifier()) return 1;
}
return 0;
});
// TODO refactor to Map
chunks.forEach((c) => {
c.__OccurenceOrderPlugin_occursInEntry = undefined;
return a.compareTo(b);
});
});
});

View File

@ -4,59 +4,31 @@
*/
"use strict";
function chunkContainsModule(chunk, module) {
const chunks = module.chunks;
const modules = chunk.modules;
if(chunks.length < modules.length) {
return chunks.indexOf(chunk) >= 0;
} else {
return modules.indexOf(module) >= 0;
}
}
function hasModule(chunk, module, checkedChunks) {
if(chunkContainsModule(chunk, module)) return [chunk];
if(chunk.containsModule(module)) return [chunk];
if(chunk.parents.length === 0) return false;
return allHaveModule(chunk.parents.filter((c) => {
return checkedChunks.indexOf(c) < 0;
return !checkedChunks.has(c);
}), module, checkedChunks);
}
function allHaveModule(someChunks, module, checkedChunks) {
if(!checkedChunks) checkedChunks = [];
var chunks = [];
if(!checkedChunks) checkedChunks = new Set();
var chunks = new Set();
for(var i = 0; i < someChunks.length; i++) {
checkedChunks.push(someChunks[i]);
checkedChunks.add(someChunks[i]);
var subChunks = hasModule(someChunks[i], module, checkedChunks);
if(!subChunks) return false;
for(var index = 0; index < subChunks.length; index++) {
var item = subChunks[index];
if(!chunks.length || chunks.indexOf(item) < 0) {
chunks.push(item);
}
chunks.add(item);
}
}
return chunks;
}
function debugIds(chunks) {
var list = [];
for(var i = 0; i < chunks.length; i++) {
var debugId = chunks[i].debugId;
if(typeof debugId !== "number") {
return "no";
}
list.push(debugId);
}
list.sort();
return list.join(",");
}
class RemoveParentModulesPlugin {
apply(compiler) {
compiler.plugin("compilation", (compilation) => {
@ -67,19 +39,21 @@ class RemoveParentModulesPlugin {
// TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311
var cache = Object.create(null);
var modules = chunk.modules.slice();
var modules = chunk.getModules();
for(var i = 0; i < modules.length; i++) {
var module = modules[i];
var dId = debugIds(module.chunks);
var dId = module.getChunkIdsIdent();
var parentChunksWithModule;
if((dId in cache) && dId !== "no") {
if(dId === null) {
parentChunksWithModule = allHaveModule(chunk.parents, module);
} else if(dId in cache) {
parentChunksWithModule = cache[dId];
} else {
parentChunksWithModule = cache[dId] = allHaveModule(chunk.parents, module);
}
if(parentChunksWithModule) {
module.rewriteChunkInReasons(chunk, parentChunksWithModule);
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));
chunk.removeModule(module);
}
}

View File

@ -114,6 +114,7 @@ exportPlugins(exports.optimize = {}, "./optimize", [
"DedupePlugin",
"LimitChunkCountPlugin",
"MinChunkSizePlugin",
"ModuleConcatenationPlugin",
"OccurrenceOrderPlugin",
"UglifyJsPlugin"
]);

View File

@ -10,11 +10,12 @@
"ajv-keywords": "^2.0.0",
"async": "^2.1.2",
"enhanced-resolve": "^3.0.0",
"escope": "^3.6.0",
"interpret": "^1.0.0",
"json-loader": "^0.5.4",
"json5": "^0.5.1",
"loader-runner": "^2.3.0",
"loader-utils": "^0.2.16",
"loader-utils": "^1.1.0",
"memory-fs": "~0.4.1",
"mkdirp": "~0.5.0",
"node-libs-browser": "^2.0.0",
@ -23,7 +24,7 @@
"tapable": "~0.2.5",
"uglify-js": "^2.8.27",
"watchpack": "^1.3.1",
"webpack-sources": "^0.2.3",
"webpack-sources": "^1.0.1",
"yargs": "^6.0.0"
},
"license": "MIT",
@ -36,20 +37,20 @@
"coffee-loader": "~0.7.1",
"coffee-script": "^1.10.0",
"coveralls": "^2.11.2",
"css-loader": "~0.25.0",
"css-loader": "^0.28.3",
"es6-promise-polyfill": "^1.1.1",
"eslint": "3.12.2",
"eslint-plugin-node": "^3.0.5",
"express": "~4.13.1",
"extract-text-webpack-plugin": "^2.0.0-beta",
"file-loader": "~0.9.0",
"file-loader": "^0.11.1",
"i18n-webpack-plugin": "^0.3.0",
"istanbul": "^0.4.5",
"jade": "^1.11.0",
"jade-loader": "~0.8.0",
"js-beautify": "^1.5.10",
"less": "^2.5.1",
"less-loader": "^2.0.0",
"less-loader": "^4.0.3",
"lodash": "^4.17.4",
"mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.0.0",
@ -61,12 +62,12 @@
"should": "^11.1.1",
"simple-git": "^1.65.0",
"sinon": "^2.3.2",
"style-loader": "~0.13.0",
"style-loader": "^0.18.1",
"url-loader": "~0.5.0",
"val-loader": "~0.5.0",
"val-loader": "^1.0.2",
"vm-browserify": "~0.0.0",
"webpack-dev-middleware": "^1.9.0",
"worker-loader": "~0.7.0"
"worker-loader": "^0.8.0"
},
"engines": {
"node": ">=4.3.0 <5.0.0 || >=5.10"

View File

@ -224,6 +224,9 @@
},
"strictExportPresence": {
"type": "boolean"
},
"strictThisContextOnImports": {
"type": "boolean"
}
},
"type": "object"
@ -1030,6 +1033,18 @@
"assetsSort": {
"type": "string",
"description": "sort the assets by that field"
},
"providedExports": {
"type": "boolean",
"description": "show exports provided by modules"
},
"usedExports": {
"type": "boolean",
"description": "show exports used by modules"
},
"optimizationBailout": {
"type": "boolean",
"description": "show reasons why optimization bailed out for modules"
}
}
},
@ -1042,6 +1057,7 @@
"errors-only",
"minimal",
"normal",
"detailed",
"verbose"
]
}

View File

@ -22,7 +22,7 @@ function getTestSpecificArguments(testDirectory) {
try {
return loadOptsFile(path.join(testDirectory, "test.opts"));
} catch(e) {
return [];
return null;
}
}
@ -51,7 +51,7 @@ describe("BinTestCases", function() {
category.tests.forEach(function(testName) {
const testDirectory = path.join(casesPath, category.name, testName);
const testArgs = defaultArgs.concat(getTestSpecificArguments(testDirectory));
const testArgs = getTestSpecificArguments(testDirectory) || defaultArgs;
const testAssertions = require(path.join(testDirectory, "test.js"));
const outputPath = path.join(path.resolve(casesPath, "../js/bin"), category.name, testName);

View File

@ -102,7 +102,7 @@ describe("Chunk", () => {
});
describe("and the chunk does contain this module", function() {
beforeEach(function() {
ChunkInstance.modules = [module];
ChunkInstance._modules = new Set([module]);
});
it("calls module.removeChunk with itself and returns true", function() {
ChunkInstance.removeModule(module).should.eql(true);

View File

@ -223,21 +223,21 @@ describe("Compiler (caching)", function() {
const helper = compile("./temp-cache-fixture/c", options, (stats, files) => {
// Built the first time
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");
setTimeout(() => {
helper.runAgain((stats, files, iteration) => {
// Not built when cached the second run
stats.modules[0].name.should.containEql("a.js");
//stats.modules[0].built.should.be.exactly(false, "a.js should not have built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
//stats.modules[1].built.should.be.exactly(false, "a.js should not have built");
const aContent = fs.readFileSync(tempFixture.aFilepath).toString().replace("This is a", "This is a MODIFIED");
@ -247,11 +247,11 @@ describe("Compiler (caching)", function() {
helper.runAgain((stats, files, iteration) => {
// And only a.js built after it was modified
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");
done();
});
@ -269,20 +269,20 @@ describe("Compiler (caching)", function() {
const helper = compile("./temp-cache-fixture/c", options, (stats, files) => {
// Built the first time
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
stats.modules[0].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("c.js");
stats.modules[1].built.should.be.exactly(true, "c.js should have been built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");
helper.runAgain((stats, files, iteration) => {
// Not built when cached the second run
stats.modules[0].name.should.containEql("a.js");
//stats.modules[0].built.should.be.exactly(false, "a.js should not have built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
//stats.modules[1].built.should.be.exactly(false, "a.js should not have built");
const aContent = fs.readFileSync(tempFixture.aFilepath).toString().replace("This is a", "This is a MODIFIED");
@ -291,11 +291,11 @@ describe("Compiler (caching)", function() {
helper.runAgain((stats, files, iteration) => {
// And only a.js built after it was modified
stats.modules[0].name.should.containEql("a.js");
stats.modules[0].built.should.be.exactly(true, "a.js should have been built");
stats.modules[0].name.should.containEql("c.js");
//stats.modules[0].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("c.js");
//stats.modules[1].built.should.be.exactly(false, "c.js should not have built");
stats.modules[1].name.should.containEql("a.js");
stats.modules[1].built.should.be.exactly(true, "a.js should have been built");
done();
});

View File

@ -78,7 +78,7 @@ describe("Compiler", () => {
Object.keys(files).should.be.eql(["/main.js"]);
const bundle = files["/main.js"];
bundle.should.containEql("function __webpack_require__(");
bundle.should.containEql("__webpack_require__(/*! ./a */ 0);");
bundle.should.containEql("__webpack_require__(/*! ./a */ 1);");
bundle.should.containEql("./c.js");
bundle.should.containEql("./a.js");
bundle.should.containEql("This is a");
@ -101,7 +101,7 @@ describe("Compiler", () => {
bundle.should.containEql("./main1.js");
bundle.should.containEql("./a.js");
bundle.should.containEql("./b.js");
bundle.should.containEql("./~/m1/a.js");
bundle.should.containEql("./node_modules/m1/a.js");
bundle.should.containEql("This is a");
bundle.should.containEql("This is b");
bundle.should.containEql("This is m1/a");

View File

@ -210,7 +210,6 @@ describe("MultiStats", () => {
"(xyz890-compilation) xyz890-warning-1",
"(xyz890-compilation) xyz890-warning-2"
],
hash: "abc123xyz890",
children: [{
warnings: ["abc123-warning"],
errors: ["abc123-error"],

View File

@ -154,22 +154,7 @@ describe("Stats", () => {
describe("Presets", () => {
describe("presetToOptions", () => {
it("returns correct object with 'Normal'", () => {
Stats.presetToOptions("Normal").should.eql({
assets: false,
version: false,
timings: true,
hash: true,
entrypoints: false,
chunks: true,
chunkModules: false,
errorDetails: true,
reasons: false,
depth: false,
usedExports: false,
providedExports: false,
colors: true,
performance: true
});
Stats.presetToOptions("Normal").should.eql({});
});
it("truthy values behave as 'normal'", () => {
const normalOpts = Stats.presetToOptions("normal");
@ -182,25 +167,7 @@ describe("Stats", () => {
});
it("returns correct object with 'none'", () => {
Stats.presetToOptions("none").should.eql({
hash: false,
version: false,
timings: false,
assets: false,
entrypoints: false,
chunks: false,
chunkModules: false,
modules: false,
reasons: false,
depth: false,
usedExports: false,
providedExports: false,
children: false,
source: false,
errors: false,
errorDetails: false,
warnings: false,
publicPath: false,
performance: false
all: false
});
});
it("falsy values behave as 'none'", () => {

View File

@ -22,6 +22,11 @@ describe("TestCases", () => {
});
[{
name: "normal"
}, {
name: "concat",
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
}, {
name: "hot",
plugins: [

View File

@ -5,9 +5,9 @@ module.exports = function testAssertions(code, stdout, stderr) {
stdout.should.be.ok();
stdout[4].should.containEql("null.js");
stdout[5].should.match(/a\.js.*\{0\}/);
stdout[5].should.match(/multi.*index\.js.*a\.js/); // should have multi-file entry
stdout[6].should.match(/index\.js.*\{0\}/);
stdout[7].should.match(/multi.*index\.js.*a\.js/); // should have multi-file entry
stdout[7].should.match(/a\.js.*\{0\}/);
stderr.should.be.empty();
};

View File

@ -1 +1,6 @@
--entry ./index.js
--entry ./a.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node

View File

@ -1 +1,6 @@
--entry foo=./a.js
--entry ./index.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node

View File

@ -6,8 +6,8 @@ module.exports = function testAssertions(code, stdout, stderr) {
stdout.should.be.ok();
stdout[4].should.containEql("null.js");
stdout[5].should.containEql("main.js"); // non-hyphenated arg ./a.js should create chunk "main"
stdout[6].should.match(/a\.js.*\{1\}/); // a.js should be in chunk 1
stdout[7].should.match(/index\.js.*\{0\}/); // index.js should be in chunk 0
stdout[6].should.match(/index\.js.*\{0\}/); // index.js should be in chunk 0
stdout[7].should.match(/a\.js.*\{1\}/); // a.js should be in chunk 1
stderr.should.be.empty();
};

View File

@ -1 +1,6 @@
./a.js
--entry ./index.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node

View File

@ -1 +1,6 @@
--entry ./index.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node
--plugin webpack/lib/optimize/UglifyJsPlugin

View File

@ -0,0 +1 @@
module.exports = "foo";

View File

@ -0,0 +1,8 @@
"use strict";
module.exports = function testAssertions(code, stdout, stderr) {
stderr.should.be.empty();
code.should.be.eql(0);
stdout.should.be.empty();
};

View File

@ -0,0 +1,5 @@
--entry ./index.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node
--display none

View File

@ -0,0 +1 @@
module.exports = "foo";

View File

@ -0,0 +1,8 @@
"use strict";
module.exports = function testAssertions(code, stdout, stderr) {
code.should.be.eql(0);
stdout.should.be.empty();
stderr.should.be.empty();
};

View File

@ -0,0 +1,6 @@
var path = require("path");
module.exports = {
entry: path.resolve(__dirname, "./index"),
stats: "none"
};

View File

@ -8,9 +8,10 @@ module.exports = function testAssertions(code, stdout, stderr) {
stdout[1].should.containEql("Version: ");
stdout[2].should.containEql("Time: ");
stdout[4].should.containEql("\u001b[1m\u001b[32mnull.js\u001b[39m\u001b[22m");
stdout[5].should.not.containEql("./index.js");
stdout[5].should.not.containEql("[built]");
stdout[5].should.containEql("1 hidden module");
stdout[5].should.containEql("chunk");
stdout[6].should.not.containEql("./index.js");
stdout[6].should.not.containEql("[built]");
stdout[6].should.containEql("1 module");
stderr.should.be.empty();
};

View File

@ -1 +1,6 @@
--entry ./index.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node
--watch

View File

@ -1 +1,6 @@
--entry ./index.js
--config ./webpack.config.js
--output-filename [name].js
--output-chunk-filename [id].chunk.js
--target async-node
--watch

View File

@ -1,3 +1,7 @@
var fs = require("fs");
var path = require("path");
module.exports = fs.readFileSync(path.join(path.dirname(__filename), "stylesheet.css"), "utf-8") + "\n.generated { color: red; }";
module.exports = function() {
return {
code: fs.readFileSync(path.join(path.dirname(__filename), "stylesheet.css"), "utf-8") + "\n.generated { color: red; }"
};
};

View File

@ -0,0 +1 @@
export var named = "named";

View File

@ -0,0 +1 @@
export * from "./a";

View File

@ -0,0 +1 @@
export { named } from "./b";

View File

@ -0,0 +1,5 @@
import { named } from "./c";
it("should have the correct values", function() {
named.should.be.eql("named");
});

View File

@ -0,0 +1,16 @@
import { ns as ns1 } from "./module1";
const ns2 = require("./module2").ns;
it("should allow to export a namespace object (concated)", function() {
ns1.should.be.eql({
a: "a",
b: "b"
});
});
it("should allow to export a namespace object (exposed)", function() {
ns2.should.be.eql({
a: "a",
b: "b"
});
});

View File

@ -0,0 +1,2 @@
import * as ns from "./ns1";
export { ns };

View File

@ -0,0 +1,2 @@
import * as ns from "./ns2";
export { ns };

View File

@ -0,0 +1,2 @@
export var a = "a";
export var b = "b";

View File

@ -0,0 +1,2 @@
export var a = "a";
export var b = "b";

View File

@ -0,0 +1 @@
export default "named";

View File

@ -0,0 +1,2 @@
import named from "./a";
export { named }

View File

@ -0,0 +1,2 @@
import { named } from "./b";
export { named }

View File

@ -0,0 +1,5 @@
var c = require("./c");
it("should have the correct values", function() {
c.named.should.be.eql("named");
});

View File

@ -0,0 +1 @@
export { default } from "./b";

View File

@ -0,0 +1 @@
export { default } from "./c";

View File

@ -0,0 +1 @@
export default "ok";

View File

@ -0,0 +1,9 @@
import value from "./a";
it("should have the correct values", function() {
value.should.be.eql("ok");
});
// prevent scope hoisting of b
require("./b");

View File

@ -0,0 +1 @@
exports.named = "named";

View File

@ -0,0 +1 @@
export { named } from "./a";

View File

@ -0,0 +1 @@
export { named } from "./b";

Some files were not shown because too many files have changed in this diff Show More