mirror of https://github.com/webpack/webpack.git
commit
463eeb52e0
|
|
@ -4,6 +4,7 @@ language: node_js
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
```
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("../build-common");
|
||||
|
|
@ -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 |
|
|
@ -0,0 +1,3 @@
|
|||
export * from "c";
|
||||
import * as d from "d";
|
||||
export { d };
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// module a
|
||||
export var a = "a";
|
||||
export * from "shared";
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
// module b
|
||||
export function a() {
|
||||
return "b";
|
||||
};
|
||||
|
|
@ -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";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// module cjs (commonjs)
|
||||
exports.c = "e";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// module d
|
||||
export var a = "d";
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// shared module
|
||||
export var x = "x";
|
||||
export * from "shared2";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
// shared2 module
|
||||
export var y = "y";
|
||||
|
|
@ -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)
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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}}
|
||||
```
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
var webpack = require("../../");
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new webpack.optimize.ModuleConcatenationPlugin()
|
||||
]
|
||||
};
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
116
lib/Chunk.js
116
lib/Chunk.js
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(",") + "})";
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class FlagInitialModulesAsUsedPlugin {
|
|||
if(!chunk.isInitial()) {
|
||||
return;
|
||||
}
|
||||
chunk.modules.forEach((module) => {
|
||||
chunk.forEachModule((module) => {
|
||||
module.usedExports = true;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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) {`);
|
||||
|
|
|
|||
104
lib/Module.js
104
lib/Module.js
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(/^!|!$/, "");
|
||||
}
|
||||
|
|
|
|||
266
lib/Stats.js
266
lib/Stats.js
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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}`;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ exportPlugins(exports.optimize = {}, "./optimize", [
|
|||
"DedupePlugin",
|
||||
"LimitChunkCountPlugin",
|
||||
"MinChunkSizePlugin",
|
||||
"ModuleConcatenationPlugin",
|
||||
"OccurrenceOrderPlugin",
|
||||
"UglifyJsPlugin"
|
||||
]);
|
||||
|
|
|
|||
17
package.json
17
package.json
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
|
|
|
|||
|
|
@ -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'", () => {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ describe("TestCases", () => {
|
|||
});
|
||||
[{
|
||||
name: "normal"
|
||||
}, {
|
||||
name: "concat",
|
||||
plugins: [
|
||||
new webpack.optimize.ModuleConcatenationPlugin()
|
||||
]
|
||||
}, {
|
||||
name: "hot",
|
||||
plugins: [
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
module.exports = "foo";
|
||||
|
|
@ -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();
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
--entry ./index.js
|
||||
--output-filename [name].js
|
||||
--output-chunk-filename [id].chunk.js
|
||||
--target async-node
|
||||
--display none
|
||||
|
|
@ -0,0 +1 @@
|
|||
module.exports = "foo";
|
||||
|
|
@ -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();
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
var path = require("path");
|
||||
|
||||
module.exports = {
|
||||
entry: path.resolve(__dirname, "./index"),
|
||||
stats: "none"
|
||||
};
|
||||
|
|
@ -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();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1 +1,6 @@
|
|||
--entry ./index.js
|
||||
--config ./webpack.config.js
|
||||
--output-filename [name].js
|
||||
--output-chunk-filename [id].chunk.js
|
||||
--target async-node
|
||||
--watch
|
||||
|
|
|
|||
|
|
@ -1 +1,6 @@
|
|||
--entry ./index.js
|
||||
--config ./webpack.config.js
|
||||
--output-filename [name].js
|
||||
--output-chunk-filename [id].chunk.js
|
||||
--target async-node
|
||||
--watch
|
||||
|
|
|
|||
|
|
@ -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; }"
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export var named = "named";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export * from "./a";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { named } from "./b";
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { named } from "./c";
|
||||
|
||||
it("should have the correct values", function() {
|
||||
named.should.be.eql("named");
|
||||
});
|
||||
|
|
@ -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"
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import * as ns from "./ns1";
|
||||
export { ns };
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import * as ns from "./ns2";
|
||||
export { ns };
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export var a = "a";
|
||||
export var b = "b";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export var a = "a";
|
||||
export var b = "b";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export default "named";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import named from "./a";
|
||||
export { named }
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { named } from "./b";
|
||||
export { named }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
var c = require("./c");
|
||||
|
||||
it("should have the correct values", function() {
|
||||
c.named.should.be.eql("named");
|
||||
});
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./b";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./c";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export default "ok";
|
||||
|
|
@ -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");
|
||||
|
|
@ -0,0 +1 @@
|
|||
exports.named = "named";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { named } from "./a";
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { named } from "./b";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue