Merge branch 'next' into import-context-filter

This commit is contained in:
Tobias Koppers 2017-10-16 13:54:27 +02:00
commit 6a6cd57b26
199 changed files with 2805 additions and 1341 deletions

View File

@ -283,6 +283,11 @@ module.exports = function(yargs, argv, convertOptions) {
}
}
function addPlugin(options, plugin) {
ensureArray(options, "plugins");
options.plugins.unshift(plugin);
}
ifArgPair("entry", function(name, entry) {
if(typeof options.entry[name] !== "undefined" && options.entry[name] !== null) {
options.entry[name] = [].concat(options.entry[name]).concat(entry);
@ -328,9 +333,8 @@ module.exports = function(yargs, argv, convertOptions) {
}, function() {
defineObject = {};
}, function() {
ensureArray(options, "plugins");
var DefinePlugin = require("../lib/DefinePlugin");
options.plugins.push(new DefinePlugin(defineObject));
addPlugin(options, new DefinePlugin(defineObject));
});
ifArg("output-path", function(value) {
@ -398,15 +402,13 @@ module.exports = function(yargs, argv, convertOptions) {
mapArgToBoolean("cache");
ifBooleanArg("hot", function() {
ensureArray(options, "plugins");
var HotModuleReplacementPlugin = require("../lib/HotModuleReplacementPlugin");
options.plugins.push(new HotModuleReplacementPlugin());
addPlugin(options, new HotModuleReplacementPlugin());
});
ifBooleanArg("debug", function() {
ensureArray(options, "plugins");
var LoaderOptionsPlugin = require("../lib/LoaderOptionsPlugin");
options.plugins.push(new LoaderOptionsPlugin({
addPlugin(options, new LoaderOptionsPlugin({
debug: true
}));
});
@ -438,41 +440,36 @@ module.exports = function(yargs, argv, convertOptions) {
});
ifArg("optimize-max-chunks", function(value) {
ensureArray(options, "plugins");
var LimitChunkCountPlugin = require("../lib/optimize/LimitChunkCountPlugin");
options.plugins.push(new LimitChunkCountPlugin({
addPlugin(options, new LimitChunkCountPlugin({
maxChunks: parseInt(value, 10)
}));
});
ifArg("optimize-min-chunk-size", function(value) {
ensureArray(options, "plugins");
var MinChunkSizePlugin = require("../lib/optimize/MinChunkSizePlugin");
options.plugins.push(new MinChunkSizePlugin({
addPlugin(options, new MinChunkSizePlugin({
minChunkSize: parseInt(value, 10)
}));
});
ifBooleanArg("optimize-minimize", function() {
ensureArray(options, "plugins");
var UglifyJsPlugin = require("../lib/optimize/UglifyJsPlugin");
var LoaderOptionsPlugin = require("../lib/LoaderOptionsPlugin");
options.plugins.push(new UglifyJsPlugin({
addPlugin(options, new UglifyJsPlugin({
sourceMap: options.devtool && (options.devtool.indexOf("sourcemap") >= 0 || options.devtool.indexOf("source-map") >= 0)
}));
options.plugins.push(new LoaderOptionsPlugin({
addPlugin(options, new LoaderOptionsPlugin({
minimize: true
}));
});
ifArg("prefetch", function(request) {
ensureArray(options, "plugins");
var PrefetchPlugin = require("../lib/PrefetchPlugin");
options.plugins.push(new PrefetchPlugin(request));
addPlugin(options, new PrefetchPlugin(request));
});
ifArg("provide", function(value) {
ensureArray(options, "plugins");
var idx = value.indexOf("=");
var name;
if(idx >= 0) {
@ -482,12 +479,11 @@ module.exports = function(yargs, argv, convertOptions) {
name = value;
}
var ProvidePlugin = require("../lib/ProvidePlugin");
options.plugins.push(new ProvidePlugin(name, value));
addPlugin(options, new ProvidePlugin(name, value));
});
ifArg("plugin", function(value) {
ensureArray(options, "plugins");
options.plugins.push(loadPlugin(value));
addPlugin(options, loadPlugin(value));
});
mapArgToBoolean("bail");

View File

@ -106,6 +106,9 @@
## Scope Hoisting
[scope-hoisting](scope-hoisting)
## Pure Module
[pure-module](pure-module)
## Source Map
[source-map](source-map)

View File

@ -1,7 +1,7 @@
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
loaders: [
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({

View File

@ -1,6 +1,6 @@
module.exports = {
module: {
loaders: [
rules: [
{ test: /\.coffee$/, loader: "coffee-loader" }
]
},

View File

@ -1,7 +1,7 @@
var ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
module: {
loaders: [
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({

View File

@ -1,6 +1,6 @@
module.exports = {
module: {
loaders: [
rules: [
{ test: /\.css$/, loader: "css-loader" }
]
}

View File

@ -12,7 +12,7 @@ module.exports = {
filename: "[name].js"
},
module: {
loaders: [
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({

View File

@ -0,0 +1,280 @@
This example shows how the `sideEffects` flag for library authors works.
The example contains a large library, `big-module`. `big-module` contains multiple child modules: `a`, `b` and `c`. The exports from the child modules are re-exported in the entry module (`index.js`) of the library. A consumer uses **some** of the exports, importing them from the library via `import { a, b } from "big-module"`. According to the EcmaScript spec, all child modules _must_ be evaluated because they could contain side effects.
The `"sideEffects": false` flag in `big-module`'s `package.json` indicates that the package's modules have no side effects (on evaluation) and only expose exports. This allows tools like webpack to optimize re-exports. In the case `import { a, b } from "big-module-with-flag"` is rewritten to `import { a } from "big-module-with-flag/a"; import { b } from "big-module-with-flag/b"`.
The example contains two variants of `big-module`. `big-module` has no `sideEffects` flag and `big-module-with-flag` has the `sideEffects` flag. The example client imports `a` and `b` from each of the variants.
After being built by webpack, the output bundle contains `index.js` `a.js` `b.js` `c.js` from `big-module`, but only `a.js` and `b.js` from `big-module-with-flag`.
Advantages:
* Smaller bundles
* Faster bootup
# example.js
``` javascript
import { a as a1, b as b1 } from "big-module";
import { a as a2, b as b2 } from "big-module-with-flag";
console.log(
a1,
b1,
a2,
b2
);
```
# node_modules/big-module/package.json
``` javascript
{
"name": "big-module"
}
```
# node_modules/big-module-with-flag/package.json
``` javascript
{
"name": "big-module-with-flag",
"sideEffects": false
}
```
# node_modules/big-module(-with-flag)/index.js
``` javascript
export { a } from "./a";
export { b } from "./b";
export { c } from "./c";
```
# js/output.js
<details><summary><code>/******/ (function(modules) { /* webpackBootstrap */ })</code></summary>
``` javascript
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // 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/";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 4);
/******/ })
/************************************************************************/
```
</details>
``` javascript
/******/ ([
/* 0 */
/*!******************************************!*\
!*** ./node_modules/big-module/index.js ***!
\******************************************/
/*! exports provided: a, b, c */
/*! exports used: a, b */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */1);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "a", function() { return _a__WEBPACK_IMPORTED_MODULE_0__["a"]; });
/* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */2);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "b", function() { return _b__WEBPACK_IMPORTED_MODULE_1__["a"]; });
/* harmony import */ var _c__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./c */3);
/***/ }),
/* 1 */
/*!**************************************!*\
!*** ./node_modules/big-module/a.js ***!
\**************************************/
/*! exports provided: a */
/*! exports used: a */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
const a = "a";
/***/ }),
/* 2 */
/*!**************************************!*\
!*** ./node_modules/big-module/b.js ***!
\**************************************/
/*! exports provided: b */
/*! exports used: b */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return b; });
const b = "b";
/***/ }),
/* 3 */
/*!**************************************!*\
!*** ./node_modules/big-module/c.js ***!
\**************************************/
/*! exports provided: c */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* unused harmony export c */
const c = "c";
/***/ }),
/* 4 */
/*!********************!*\
!*** ./example.js ***!
\********************/
/*! exports provided: */
/*! all exports used */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var big_module__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! big-module */0);
/* harmony import */ var big_module_with_flag__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! big-module-with-flag */5);
/* harmony import */ var big_module_with_flag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! big-module-with-flag */6);
console.log(
big_module__WEBPACK_IMPORTED_MODULE_0__["a"],
big_module__WEBPACK_IMPORTED_MODULE_0__["b"],
big_module_with_flag__WEBPACK_IMPORTED_MODULE_1__["a"],
big_module_with_flag__WEBPACK_IMPORTED_MODULE_2__["a" /* b */]
);
/***/ }),
/* 5 */
/*!************************************************!*\
!*** ./node_modules/big-module-with-flag/a.js ***!
\************************************************/
/*! exports provided: a */
/*! exports used: a */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return a; });
const a = "a";
/***/ }),
/* 6 */
/*!************************************************!*\
!*** ./node_modules/big-module-with-flag/b.js ***!
\************************************************/
/*! exports provided: b */
/*! exports used: b */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return b; });
const b = "b";
/***/ })
/******/ ]);
```
# Info
## Uncompressed
```
Hash: 0f036598352d4d3caffa
Version: webpack 3.5.6
Asset Size Chunks Chunk Names
output.js 6.2 kB 0 [emitted] main
Entrypoint main = output.js
chunk {0} output.js (main) 342 bytes [entry] [rendered]
> main [4] ./example.js
[4] ./example.js 149 bytes {0} [built]
[no exports]
+ 6 hidden modules
```
## Minimized (uglify-js, no zip)
```
Hash: 0f036598352d4d3caffa
Version: webpack 3.5.6
Asset Size Chunks Chunk Names
output.js 1.05 kB 0 [emitted] main
Entrypoint main = output.js
chunk {0} output.js (main) 342 bytes [entry] [rendered]
> main [4] ./example.js
[4] ./example.js 149 bytes {0} [built]
[no exports]
+ 6 hidden modules
```

View File

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

View File

@ -0,0 +1,9 @@
import { a as a1, b as b1 } from "big-module";
import { a as a2, b as b2 } from "big-module-with-flag";
console.log(
a1,
b1,
a2,
b2
);

View File

@ -0,0 +1 @@
export const a = "a";

View File

@ -0,0 +1 @@
export const b = "b";

View File

@ -0,0 +1 @@
export const c = "c";

View File

@ -0,0 +1,3 @@
export { a } from "./a";
export { b } from "./b";
export { c } from "./c";

View File

@ -0,0 +1,4 @@
{
"name": "big-module-with-flag",
"side-effects": false
}

1
examples/side-effects/node_modules/big-module/a.js generated vendored Normal file
View File

@ -0,0 +1 @@
export const a = "a";

1
examples/side-effects/node_modules/big-module/b.js generated vendored Normal file
View File

@ -0,0 +1 @@
export const b = "b";

1
examples/side-effects/node_modules/big-module/c.js generated vendored Normal file
View File

@ -0,0 +1 @@
export const c = "c";

View File

@ -0,0 +1,3 @@
export { a } from "./a";
export { b } from "./b";
export { c } from "./c";

View File

@ -0,0 +1,3 @@
{
"name": "big-module"
}

View File

@ -0,0 +1,58 @@
This example shows how the `sideEffects` flag for library authors works.
The example contains a large library, `big-module`. `big-module` contains multiple child modules: `a`, `b` and `c`. The exports from the child modules are re-exported in the entry module (`index.js`) of the library. A consumer uses **some** of the exports, importing them from the library via `import { a, b } from "big-module"`. According to the EcmaScript spec, all child modules _must_ be evaluated because they could contain side effects.
The `"sideEffects": false` flag in `big-module`'s `package.json` indicates that the package's modules have no side effects (on evaluation) and only expose exports. This allows tools like webpack to optimize re-exports. In the case `import { a, b } from "big-module-with-flag"` is rewritten to `import { a } from "big-module-with-flag/a"; import { b } from "big-module-with-flag/b"`.
The example contains two variants of `big-module`. `big-module` has no `sideEffects` flag and `big-module-with-flag` has the `sideEffects` flag. The example client imports `a` and `b` from each of the variants.
After being built by webpack, the output bundle contains `index.js` `a.js` `b.js` `c.js` from `big-module`, but only `a.js` and `b.js` from `big-module-with-flag`.
Advantages:
* Smaller bundles
* Faster bootup
# example.js
``` javascript
{{example.js}}
```
# node_modules/big-module/package.json
``` javascript
{{node_modules/big-module/package.json}}
```
# node_modules/big-module-with-flag/package.json
``` javascript
{{node_modules/big-module-with-flag/package.json}}
```
# node_modules/big-module(-with-flag)/index.js
``` javascript
{{node_modules/big-module-with-flag/index.js}}
```
# js/output.js
``` javascript
{{js/output.js}}
```
# Info
## Uncompressed
```
{{stdout}}
```
## Minimized (uglify-js, no zip)
```
{{min:stdout}}
```

View File

@ -22,7 +22,7 @@ class AmdMainTemplatePlugin {
typeof m.request === "object" ? m.request.amd : m.request
));
const externalsArguments = externals.map((m) =>
Template.toIdentifier(`__WEBPACK_EXTERNAL_MODULE_${m.id}__`)
`__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${m.id}`)}__`
).join(", ");
if(this.name) {

View File

@ -7,10 +7,11 @@
const ConcatSource = require("webpack-sources").ConcatSource;
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
const Template = require("./Template");
const wrapComment = (str) => {
if(!str.includes("\n")) return `/*! ${str} */`;
return `/*!\n * ${str.split("\n").join("\n * ")}\n */`;
if(!str.includes("\n")) return Template.toComment(str);
return `/*!\n * ${str.replace(/\*\//g, "* /").split("\n").join("\n * ")}\n */`;
};
class BannerPlugin {

View File

@ -20,8 +20,6 @@ class CachePlugin {
} else {
const registerCacheToCompiler = (compiler, cache) => {
compiler.plugin("this-compilation", compilation => {
// TODO remove notCacheable for webpack 4
if(!compilation.notCacheable) {
compilation.cache = cache;
compilation.plugin("child-compiler", (childCompiler, compilerName, compilerIndex) => {
if(cache) {
@ -35,11 +33,6 @@ class CachePlugin {
registerCacheToCompiler(childCompiler, childCache);
}
});
} else if(this.watching) {
compilation.warnings.push(
new Error(`CachePlugin - Cache cannot be used because of: ${compilation.notCacheable}`)
);
}
});
};
registerCacheToCompiler(compiler, this.cache);

View File

@ -30,9 +30,9 @@ class Chunk {
this.name = name;
this._modules = new SortableSet(undefined, sortByIdentifier);
this.entrypoints = [];
this.chunks = [];
this.parents = [];
this.blocks = [];
this._chunks = new SortableSet(undefined, sortById);
this._parents = new SortableSet(undefined, sortById);
this._blocks = new SortableSet();
this.origins = [];
this.files = [];
this.rendered = false;
@ -61,9 +61,82 @@ class Chunk {
throw new Error("Chunk.initial was removed. Use isInitial()");
}
/**
* @return {Array} - an array containing the chunks
*/
getChunks() {
return Array.from(this._chunks);
}
getNumberOfChunks() {
return this._chunks.size;
}
get chunksIterable() {
return this._chunks;
}
/**
* @return {Array} - an array containing the parents
*/
getParents() {
return Array.from(this._parents);
}
setParents(newParents) {
this._parents.clear();
for(const p of newParents)
this._parents.add(p);
}
mapParents(fn) {
return Array.from(this._parents, fn);
}
getNumberOfParents() {
return this._parents.size;
}
hasParent(parent) {
return this._parents.has(parent);
}
get parentsIterable() {
return this._parents;
}
/**
* @return {Array} - an array containing the blocks
*/
getBlocks() {
return Array.from(this._blocks);
}
setBlocks(newBlocks) {
this._blocks.clear();
for(const p of newBlocks)
this._blocks.add(p);
}
mapBlocks(fn) {
return Array.from(this._blocks, fn);
}
getNumberOfBlocks() {
return this._blocks.size;
}
hasBlock(block) {
return this._blocks.has(block);
}
get blocksIterable() {
return this._blocks;
}
hasRuntime() {
if(this.entrypoints.length === 0) return false;
return this.entrypoints[0].chunks[0] === this;
return this.entrypoints[0].getRuntimeChunk() === this;
}
isInitial() {
@ -88,11 +161,19 @@ class Chunk {
}
addChunk(chunk) {
return this.addToCollection(this.chunks, chunk);
if(this._chunks.has(chunk)) {
return false;
}
this._chunks.add(chunk);
return true;
}
addParent(parentChunk) {
return this.addToCollection(this.parents, parentChunk);
if(!this._parents.has(parentChunk)) {
this._parents.add(parentChunk);
return true;
}
return false;
}
addModule(module) {
@ -104,7 +185,11 @@ class Chunk {
}
addBlock(block) {
return this.addToCollection(this.blocks, block);
if(!this._blocks.has(block)) {
this._blocks.add(block);
return true;
}
return false;
}
removeModule(module) {
@ -116,19 +201,17 @@ class Chunk {
}
removeChunk(chunk) {
const idx = this.chunks.indexOf(chunk);
if(idx >= 0) {
this.chunks.splice(idx, 1);
chunk.removeParent(this);
return true;
}
if(!this._chunks.has(chunk)) {
return false;
}
this._chunks.delete(chunk);
chunk.removeParent(this);
return true;
}
removeParent(chunk) {
const idx = this.parents.indexOf(chunk);
if(idx >= 0) {
this.parents.splice(idx, 1);
if(this._parents.delete(chunk)) {
chunk.removeChunk(this);
return true;
}
@ -201,20 +284,17 @@ class Chunk {
remove(reason) {
// cleanup modules
// Array.from is used here to create a clone, because removeChunk modifies this._modules
Array.from(this._modules).forEach(module => {
for(const module of Array.from(this._modules)) {
module.removeChunk(this);
});
// cleanup parents
this.parents.forEach(parentChunk => {
// remove this chunk from its parents
const idx = parentChunk.chunks.indexOf(this);
if(idx >= 0) {
parentChunk.chunks.splice(idx, 1);
}
// cleanup parents
for(const parentChunk of this._parents) {
// remove this chunk from its parents
parentChunk._chunks.delete(this);
// cleanup "sub chunks"
this.chunks.forEach(chunk => {
this._chunks.forEach(chunk => {
/**
* remove this chunk as "intermediary" and connect
* it "sub chunks" and parents directly
@ -224,24 +304,21 @@ class Chunk {
// add "sub chunk" to parent
parentChunk.addChunk(chunk);
});
});
}
/**
* we need to iterate again over the chunks
* to remove this from the chunks parents.
* This can not be done in the above loop
* as it is not garuanteed that `this.parents` contains anything.
* as it is not garuanteed that `this._parents` contains anything.
*/
this.chunks.forEach(chunk => {
for(const chunk of this._chunks) {
// remove this as parent of every "sub chunk"
const idx = chunk.parents.indexOf(this);
if(idx >= 0) {
chunk.parents.splice(idx, 1);
chunk._parents.delete(this);
}
});
// cleanup blocks
this.blocks.forEach(block => {
for(const block of this._blocks) {
const idx = block.chunks.indexOf(this);
if(idx >= 0) {
block.chunks.splice(idx, 1);
@ -250,7 +327,7 @@ class Chunk {
block.chunkReason = reason;
}
}
});
}
}
moveModule(module, otherChunk) {
@ -261,20 +338,14 @@ class Chunk {
}
replaceChunk(oldChunk, newChunk) {
const idx = this.chunks.indexOf(oldChunk);
if(idx >= 0) {
this.chunks.splice(idx, 1);
}
this._chunks.delete(oldChunk);
if(this !== newChunk && newChunk.addParent(this)) {
this.addChunk(newChunk);
}
}
replaceParentChunk(oldParentChunk, newParentChunk) {
const idx = this.parents.indexOf(oldParentChunk);
if(idx >= 0) {
this.parents.splice(idx, 1);
}
this._parents.delete(oldParentChunk);
if(this !== newParentChunk && newParentChunk.addChunk(this)) {
this.addParent(newParentChunk);
}
@ -286,24 +357,29 @@ class Chunk {
}
// 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));
for(const module of Array.from(otherChunk._modules)) {
otherChunk.moveModule(module, this);
}
otherChunk._modules.clear();
otherChunk.parents.forEach(parentChunk => parentChunk.replaceChunk(otherChunk, this));
otherChunk.parents.length = 0;
for(const parentChunk of otherChunk._parents) {
parentChunk.replaceChunk(otherChunk, this);
}
otherChunk._parents.clear();
otherChunk.chunks.forEach(chunk => chunk.replaceParentChunk(otherChunk, this));
otherChunk.chunks.length = 0;
for(const chunk of otherChunk._chunks) {
chunk.replaceParentChunk(otherChunk, this);
}
otherChunk._chunks.clear();
otherChunk.blocks.forEach(b => {
for(const b of otherChunk._blocks) {
b.chunks = b.chunks ? b.chunks.map(c => {
return c === otherChunk ? this : c;
}) : [this];
b.chunkReason = reason;
this.addBlock(b);
});
otherChunk.blocks.length = 0;
}
otherChunk._blocks.clear();
otherChunk.origins.forEach(origin => {
this.origins.push(origin);
@ -318,31 +394,31 @@ class Chunk {
origin.reasons.unshift(reason);
}
});
this.chunks = this.chunks.filter(chunk => {
return chunk !== otherChunk && chunk !== this;
});
this.parents = this.parents.filter(parentChunk => {
return parentChunk !== otherChunk && parentChunk !== this;
});
this._chunks.delete(otherChunk);
this._chunks.delete(this);
for(const parentChunk of this._parents) {
if(parentChunk === otherChunk || parentChunk === this)
this._parents.delete(parentChunk);
}
return true;
}
split(newChunk) {
this.blocks.forEach(block => {
newChunk.blocks.push(block);
for(const block of this._blocks) {
newChunk._blocks.add(block);
block.chunks.push(newChunk);
});
this.chunks.forEach(chunk => {
newChunk.chunks.push(chunk);
chunk.parents.push(newChunk);
});
this.parents.forEach(parentChunk => {
parentChunk.chunks.push(newChunk);
newChunk.parents.push(parentChunk);
});
this.entrypoints.forEach(entrypoint => {
}
for(const chunk of this._chunks) {
newChunk.addChunk(chunk);
chunk._parents.add(newChunk);
}
for(const parentChunk of this._parents) {
parentChunk.addChunk(newChunk);
newChunk._parents.add(parentChunk);
}
for(const entrypoint of this.entrypoints) {
entrypoint.insertChunk(newChunk, this);
});
}
}
isEmpty() {
@ -361,7 +437,7 @@ class Chunk {
return false;
}
if(this.isInitial()) {
if(otherChunk.parents.length !== 1 || otherChunk.parents[0] !== this) {
if(otherChunk.getNumberOfParents() !== 1 || otherChunk.getParents()[0] !== this) {
return false;
}
}
@ -416,7 +492,7 @@ class Chunk {
if(chunk.name)
chunkNameMap[chunk.id] = chunk.name;
}
chunk.chunks.forEach(addChunk);
chunk._chunks.forEach(addChunk);
}(this));
return {
hash: chunkHashMap,
@ -428,7 +504,7 @@ class Chunk {
this._modules.sortWith(sortByFn || sortById);
}
sortItems() {
sortItems(sortChunks) {
this.sortModules();
this.origins.sort((a, b) => {
const aIdent = a.module.identifier();
@ -441,8 +517,10 @@ class Chunk {
if(origin.reasons)
origin.reasons.sort();
});
this.parents.sort(sortById);
this.chunks.sort(sortById);
if(sortChunks) {
this._parents.sort();
this._chunks.sort();
}
}
toString() {
@ -451,29 +529,55 @@ class Chunk {
checkConstraints() {
const chunk = this;
chunk.chunks.forEach((child, idx) => {
if(chunk.chunks.indexOf(child) !== idx)
throw new Error(`checkConstraints: duplicate child in chunk ${chunk.debugId} ${child.debugId}`);
if(child.parents.indexOf(chunk) < 0)
for(const child of chunk._chunks) {
if(!child._parents.has(chunk))
throw new Error(`checkConstraints: child missing parent ${chunk.debugId} -> ${child.debugId}`);
});
chunk.parents.forEach((parentChunk, idx) => {
if(chunk.parents.indexOf(parentChunk) !== idx)
throw new Error(`checkConstraints: duplicate parent in chunk ${chunk.debugId} ${parentChunk.debugId}`);
if(parentChunk.chunks.indexOf(chunk) < 0)
}
for(const parentChunk of chunk._parents) {
if(!parentChunk._chunks.has(chunk))
throw new Error(`checkConstraints: parent missing child ${parentChunk.debugId} <- ${chunk.debugId}`);
});
}
}
}
Object.defineProperty(Chunk.prototype, "modules", {
configurable: false,
get: util.deprecate(function() {
return Array.from(this._modules);
return this._modules.getFrozenArray();
}, "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.")
});
Object.defineProperty(Chunk.prototype, "chunks", {
configurable: false,
get: util.deprecate(function() {
return this._chunks.getFrozenArray();
}, "Chunk.chunks: Use Chunk.getChunks() instead"),
set() {
throw new Error("Readonly. Use Chunk.addChunk/removeChunk/getChunks to access/modify chunks.");
}
});
Object.defineProperty(Chunk.prototype, "parents", {
configurable: false,
get: util.deprecate(function() {
return this._parents.getFrozenArray();
}, "Chunk.parents: Use Chunk.getParents() instead"),
set: util.deprecate(function(value) {
this.setParents(value);
}, "Chunk.parents: Use Chunk.addParent/removeParent/setParents to modify parents.")
});
Object.defineProperty(Chunk.prototype, "blocks", {
configurable: false,
get: util.deprecate(function() {
return this._blocks.getFrozenArray();
}, "Chunk.blocks: Use Chunk.getBlocks() instead"),
set: util.deprecate(function(value) {
this.setBlocks(value);
}, "Chunk.blocks: Use Chunk.addBlock/removeBlock/setBlocks to modify blocks.")
});
module.exports = Chunk;

View File

@ -573,6 +573,12 @@ class Compilation extends Tapable {
seal(callback) {
const self = this;
self.applyPlugins0("seal");
while(self.applyPluginsBailResult1("optimize-dependencies-basic", self.modules) ||
self.applyPluginsBailResult1("optimize-dependencies", self.modules) ||
self.applyPluginsBailResult1("optimize-dependencies-advanced", self.modules)) { /* empty */ }
self.applyPlugins1("after-optimize-dependencies", self.modules);
self.nextFreeModuleIndex = 0;
self.nextFreeModuleIndex2 = 0;
self.preparedChunks.forEach(preparedChunk => {
@ -910,8 +916,14 @@ class Compilation extends Tapable {
// For each Dependency in the graph
const iteratorDependency = d => {
// We skip Dependencies without Reference
const ref = d.getReference();
if(!ref) {
return;
}
// We skip Dependencies without Module pointer
if(!d.module) {
const refModule = ref.module;
if(!refModule) {
return;
}
// We skip weak Dependencies
@ -919,12 +931,13 @@ class Compilation extends Tapable {
return;
}
// We connect Module and Chunk when not already done
if(chunk.addModule(d.module)) {
d.module.addChunk(chunk);
if(chunk.addModule(refModule)) {
refModule.addChunk(chunk);
// And enqueue the Module for traversal
queue.push({
block: d.module,
block: refModule,
module: refModule,
chunk
});
}
@ -1051,7 +1064,7 @@ class Compilation extends Tapable {
// Remove all unconnected chunks
for(const chunk of allCreatedChunks) {
if(chunk.parents.length === 0)
if(chunk.getNumberOfParents() === 0)
chunk.remove("unconnected");
}
}
@ -1203,7 +1216,7 @@ class Compilation extends Tapable {
const chunks = this.chunks;
for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
chunks[indexChunk].sortItems();
chunks[indexChunk].sortItems(false);
}
}
@ -1217,7 +1230,7 @@ class Compilation extends Tapable {
const chunks = this.chunks;
for(let indexChunk = 0; indexChunk < chunks.length; indexChunk++) {
chunks[indexChunk].sortItems();
chunks[indexChunk].sortItems(true);
}
const byMessage = (a, b) => {

View File

@ -39,6 +39,12 @@ class DependenciesBlock {
this.dependencies.push(dependency);
}
removeDependency(dependency) {
const idx = this.dependencies.indexOf(dependency);
if(idx >= 0)
this.dependencies.splice(idx, 1);
}
updateHash(hash) {
function updateHash(i) {
i.updateHash(hash);

View File

@ -8,6 +8,7 @@ const compareLocations = require("./compareLocations");
class Dependency {
constructor() {
this.module = null;
this.weak = false;
}
isEqualResource() {
@ -19,6 +20,7 @@ class Dependency {
if(!this.module) return null;
return {
module: this.module,
weak: this.weak,
importedNames: true, // true: full object, false: only sideeffects/no export, array of strings: the exports with this names
};
}
@ -43,11 +45,6 @@ class Dependency {
disconnect() {
this.module = null;
}
// TODO: remove in webpack 3
compare(a, b) {
return compareLocations(a.loc, b.loc);
}
}
Dependency.compare = (a, b) => compareLocations(a.loc, b.loc);

View File

@ -16,14 +16,19 @@ class Entrypoint {
}
insertChunk(chunk, before) {
const oldIdx = this.chunks.indexOf(chunk);
const idx = this.chunks.indexOf(before);
if(idx >= 0) {
this.chunks.splice(idx, 0, chunk);
} else {
if(idx < 0) {
throw new Error("before chunk not found");
}
if(oldIdx >= 0 && oldIdx > idx) {
this.chunks.splice(oldIdx, 1);
this.chunks.splice(idx, 0, chunk);
} else if(oldIdx < 0) {
this.chunks.splice(idx, 0, chunk);
chunk.entrypoints.push(this);
}
}
getFiles() {
const files = [];
@ -38,6 +43,10 @@ class Entrypoint {
return files;
}
getRuntimeChunk() {
return this.chunks[0];
}
}
module.exports = Entrypoint;

View File

@ -70,7 +70,7 @@ class ExternalModule extends Module {
}
getSourceForAmdOrUmdExternal(id, optional, request) {
const externalVariable = Template.toIdentifier(`__WEBPACK_EXTERNAL_MODULE_${id}__`);
const externalVariable = `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${id}`)}__`;
const missingModuleError = optional ? this.checkExternalVariable(externalVariable, request) : "";
return `${missingModuleError}module.exports = ${externalVariable};`;
}

View File

@ -39,6 +39,13 @@ class FlagDependencyUsagePlugin {
else
module.usedExports = false;
// for a module without side effects we stop tracking usage here when no export is used
// This module won't be evaluated in this case
if(module.sideEffectFree) {
if(module.usedExports === false) return;
if(Array.isArray(module.usedExports) && module.usedExports.length === 0) return;
}
queue.push([module, module.usedExports]);
}

View File

@ -13,6 +13,7 @@ class FlagInitialModulesAsUsedPlugin {
return;
}
chunk.forEachModule((module) => {
module.used = true;
module.usedExports = true;
});
});

View File

@ -5,6 +5,7 @@
"use strict";
const ConcatSource = require("webpack-sources").ConcatSource;
const Template = require("./Template");
class FunctionModuleTemplatePlugin {
apply(moduleTemplate) {
@ -29,21 +30,21 @@ class FunctionModuleTemplatePlugin {
source.add(" !*** " + req.replace(/\*\//g, "*_/") + " ***!\n");
source.add(" \\****" + req.replace(/./g, "*") + "****/\n");
if(Array.isArray(module.providedExports) && module.providedExports.length === 0)
source.add("/*! no exports provided */\n");
source.add(Template.toComment("no exports provided") + "\n");
else if(Array.isArray(module.providedExports))
source.add("/*! exports provided: " + module.providedExports.join(", ") + " */\n");
source.add(Template.toComment("exports provided: " + module.providedExports.join(", ")) + "\n");
else if(module.providedExports)
source.add("/*! dynamic exports provided */\n");
source.add(Template.toComment("no static exports found") + "\n");
if(Array.isArray(module.usedExports) && module.usedExports.length === 0)
source.add("/*! no exports used */\n");
source.add(Template.toComment("no exports used") + "\n");
else if(Array.isArray(module.usedExports))
source.add("/*! exports used: " + module.usedExports.join(", ") + " */\n");
source.add(Template.toComment("exports used: " + module.usedExports.join(", ")) + "\n");
else if(module.usedExports)
source.add("/*! all exports used */\n");
source.add(Template.toComment("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(Template.toComment(`${text}`) + "\n");
});
}
source.add(moduleSource);

View File

@ -561,7 +561,6 @@ module.exports = function() {
type: "self-accept-error-handler-errored",
moduleId: moduleId,
error: err2,
orginalError: err, // TODO remove in webpack 4
originalError: err
});
}

View File

@ -166,7 +166,7 @@ module.exports = class HotModuleReplacementPlugin {
.replace(/\$require\$/g, this.requireFn)
.replace(/\$hash\$/g, JSON.stringify(hash))
.replace(/\$requestTimeout\$/g, requestTimeout)
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.chunks.length > 0 ? "for(var chunkId in installedChunks)" : `var chunkId = ${JSON.stringify(chunk.id)};`)
.replace(/\/\*foreachInstalledChunks\*\//g, chunk.getNumberOfChunks() > 0 ? "for(var chunkId in installedChunks)" : `var chunkId = ${JSON.stringify(chunk.id)};`)
]);
});

View File

@ -11,18 +11,20 @@ class JsonpChunkTemplatePlugin {
chunkTemplate.plugin("render", function(modules, chunk) {
const jsonpFunction = this.outputOptions.jsonpFunction;
const source = new ConcatSource();
source.add(`${jsonpFunction}(${JSON.stringify(chunk.ids)},`);
source.add(`(window[${JSON.stringify(jsonpFunction)}] = window[${JSON.stringify(jsonpFunction)}] || []).push([${JSON.stringify(chunk.ids)},`);
source.add(modules);
const entries = [chunk.entryModule].filter(Boolean).map(m => m.id);
const entries = [chunk.entryModule]
.filter(Boolean)
.map(m => [m.id].concat(chunk.entrypoints[0].chunks.map(c => c.id)));
if(entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
}
source.add(")");
source.add("])");
return source;
});
chunkTemplate.plugin("hash", function(hash) {
hash.update("JsonpChunkTemplatePlugin");
hash.update("3");
hash.update("4");
hash.update(`${this.outputOptions.jsonpFunction}`);
hash.update(`${this.outputOptions.library}`);
});

View File

@ -12,7 +12,6 @@ module.exports = function() {
function hotDownloadUpdateChunk(chunkId) { // eslint-disable-line no-unused-vars
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.type = "text/javascript";
script.charset = "utf-8";
script.src = $require$.p + $hotChunkFilename$;
$crossOriginLoading$;

View File

@ -9,8 +9,15 @@ const Template = require("./Template");
class JsonpMainTemplatePlugin {
apply(mainTemplate) {
function needChunkLoadingCode(chunk) {
var otherChunksInEntry = chunk.entrypoints.some(function(entrypoint) {
return entrypoint.chunks.length > 1;
});
var onDemandChunks = chunk.getNumberOfChunks() > 0;
return otherChunksInEntry || onDemandChunks;
}
mainTemplate.plugin("local-vars", function(source, chunk) {
if(chunk.chunks.length > 0) {
if(needChunkLoadingCode(chunk)) {
return this.asString([
source,
"",
@ -19,7 +26,9 @@ class JsonpMainTemplatePlugin {
this.indent(
chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(",\n")
),
"};"
"};",
"",
"var scheduledModules = [];"
]);
}
return source;
@ -48,18 +57,20 @@ class JsonpMainTemplatePlugin {
});
return this.asString([
"var script = document.createElement('script');",
"script.type = 'text/javascript';",
"script.charset = 'utf-8';",
"script.async = true;",
`script.timeout = ${chunkLoadTimeout};`,
crossOriginLoading ? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)};` : "",
`if (${this.requireFn}.nc) {`,
this.indent(`script.setAttribute("nonce", ${this.requireFn}.nc);`),
"}",
`script.src = ${this.requireFn}.p + ${scriptSrcPath};`,
`var timeout = setTimeout(onScriptComplete, ${chunkLoadTimeout});`,
"var timeout = setTimeout(function(){",
this.indent([
"onScriptComplete({ type: 'timeout', target: script });",
]),
`}, ${chunkLoadTimeout});`,
"script.onerror = script.onload = onScriptComplete;",
"function onScriptComplete() {",
"function onScriptComplete(event) {",
this.indent([
"// avoid mem leaks in IE.",
"script.onerror = script.onload = null;",
@ -68,7 +79,14 @@ class JsonpMainTemplatePlugin {
"if(chunk !== 0) {",
this.indent([
"if(chunk) {",
this.indent("chunk[1](new Error('Loading chunk ' + chunkId + ' failed.'));"),
this.indent([
"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
"var realSrc = event && event.target && event.target.src;",
"var error = new Error('Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')');",
"error.type = errorType;",
"error.request = realSrc;",
"chunk[1](error);"
]),
"}",
"installedChunks[chunkId] = undefined;"
]),
@ -110,7 +128,7 @@ class JsonpMainTemplatePlugin {
]);
});
mainTemplate.plugin("require-extensions", function(source, chunk) {
if(chunk.chunks.length === 0) return source;
if(chunk.getNumberOfChunks() === 0) return source;
return this.asString([
source,
@ -120,15 +138,14 @@ class JsonpMainTemplatePlugin {
]);
});
mainTemplate.plugin("bootstrap", function(source, chunk, hash) {
if(chunk.chunks.length > 0) {
var jsonpFunction = this.outputOptions.jsonpFunction;
if(needChunkLoadingCode(chunk)) {
return this.asString([
source,
"",
"// install a JSONP callback for chunk loading",
`var parentJsonpFunction = window[${JSON.stringify(jsonpFunction)}];`,
`window[${JSON.stringify(jsonpFunction)}] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {`,
"function webpackJsonpCallback(data) {",
this.indent([
"var chunkIds = data[0], moreModules = data[1], executeModules = data[2];",
"// add \"moreModules\" to the modules object,",
"// then flag all \"chunkIds\" as loaded and fire callback",
"var moduleId, chunkId, i = 0, resolves = [], result;",
@ -148,15 +165,28 @@ class JsonpMainTemplatePlugin {
"}"
]),
"}",
"if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);",
"if(parentJsonpFunction) parentJsonpFunction(data);",
"while(resolves.length) {",
this.indent("resolves.shift()();"),
"}",
this.entryPointInChildren(chunk) ? [
"if(executeModules) {",
"scheduledModules.push.apply(scheduledModules, executeModules || []);",
"",
"for(i = 0; i < scheduledModules.length; i++) {",
this.indent([
"for(i=0; i < executeModules.length; i++) {",
this.indent(`result = ${this.requireFn}(${this.requireFn}.s = executeModules[i]);`),
"var scheduledModule = scheduledModules[i];",
"var fullfilled = true;",
"for(var j = 1; j < scheduledModule.length; j++) {",
this.indent([
"var depId = scheduledModule[j];",
"if(installedChunks[depId] !== 0) fullfilled = false;"
]),
"}",
"if(fullfilled) {",
this.indent([
"scheduledModules.splice(i--, 1);",
"result = " + this.requireFn + "(" + this.requireFn + ".s = scheduledModule[0]);",
]),
"}"
]),
"}",
@ -168,6 +198,21 @@ class JsonpMainTemplatePlugin {
}
return source;
});
mainTemplate.plugin("startup", function(source, chunk, hash) {
if(needChunkLoadingCode(chunk)) {
var jsonpFunction = this.outputOptions.jsonpFunction;
return this.asString([
`var jsonpArray = window[${JSON.stringify(jsonpFunction)}] = window[${JSON.stringify(jsonpFunction)}] || [];`,
"var parentJsonpFunction = jsonpArray.push.bind(jsonpArray);",
"jsonpArray.push = webpackJsonpCallback;",
"jsonpArray = jsonpArray.slice();",
"for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);",
"",
source
]);
}
return source;
});
mainTemplate.plugin("hot-bootstrap", function(source, chunk, hash) {
const hotUpdateChunkFilename = this.outputOptions.hotUpdateChunkFilename;
const hotUpdateMainFilename = this.outputOptions.hotUpdateMainFilename;
@ -200,7 +245,7 @@ this[${JSON.stringify(hotUpdateFunction)}] = ${runtimeSource}`;
});
mainTemplate.plugin("hash", function(hash) {
hash.update("jsonp");
hash.update("4");
hash.update("5");
hash.update(`${this.outputOptions.filename}`);
hash.update(`${this.outputOptions.chunkFilename}`);
hash.update(`${this.outputOptions.jsonpFunction}`);

View File

@ -99,7 +99,7 @@ module.exports = class MainTemplate extends Template {
});
this.plugin("require-extensions", (source, chunk, hash) => {
const buf = [];
if(chunk.chunks.length > 0) {
if(chunk.getNumberOfChunks() > 0) {
buf.push("// This file contains only the entry chunk.");
buf.push("// The chunk loading function for additional chunks");
buf.push(`${this.requireFn}.e = function requireEnsure(chunkId) {`);
@ -199,13 +199,16 @@ module.exports = class MainTemplate extends Template {
entryPointInChildren(chunk) {
const checkChildren = (chunk, alreadyCheckedChunks) => {
return chunk.chunks.some((child) => {
if(alreadyCheckedChunks.indexOf(child) >= 0) return;
alreadyCheckedChunks.push(child);
return child.hasEntryModule() || checkChildren(child, alreadyCheckedChunks);
});
for(const child of chunk.chunksIterable) {
if(!alreadyCheckedChunks.has(child)) {
alreadyCheckedChunks.add(child);
if(child.hasEntryModule() || checkChildren(child, alreadyCheckedChunks)) {
return true;
}
}
}
};
return checkChildren(chunk, []);
return checkChildren(chunk, new Set()) === true;
}
getPublicPath(options) {

View File

@ -129,6 +129,10 @@ class Module extends DependenciesBlock {
return this._chunks.size;
}
get chunksIterable() {
return this._chunks;
}
hasEqualsChunks(otherModule) {
if(this._chunks.size !== otherModule._chunks.size) return false;
this._chunks.sortWith(sortByDebugId);
@ -233,7 +237,7 @@ Object.defineProperty(Module.prototype, "entry", {
Object.defineProperty(Module.prototype, "chunks", {
configurable: false,
get: util.deprecate(function() {
return Array.from(this._chunks);
return this._chunks.getFrozenArray();
}, "Module.chunks: Use Module.forEachChunk/mapChunks/getNumberOfChunks/isInChunk/addChunk/removeChunk instead"),
set() {
throw new Error("Readonly. Use Module.addChunk/removeChunk to modify chunks.");

View File

@ -75,6 +75,63 @@ module.exports = class MultiCompiler extends Tapable {
});
}
validateDependencies(callback) {
const edges = new Set();
const missing = [];
const targetFound = (compiler) => {
for(const edge of edges) {
if(edge.target === compiler) {
return true;
}
}
return false;
};
const sortEdges = (e1, e2) => {
return e1.source.name.localeCompare(e2.source.name) ||
e1.target.name.localeCompare(e2.target.name);
};
for(const source of this.compilers) {
if(source.dependencies) {
for(const dep of source.dependencies) {
const target = this.compilers.find((c) => c.name === dep);
if(!target) {
missing.push(dep);
} else {
edges.add({
source,
target
});
}
}
}
}
const errors = missing.map((m) => `Compiler dependency \`${m}\` not found.`);
const stack = this.compilers.filter((c) => !targetFound(c));
while(stack.length > 0) {
const current = stack.pop();
for(const edge of edges) {
if(edge.source === current) {
edges.delete(edge);
const target = edge.target;
if(!targetFound(target)) {
stack.push(target);
}
}
}
}
if(edges.size > 0) {
const lines = Array.from(edges).sort(sortEdges).map(edge => `${edge.source.name} -> ${edge.target.name}`);
lines.unshift("Circular dependency found in compiler dependencies.");
errors.unshift(lines.join("\n"));
}
if(errors.length > 0) {
const message = errors.join("\n");
callback(new Error(message));
return false;
}
return true;
}
runWithDependencies(compilers, fn, callback) {
let fulfilledNames = {};
let remainingCompilers = compilers;
@ -109,6 +166,7 @@ module.exports = class MultiCompiler extends Tapable {
let watchings = [];
let allStats = this.compilers.map(() => null);
let compilerStatus = this.compilers.map(() => false);
if(this.validateDependencies(handler)) {
this.runWithDependencies(this.compilers, (compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
let firstRun = true;
@ -136,12 +194,14 @@ module.exports = class MultiCompiler extends Tapable {
}, () => {
// ignore
});
}
return new MultiWatching(watchings, this);
}
run(callback) {
const allStats = this.compilers.map(() => null);
if(this.validateDependencies(callback)) {
this.runWithDependencies(this.compilers, ((compiler, callback) => {
const compilerIdx = this.compilers.indexOf(compiler);
compiler.run((err, stats) => {
@ -154,6 +214,7 @@ module.exports = class MultiCompiler extends Tapable {
callback(null, new MultiStats(allStats));
});
}
}
purgeInputFileSystem() {
this.compilers.forEach((compiler) => {

View File

@ -5,6 +5,7 @@
"use strict";
const Module = require("./Module");
const Template = require("./Template");
const RawSource = require("webpack-sources").RawSource;
class MultiModule extends Module {
@ -58,7 +59,7 @@ class MultiModule extends Module {
str.push("module.exports = ");
str.push("__webpack_require__(");
if(outputOptions.pathinfo)
str.push(`/*! ${dep.request} */`);
str.push(Template.toComment(dep.request));
str.push(`${JSON.stringify(dep.module.id)}`);
str.push(")");
} else {

View File

@ -132,13 +132,10 @@ class NormalModule extends Module {
resolve(context, request, callback) {
resolver.resolve({}, context, request, callback);
},
resolveSync(context, request) {
return resolver.resolveSync({}, context, request);
},
emitFile: (name, content, sourceMap) => {
this.assets[name] = this.createSourceForAsset(name, content, sourceMap);
},
options: options,
rootContext: options.context,
webpack: true,
sourceMap: !!this.useSourceMap,
_module: this,

View File

@ -46,7 +46,7 @@ class NormalModuleFactory extends Tapable {
constructor(context, resolvers, options) {
super();
this.resolvers = resolvers;
this.ruleSet = new RuleSet(options.rules || options.loaders);
this.ruleSet = new RuleSet(options.rules);
this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
this.context = context || "";
this.parserCache = {};
@ -72,7 +72,7 @@ class NormalModuleFactory extends Tapable {
// Ignored
if(!result) return callback();
let createdModule = this.applyPluginsBailResult("create-module", result);
let createdModule = this.applyPluginsBailResult1("create-module", result);
if(!createdModule) {
if(!result.request) {
@ -89,7 +89,7 @@ class NormalModuleFactory extends Tapable {
);
}
createdModule = this.applyPluginsWaterfall0("module", createdModule);
createdModule = this.applyPluginsWaterfall1("module", createdModule, result);
return callback(null, createdModule);
});

View File

@ -30,7 +30,7 @@ class OptionsDefaulter {
}
process(options) {
// TODO: change this for webpack 4: options = Object.assign({}, options);
options = Object.assign({}, options);
for(let name in this.defaults) {
switch(this.config[name]) {
case undefined:
@ -56,7 +56,7 @@ class OptionsDefaulter {
throw new Error("OptionsDefaulter cannot process " + this.config[name]);
}
}
// TODO: change this for webpack 4: return options;
return options;
}
set(name, config, def) {

View File

@ -10,6 +10,7 @@ const acorn = require("acorn-dynamic-import").default;
const Tapable = require("tapable");
const vm = require("vm");
const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
const StackedSetMap = require("./util/StackedSetMap");
function joinRanges(startRange, endRange) {
if(!endRange) return startRange;
@ -37,6 +38,36 @@ const POSSIBLE_AST_OPTIONS = [{
}
}];
class TrackingSet {
constructor(set) {
this.set = set;
this.set2 = new Set();
this.stack = set.stack;
}
add(item) {
this.set2.add(item);
return this.set.add(item);
}
delete(item) {
this.set2.delete(item);
return this.set.delete(item);
}
has(item) {
return this.set.has(item);
}
createChild() {
return this.set.createChild();
}
getAddedItems() {
return this.set2;
}
}
class Parser extends Tapable {
constructor(options) {
super();
@ -207,8 +238,8 @@ class Parser extends Tapable {
let res;
let name;
if(expr.argument.type === "Identifier") {
name = this.scope.renames["$" + expr.argument.name] || expr.argument.name;
if(this.scope.definitions.indexOf(name) === -1) {
name = this.scope.renames.get(expr.argument.name) || expr.argument.name;
if(!this.scope.definitions.has(name)) {
res = this.applyPluginsBailResult1("evaluate typeof " + name, expr);
if(res !== undefined) return res;
}
@ -248,8 +279,8 @@ class Parser extends Tapable {
return new BasicEvaluatedExpression().setString("undefined").setRange(expr.range);
});
this.plugin("evaluate Identifier", function(expr) {
const name = this.scope.renames["$" + expr.name] || expr.name;
if(this.scope.definitions.indexOf(expr.name) === -1) {
const name = this.scope.renames.get(expr.name) || expr.name;
if(!this.scope.definitions.has(expr.name)) {
const result = this.applyPluginsBailResult1("evaluate Identifier " + name, expr);
if(result) return result;
return new BasicEvaluatedExpression().setIdentifier(name).setRange(expr.range);
@ -258,7 +289,7 @@ class Parser extends Tapable {
}
});
this.plugin("evaluate ThisExpression", function(expr) {
const name = this.scope.renames.$this;
const name = this.scope.renames.get("this");
if(name) {
const result = this.applyPluginsBailResult1("evaluate Identifier " + name, expr);
if(result) return result;
@ -283,7 +314,7 @@ class Parser extends Tapable {
const param = this.evaluateExpression(expr.callee.object);
if(!param) return;
const property = expr.callee.property.name || expr.callee.property.value;
return this.applyPluginsBailResult("evaluate CallExpression ." + property, expr, param);
return this.applyPluginsBailResult2("evaluate CallExpression ." + property, expr, param);
});
this.plugin("evaluate CallExpression .replace", function(expr, param) {
if(!param.isString()) return;
@ -656,8 +687,8 @@ class Parser extends Tapable {
// Declarations
prewalkFunctionDeclaration(statement) {
if(statement.id) {
this.scope.renames["$" + statement.id.name] = undefined;
this.scope.definitions.push(statement.id.name);
this.scope.renames.set(statement.id.name, null);
this.scope.definitions.add(statement.id.name);
}
}
@ -677,20 +708,20 @@ class Parser extends Tapable {
prewalkImportDeclaration(statement) {
const source = statement.source.value;
this.applyPluginsBailResult("import", statement, source);
this.applyPluginsBailResult2("import", statement, source);
statement.specifiers.forEach(function(specifier) {
const name = specifier.local.name;
this.scope.renames["$" + name] = undefined;
this.scope.definitions.push(name);
this.scope.renames.set(name, null);
this.scope.definitions.add(name);
switch(specifier.type) {
case "ImportDefaultSpecifier":
this.applyPluginsBailResult("import specifier", statement, source, "default", name);
this.applyPluginsBailResult4("import specifier", statement, source, "default", name);
break;
case "ImportSpecifier":
this.applyPluginsBailResult("import specifier", statement, source, specifier.imported.name, name);
this.applyPluginsBailResult4("import specifier", statement, source, specifier.imported.name, name);
break;
case "ImportNamespaceSpecifier":
this.applyPluginsBailResult("import specifier", statement, source, null, name);
this.applyPluginsBailResult4("import specifier", statement, source, null, name);
break;
}
}, this);
@ -700,7 +731,7 @@ class Parser extends Tapable {
let source;
if(statement.source) {
source = statement.source.value;
this.applyPluginsBailResult("export import", statement, source);
this.applyPluginsBailResult2("export import", statement, source);
} else {
this.applyPluginsBailResult1("export", statement);
}
@ -708,13 +739,16 @@ class Parser extends Tapable {
if(/Expression$/.test(statement.declaration.type)) {
throw new Error("Doesn't occur?");
} else {
if(!this.applyPluginsBailResult("export declaration", statement, statement.declaration)) {
const pos = this.scope.definitions.length;
if(!this.applyPluginsBailResult2("export declaration", statement, statement.declaration)) {
const originalDefinitions = this.scope.definitions;
const tracker = new TrackingSet(this.scope.definitions);
this.scope.definitions = tracker;
this.prewalkStatement(statement.declaration);
const newDefs = this.scope.definitions.slice(pos);
const newDefs = Array.from(tracker.getAddedItems());
this.scope.definitions = originalDefinitions;
for(let index = newDefs.length - 1; index >= 0; index--) {
const def = newDefs[index];
this.applyPluginsBailResult("export specifier", statement, def, def, index);
this.applyPluginsBailResult4("export specifier", statement, def, def, index);
}
}
}
@ -727,9 +761,9 @@ class Parser extends Tapable {
{
const name = specifier.exported.name;
if(source)
this.applyPluginsBailResult("export import specifier", statement, source, specifier.local.name, name, specifierIndex);
this.applyPluginsBailResult5("export import specifier", statement, source, specifier.local.name, name, specifierIndex);
else
this.applyPluginsBailResult("export specifier", statement, specifier.local.name, name, specifierIndex);
this.applyPluginsBailResult4("export specifier", statement, specifier.local.name, name, specifierIndex);
break;
}
}
@ -745,12 +779,15 @@ class Parser extends Tapable {
prewalkExportDefaultDeclaration(statement) {
if(/Declaration$/.test(statement.declaration.type)) {
const pos = this.scope.definitions.length;
const originalDefinitions = this.scope.definitions;
const tracker = new TrackingSet(this.scope.definitions);
this.scope.definitions = tracker;
this.prewalkStatement(statement.declaration);
const newDefs = this.scope.definitions.slice(pos);
const newDefs = Array.from(tracker.getAddedItems());
this.scope.definitions = originalDefinitions;
for(let index = 0, len = newDefs.length; index < len; index++) {
const def = newDefs[index];
this.applyPluginsBailResult("export specifier", statement, def, "default");
this.applyPluginsBailResult3("export specifier", statement, def, "default");
}
}
}
@ -758,21 +795,21 @@ class Parser extends Tapable {
walkExportDefaultDeclaration(statement) {
this.applyPluginsBailResult1("export", statement);
if(/Declaration$/.test(statement.declaration.type)) {
if(!this.applyPluginsBailResult("export declaration", statement, statement.declaration)) {
if(!this.applyPluginsBailResult2("export declaration", statement, statement.declaration)) {
this.walkStatement(statement.declaration);
}
} else {
this.walkExpression(statement.declaration);
if(!this.applyPluginsBailResult("export expression", statement, statement.declaration)) {
this.applyPluginsBailResult("export specifier", statement, statement.declaration, "default");
if(!this.applyPluginsBailResult2("export expression", statement, statement.declaration)) {
this.applyPluginsBailResult3("export specifier", statement, statement.declaration, "default");
}
}
}
prewalkExportAllDeclaration(statement) {
const source = statement.source.value;
this.applyPluginsBailResult("export import", statement, source);
this.applyPluginsBailResult("export import specifier", statement, source, null, null, 0);
this.applyPluginsBailResult2("export import", statement, source);
this.applyPluginsBailResult5("export import specifier", statement, source, null, null, 0);
}
prewalkVariableDeclaration(statement) {
@ -787,8 +824,8 @@ class Parser extends Tapable {
prewalkClassDeclaration(statement) {
if(statement.id) {
this.scope.renames["$" + statement.id.name] = undefined;
this.scope.definitions.push(statement.id.name);
this.scope.renames.set(statement.id.name, null);
this.scope.definitions.add(statement.id.name);
}
}
@ -829,9 +866,8 @@ class Parser extends Tapable {
this.enterPattern(declarator.id, (name, decl) => {
if(!this.applyPluginsBailResult1("var-" + declarator.kind + " " + name, decl)) {
if(!this.applyPluginsBailResult1("var " + name, decl)) {
this.scope.renames["$" + name] = undefined;
if(this.scope.definitions.indexOf(name) < 0)
this.scope.definitions.push(name);
this.scope.renames.set(name, null);
this.scope.definitions.add(name);
}
}
});
@ -850,9 +886,8 @@ class Parser extends Tapable {
if(renameIdentifier && declarator.id.type === "Identifier" && this.applyPluginsBailResult1("can-rename " + renameIdentifier, declarator.init)) {
// renaming with "var a = b;"
if(!this.applyPluginsBailResult1("rename " + renameIdentifier, declarator.init)) {
this.scope.renames["$" + declarator.id.name] = this.scope.renames["$" + renameIdentifier] || renameIdentifier;
const idx = this.scope.definitions.indexOf(declarator.id.name);
if(idx >= 0) this.scope.definitions.splice(idx, 1);
this.scope.renames.set(declarator.id.name, this.scope.renames.get(renameIdentifier) || renameIdentifier);
this.scope.definitions.delete(declarator.id.name);
}
} else {
this.walkPattern(declarator.id);
@ -1010,15 +1045,14 @@ class Parser extends Tapable {
if(expression.left.type === "Identifier" && renameIdentifier && this.applyPluginsBailResult1("can-rename " + renameIdentifier, expression.right)) {
// renaming "a = b;"
if(!this.applyPluginsBailResult1("rename " + renameIdentifier, expression.right)) {
this.scope.renames["$" + expression.left.name] = renameIdentifier;
const idx = this.scope.definitions.indexOf(expression.left.name);
if(idx >= 0) this.scope.definitions.splice(idx, 1);
this.scope.renames.set(expression.left.name, renameIdentifier);
this.scope.definitions.delete(expression.left.name);
}
} else if(expression.left.type === "Identifier") {
if(!this.applyPluginsBailResult1("assigned " + expression.left.name, expression)) {
this.walkExpression(expression.right);
}
this.scope.renames["$" + expression.left.name] = undefined;
this.scope.renames.set(expression.left.name, null);
if(!this.applyPluginsBailResult1("assign " + expression.left.name, expression)) {
this.walkExpression(expression.left);
}
@ -1026,7 +1060,7 @@ class Parser extends Tapable {
this.walkExpression(expression.right);
this.walkPattern(expression.left);
this.enterPattern(expression.left, (name, decl) => {
this.scope.renames["$" + name] = undefined;
this.scope.renames.set(name, null);
});
}
}
@ -1092,13 +1126,13 @@ class Parser extends Tapable {
return !args[idx];
}), () => {
if(renameThis) {
this.scope.renames.$this = renameThis;
this.scope.renames.set("this", renameThis);
}
for(let i = 0; i < args.length; i++) {
const param = args[i];
if(!param) continue;
if(!params[i] || params[i].type !== "Identifier") continue;
this.scope.renames["$" + params[i].name] = param;
this.scope.renames.set(params[i].name, param);
}
if(functionExpression.body.type === "BlockStatement") {
this.prewalkStatement(functionExpression.body);
@ -1110,7 +1144,7 @@ class Parser extends Tapable {
if(expression.callee.type === "MemberExpression" &&
expression.callee.object.type === "FunctionExpression" &&
!expression.callee.computed &&
(["call", "bind"]).indexOf(expression.callee.property.name) >= 0 &&
(expression.callee.property.name === "call" || expression.callee.property.name === "bind") &&
expression.arguments &&
expression.arguments.length > 0
) {
@ -1164,8 +1198,8 @@ class Parser extends Tapable {
}
walkIdentifier(expression) {
if(this.scope.definitions.indexOf(expression.name) === -1) {
const result = this.applyPluginsBailResult1("expression " + (this.scope.renames["$" + expression.name] || expression.name), expression);
if(!this.scope.definitions.has(expression.name)) {
const result = this.applyPluginsBailResult1("expression " + (this.scope.renames.get(expression.name) || expression.name), expression);
if(result === true)
return;
}
@ -1176,23 +1210,23 @@ class Parser extends Tapable {
this.scope = {
inTry: false,
inShorthand: false,
definitions: oldScope.definitions.slice(),
renames: Object.create(oldScope.renames)
definitions: oldScope.definitions.createChild(),
renames: oldScope.renames.createChild()
};
this.scope.renames.$this = undefined;
this.scope.renames.set("this", null);
for(let paramIndex = 0, len = params.length; paramIndex < len; paramIndex++) {
const param = params[paramIndex];
if(typeof param !== "string") {
this.enterPattern(param, param => {
this.scope.renames["$" + param] = undefined;
this.scope.definitions.push(param);
this.scope.renames.set(param, null);
this.scope.definitions.add(param);
});
} else {
this.scope.renames["$" + param] = undefined;
this.scope.definitions.push(param);
this.scope.renames.set(param, null);
this.scope.definitions.add(param);
}
}
@ -1374,12 +1408,12 @@ class Parser extends Tapable {
const oldComments = this.comments;
this.scope = {
inTry: false,
definitions: [],
renames: {}
definitions: new StackedSetMap(),
renames: new StackedSetMap()
};
const state = this.state = initialState || {};
this.comments = comments;
if(this.applyPluginsBailResult("program", ast, comments) === undefined) {
if(this.applyPluginsBailResult2("program", ast, comments) === undefined) {
this.prewalkStatements(ast.body);
this.walkStatements(ast.body);
}
@ -1433,11 +1467,11 @@ class Parser extends Tapable {
}
let free;
if(expr.type === "Identifier") {
free = this.scope.definitions.indexOf(expr.name) === -1;
exprName.push(this.scope.renames["$" + expr.name] || expr.name);
} else if(expr.type === "ThisExpression" && this.scope.renames.$this) {
free = !this.scope.definitions.has(expr.name);
exprName.push(this.scope.renames.get(expr.name) || expr.name);
} else if(expr.type === "ThisExpression" && this.scope.renames.get("this")) {
free = true;
exprName.push(this.scope.renames.$this);
exprName.push(this.scope.renames.get("this"));
} else if(expr.type === "ThisExpression") {
free = false;
exprName.push("this");
@ -1461,3 +1495,4 @@ class Parser extends Tapable {
Parser.ECMA_VERSION = ECMA_VERSION;
module.exports = Parser;
Parser.StackedSetMap = StackedSetMap;

View File

@ -62,7 +62,7 @@ class RecordIdsPlugin {
records.chunks.usedIds = {};
chunks.forEach(chunk => {
const name = chunk.name;
const blockIdents = chunk.blocks.map(getDepBlockIdent.bind(null, chunk)).filter(Boolean);
const blockIdents = chunk.mapBlocks(getDepBlockIdent.bind(null, chunk)).filter(Boolean);
if(name) records.chunks.byName[name] = chunk.id;
blockIdents.forEach((blockIdent) => {
records.chunks.byBlocks[blockIdent] = chunk.id;
@ -87,7 +87,7 @@ class RecordIdsPlugin {
if(records.chunks.byBlocks) {
const argumentedChunks = chunks.filter(chunk => chunk.id === null).map(chunk => ({
chunk,
blockIdents: chunk.blocks.map(getDepBlockIdent.bind(null, chunk)).filter(Boolean)
blockIdents: chunk.mapBlocks(getDepBlockIdent.bind(null, chunk)).filter(Boolean)
})).filter(arg => arg.blockIdents.length > 0);
let blockIdentsCount = {};
argumentedChunks.forEach((arg, idx) => {

View File

@ -53,12 +53,8 @@ class SourceMapDevToolPlugin {
constructor(options) {
if(arguments.length > 1)
throw new Error("SourceMapDevToolPlugin only takes one argument (pass an options object)");
// TODO: remove in webpack 3
if(typeof options === "string") {
options = {
sourceMapFilename: options
};
}
if(!options || typeof options !== "object")
throw new Error("SourceMapDevToolPlugin takes an options argument");
if(!options) options = {};
this.sourceMapFilename = options.filename;
this.sourceMappingURLComment = options.append === false ? false : options.append || "\n//# sourceMappingURL=[url]";

View File

@ -379,7 +379,7 @@ class Stats {
names: chunk.name ? [chunk.name] : [],
files: chunk.files.slice(),
hash: chunk.renderedHash,
parents: chunk.parents.map(c => c.id)
parents: chunk.mapParents(c => c.id)
};
if(showChunkModules) {
obj.modules = chunk

View File

@ -10,10 +10,12 @@ const ConcatSource = require("webpack-sources").ConcatSource;
const START_LOWERCASE_ALPHABET_CODE = "a".charCodeAt(0);
const START_UPPERCASE_ALPHABET_CODE = "A".charCodeAt(0);
const DELTA_A_TO_Z = "z".charCodeAt(0) - START_LOWERCASE_ALPHABET_CODE + 1;
const FUNCTION_CONTENT_REGEX = /^function\s?\(\)\s?\{\n?|\n?\}$/g;
const FUNCTION_CONTENT_REGEX = /^function\s?\(\)\s?\{\r?\n?|\r?\n?\}$/g;
const INDENT_MULTILINE_REGEX = /^\t/mg;
const IDENTIFIER_NAME_REPLACE_REGEX = /^[^a-zA-Z$_]/;
const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$_]/g;
const LINE_SEPARATOR_REGEX = /\r?\n/g;
const IDENTIFIER_NAME_REPLACE_REGEX = /^([^a-zA-Z$_])/;
const IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX = /[^a-zA-Z0-9$]+/g;
const COMMENT_END_REGEX = /\*\//g;
const PATH_NAME_NORMALIZE_REPLACE_REGEX = /[^a-zA-Z0-9_!§$()=\-^°]+/g;
const MATCH_PADDED_HYPHENS_REPLACE_REGEX = /^-|-$/g;
@ -24,12 +26,22 @@ module.exports = class Template extends Tapable {
}
static getFunctionContent(fn) {
return fn.toString().replace(FUNCTION_CONTENT_REGEX, "").replace(INDENT_MULTILINE_REGEX, "");
return fn.toString().replace(FUNCTION_CONTENT_REGEX, "").replace(INDENT_MULTILINE_REGEX, "").replace(LINE_SEPARATOR_REGEX, "\n");
}
static toIdentifier(str) {
if(typeof str !== "string") return "";
return str.replace(IDENTIFIER_NAME_REPLACE_REGEX, "_").replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_");
return str.replace(IDENTIFIER_NAME_REPLACE_REGEX, "_$1").replace(IDENTIFIER_ALPHA_NUMERIC_NAME_REPLACE_REGEX, "_");
}
static toComment(str) {
if(!str) return "";
return `/*! ${str.replace(COMMENT_END_REGEX, "* /")} */`;
}
static toNormalComment(str) {
if(!str) return "";
return `/* ${str.replace(COMMENT_END_REGEX, "* /")} */`;
}
static toPath(str) {

View File

@ -18,17 +18,6 @@ const REGEXP_HASH_FOR_TEST = new RegExp(REGEXP_HASH.source, "i"),
REGEXP_CHUNKHASH_FOR_TEST = new RegExp(REGEXP_CHUNKHASH.source, "i"),
REGEXP_NAME_FOR_TEST = new RegExp(REGEXP_NAME.source, "i");
// TODO: remove in webpack 3
// Backwards compatibility; expose regexes on Template object
const Template = require("./Template");
Template.REGEXP_HASH = REGEXP_HASH;
Template.REGEXP_CHUNKHASH = REGEXP_CHUNKHASH;
Template.REGEXP_NAME = REGEXP_NAME;
Template.REGEXP_ID = REGEXP_ID;
Template.REGEXP_FILE = REGEXP_FILE;
Template.REGEXP_QUERY = REGEXP_QUERY;
Template.REGEXP_FILEBASE = REGEXP_FILEBASE;
const withHashLength = (replacer, handlerFn) => {
return function(_, hashLength) {
const length = hashLength && parseInt(hashLength, 10);

View File

@ -95,7 +95,7 @@ class UmdMainTemplatePlugin {
}
function externalsArguments(modules) {
return modules.map(m => Template.toIdentifier(`__WEBPACK_EXTERNAL_MODULE_${m.id}__`)).join(", ");
return modules.map(m => `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(`${m.id}`)}__`).join(", ");
}
function libraryName(library) {

View File

@ -41,6 +41,7 @@ const RemoveEmptyChunksPlugin = require("./optimize/RemoveEmptyChunksPlugin");
const MergeDuplicateChunksPlugin = require("./optimize/MergeDuplicateChunksPlugin");
const FlagIncludedChunksPlugin = require("./optimize/FlagIncludedChunksPlugin");
const OccurrenceOrderPlugin = require("./optimize/OccurrenceOrderPlugin");
const SideEffectsFlagPlugin = require("./optimize/SideEffectsFlagPlugin");
const FlagDependencyUsagePlugin = require("./FlagDependencyUsagePlugin");
const FlagDependencyExportsPlugin = require("./FlagDependencyExportsPlugin");
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
@ -259,6 +260,7 @@ class WebpackOptionsApply extends OptionsApply {
new MergeDuplicateChunksPlugin(),
new FlagIncludedChunksPlugin(),
new OccurrenceOrderPlugin(true),
new SideEffectsFlagPlugin(),
new FlagDependencyExportsPlugin(),
new FlagDependencyUsagePlugin()
);

View File

@ -109,7 +109,7 @@ class AMDDefineDependencyParserPlugin {
if(fnParamsOffset < 0) fnParamsOffset = 0;
}
}
let fnRenames = Object.create(parser.scope.renames);
let fnRenames = parser.scope.renames.createChild();
let identifiers;
if(array) {
identifiers = {};
@ -118,7 +118,7 @@ class AMDDefineDependencyParserPlugin {
if(!result) return;
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) {
fnRenames["$" + param.name] = identifiers[idx];
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;
@ -127,7 +127,7 @@ class AMDDefineDependencyParserPlugin {
identifiers = ["require", "exports", "module"];
if(fnParams) fnParams = fnParams.slice(fnParamsOffset).filter((param, idx) => {
if(identifiers[idx]) {
fnRenames["$" + param.name] = identifiers[idx];
fnRenames.set(param.name, identifiers[idx]);
return false;
}
return true;

View File

@ -4,6 +4,7 @@
*/
"use strict";
const Dependency = require("../Dependency");
const Template = require("../Template");
const webpackMissingModuleModule = require("./WebpackMissingModule").module;
class AMDRequireArrayDependency extends Dependency {
@ -26,19 +27,12 @@ AMDRequireArrayDependency.Template = class AMDRequireArrayDependencyTemplate {
getContent(dep, outputOptions, requestShortener) {
const requires = dep.depsArray.map((dependency) => {
const optionalComment = this.optionalComment(outputOptions.pathinfo, requestShortener.shorten(dependency.request));
const optionalComment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dependency.request)) : "";
return this.contentForDependency(dependency, optionalComment);
});
return `[${requires.join(", ")}]`;
}
optionalComment(pathInfo, shortenedRequest) {
if(!pathInfo) {
return "";
}
return `/*! ${shortenedRequest} */ `;
}
contentForDependency(dep, comment) {
if(typeof dep === "string") {
return dep;

View File

@ -65,7 +65,7 @@ class CommonJsPlugin {
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.scope.definitions.push("require");
parser.scope.definitions.add("require");
return true;
});
parser.plugin("can-rename require", () => true);

View File

@ -4,11 +4,12 @@
*/
"use strict";
const Template = require("../Template");
class ContextDependencyTemplateAsId {
apply(dep, source, outputOptions, requestShortener) {
const comment = outputOptions.pathinfo ?
"/*! " + requestShortener.shorten(dep.request) + " */ " : "";
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dep.request)) + " " : "";
if(dep.module && dep.module.dependencies && dep.module.dependencies.length > 0) {
if(dep.valueRange) {

View File

@ -4,11 +4,12 @@
*/
"use strict";
const Template = require("../Template");
class ContextDependencyTemplateAsRequireCall {
apply(dep, source, outputOptions, requestShortener) {
const comment = outputOptions.pathinfo ?
"/*! " + requestShortener.shorten(dep.request) + " */ " : "";
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dep.request)) + " " : "";
const containsDeps = dep.module && dep.module.dependencies && dep.module.dependencies.length > 0;
const isAsync = dep.module && dep.module.options.async;

View File

@ -4,6 +4,8 @@
*/
"use strict";
const Template = require("../Template");
const DepBlockHelpers = exports;
DepBlockHelpers.getLoadDepBlockWrapper = (depBlock, outputOptions, requestShortener, name) => {
@ -25,9 +27,9 @@ DepBlockHelpers.getDepBlockPromise = (depBlock, outputOptions, requestShortener,
name = asComment(name);
if(chunks.length === 1) {
const chunkId = JSON.stringify(chunks[0].id);
return `__webpack_require__.e${name}(${chunkId}${pathChunkCheck ? "/*! " + shortChunkName + " */" : ""}${chunkReason})`;
return `__webpack_require__.e${name}(${chunkId}${pathChunkCheck ? Template.toComment(shortChunkName) : ""}${chunkReason})`;
} else if(chunks.length > 0) {
return `Promise.all${name}(${pathChunkCheck ? "/*! " + shortChunkName + " */" : ""}[${chunks.map(requireChunkId).join(", ")}])`;
return `Promise.all${name}(${pathChunkCheck ? Template.toComment(shortChunkName) : ""}[${chunks.map(requireChunkId).join(", ")}])`;
}
}
return "new Promise(function(resolve) { resolve(); })";

View File

@ -3,8 +3,9 @@
Author Tobias Koppers @sokra
*/
"use strict";
const NullDependency = require("./NullDependency");
const makeHarmonyImportStatement = require("./HarmonyImportDependency").makeImportStatement;
const HarmonyImportDependency = require("./HarmonyImportDependency");
class HarmonyAcceptDependency extends NullDependency {
constructor(range, dependencies, hasCallback) {
@ -22,12 +23,9 @@ class HarmonyAcceptDependency extends NullDependency {
HarmonyAcceptDependency.Template = class HarmonyAcceptDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const content = dep.dependencies
.map(dependency => makeHarmonyImportStatement(
false,
dependency,
outputOptions,
requestShortener
)).join("");
.filter(dependency => HarmonyImportDependency.Template.isImportEmitted(dependency, source))
.map(dependency => dependency.getImportStatement(false, outputOptions, requestShortener))
.join("");
if(dep.hasCallback) {
source.insert(dep.range[0], `function(__WEBPACK_OUTDATED_DEPENDENCIES__) { ${content}(`);

View File

@ -6,8 +6,8 @@
const HarmonyImportDependency = require("./HarmonyImportDependency");
class HarmonyAcceptImportDependency extends HarmonyImportDependency {
constructor(request, importedVar, range) {
super(request, importedVar, range);
constructor(request, originModule, parserScope) {
super(request, originModule, NaN, parserScope);
}
get type() {

View File

@ -5,6 +5,7 @@
"use strict";
const HarmonyCompatibilityDependency = require("./HarmonyCompatibilityDependency");
const HarmonyInitDependency = require("./HarmonyInitDependency");
module.exports = class HarmonyDetectionParserPlugin {
apply(parser) {
@ -14,8 +15,21 @@ module.exports = class HarmonyDetectionParserPlugin {
});
if(isHarmony) {
const module = parser.state.module;
const dep = new HarmonyCompatibilityDependency(module);
dep.loc = {
const compatDep = new HarmonyCompatibilityDependency(module);
compatDep.loc = {
start: {
line: -1,
column: 0
},
end: {
line: -1,
column: 0
},
index: -3
};
module.addDependency(compatDep);
const initDep = new HarmonyInitDependency(module);
initDep.loc = {
start: {
line: -1,
column: 0
@ -26,7 +40,8 @@ module.exports = class HarmonyDetectionParserPlugin {
},
index: -2
};
module.addDependency(dep);
module.addDependency(initDep);
parser.state.harmonyParserScope = parser.state.harmonyParserScope || {};
module.meta.harmonyModule = true;
module.strict = true;
module.exportsArgument = "__webpack_exports__";

View File

@ -5,11 +5,11 @@
"use strict";
const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");
const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImportedSpecifierDependency");
const HarmonyImportDependency = require("./HarmonyImportDependency");
const HarmonyModulesHelpers = require("./HarmonyModulesHelpers");
const ConstDependency = require("./ConstDependency");
module.exports = class HarmonyExportDependencyParserPlugin {
apply(parser) {
@ -21,11 +21,15 @@ module.exports = class HarmonyExportDependencyParserPlugin {
return true;
});
parser.plugin("export import", (statement, source) => {
const dep = new HarmonyImportDependency(source, HarmonyModulesHelpers.getNewModuleVar(parser.state, source), statement.range);
dep.loc = Object.create(statement.loc);
dep.loc.index = -1;
parser.state.current.addDependency(dep);
parser.state.lastHarmonyImport = dep;
parser.state.lastHarmonyImportOrder = (parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = Object.create(statement.loc);
clearDep.loc.index = -1;
parser.state.current.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope);
sideEffectDep.loc = Object.create(statement.loc);
sideEffectDep.loc.index = -1;
parser.state.current.addDependency(sideEffectDep);
return true;
});
parser.plugin("export expression", (statement, expr) => {
@ -37,17 +41,15 @@ module.exports = class HarmonyExportDependencyParserPlugin {
});
parser.plugin("export declaration", statement => {});
parser.plugin("export specifier", (statement, id, name, idx) => {
const rename = parser.scope.renames[`$${id}`];
const rename = parser.scope.renames.get(id);
let dep;
const harmonyNamedExports = parser.state.harmonyNamedExports = parser.state.harmonyNamedExports || new Set();
harmonyNamedExports.add(name);
if(rename === "imported var") {
const settings = parser.state.harmonySpecifier[`$${id}`];
dep = new HarmonyExportImportedSpecifierDependency(parser.state.module, settings[0], settings[1], settings[2], name, harmonyNamedExports, null);
const settings = parser.state.harmonySpecifier.get(id);
dep = new HarmonyExportImportedSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, harmonyNamedExports, null);
} else {
const immutable = statement.declaration && isImmutableStatement(statement.declaration);
const hoisted = statement.declaration && isHoistedStatement(statement.declaration);
dep = new HarmonyExportSpecifierDependency(parser.state.module, id, name, !immutable || hoisted ? -2 : (statement.range[1] + 0.5), immutable);
dep = new HarmonyExportSpecifierDependency(parser.state.module, id, name);
}
dep.loc = Object.create(statement.loc);
dep.loc.index = idx;
@ -62,7 +64,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
} else {
harmonyStarExports = parser.state.harmonyStarExports = parser.state.harmonyStarExports || [];
}
const dep = new HarmonyExportImportedSpecifierDependency(parser.state.module, parser.state.lastHarmonyImport, HarmonyModulesHelpers.getModuleVar(parser.state, source), id, name, harmonyNamedExports, harmonyStarExports && harmonyStarExports.slice());
const dep = new HarmonyExportImportedSpecifierDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope, id, name, harmonyNamedExports, harmonyStarExports && harmonyStarExports.slice());
if(harmonyStarExports) {
harmonyStarExports.push(dep);
}
@ -73,15 +75,3 @@ module.exports = class HarmonyExportDependencyParserPlugin {
});
}
};
function isImmutableStatement(statement) {
if(statement.type === "FunctionDeclaration") return true;
if(statement.type === "ClassDeclaration") return true;
if(statement.type === "VariableDeclaration" && statement.kind === "const") return true;
return false;
}
function isHoistedStatement(statement) {
if(statement.type === "FunctionDeclaration") return true;
return false;
}

View File

@ -3,14 +3,12 @@
Author Tobias Koppers @sokra
*/
"use strict";
const NullDependency = require("./NullDependency");
const HarmonyImportDependency = require("./HarmonyImportDependency");
const Template = require("../Template");
class HarmonyExportImportedSpecifierDependency extends NullDependency {
constructor(originModule, importDependency, importedVar, id, name, activeExports, otherStarExports) {
super();
this.originModule = originModule;
this.importDependency = importDependency;
this.importedVar = importedVar;
class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
constructor(request, originModule, sourceOrder, parserScope, id, name, activeExports, otherStarExports) {
super(request, originModule, sourceOrder, parserScope);
this.id = id;
this.name = name;
this.activeExports = activeExports;
@ -21,81 +19,183 @@ class HarmonyExportImportedSpecifierDependency extends NullDependency {
return "harmony export imported specifier";
}
getReference() {
getMode(ignoreUnused) {
const name = this.name;
const id = this.id;
const used = this.originModule.isUsed(name);
const importedModule = this.importDependency.module;
const importedModule = this.module;
if(!importedModule || !used || !this.originModule.usedExports) return null;
if(!importedModule) {
return {
type: "missing",
userRequest: this.userRequest
};
}
const hasUsedExports = Array.isArray(this.originModule.usedExports);
if(!ignoreUnused && (name ? !used : this.originModule.usedExports === false)) {
return {
type: "unused",
name: name || "*"
};
}
const isNotAHarmonyModule = importedModule.meta && !importedModule.meta.harmonyModule;
if(name && id === "default" && isNotAHarmonyModule) {
return {
type: "reexport-non-harmony-default",
module: importedModule,
name
};
}
if(name) {
const nameIsNotInUsedExports = hasUsedExports && this.originModule.usedExports.indexOf(name) < 0;
if(nameIsNotInUsedExports) return null;
// export { name as name }
if(this.id) {
if(id) {
return {
type: "safe-reexport",
module: importedModule,
importedNames: [this.id]
map: new Map([
[name, id]
])
};
}
// export { * as name }
return {
type: "reexport-namespace-object",
module: importedModule,
importedNames: true
name
};
}
const hasUsedExports = Array.isArray(this.originModule.usedExports);
const hasProvidedExports = Array.isArray(importedModule.providedExports);
const activeFromOtherStarExports = this._discoverActiveExportsFromOtherStartExports();
// export *
if(hasUsedExports) {
// reexport * with known used exports
const importedNames = this.originModule.usedExports.filter(id => {
if(hasProvidedExports) {
const map = new Map(this.originModule.usedExports.filter((id) => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
if(hasProvidedExports && importedModule.providedExports.indexOf(id) < 0) return false;
if(importedModule.providedExports.indexOf(id) < 0) return false;
return true;
});
}).map(item => [item, item]));
if(map.size === 0) {
return {
type: "empty-star"
};
}
return {
type: "safe-reexport",
module: importedModule,
importedNames
map
};
}
const map = new Map(this.originModule.usedExports.filter(id => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
return true;
}).map(item => [item, item]));
if(map.size === 0) {
return {
type: "empty-star"
};
}
return {
type: "checked-reexport",
module: importedModule,
map
};
}
if(hasProvidedExports) {
return {
module: importedModule,
importedNames: importedModule.providedExports.filter(id => {
const map = new Map(importedModule.providedExports
.filter(id => {
if(id === "default") return false;
if(this.activeExports.has(id)) return false;
if(activeFromOtherStarExports.has(id)) return false;
return true;
})
.map(item => [item, item])
);
if(map.size === 0) {
return {
type: "empty-star"
};
}
return {
type: "safe-reexport",
module: importedModule,
importedNames: true,
map
};
}
return {
type: "dynamic-reexport",
module: importedModule
};
}
getReference() {
const mode = this.getMode();
switch(mode.type) {
case "missing":
case "unused":
case "empty-star":
return null;
case "reexport-non-harmony-default":
return {
module: mode.module,
importedNames: ["default"]
};
case "reexport-namespace-object":
return {
module: mode.module,
importedNames: true
};
case "safe-reexport":
case "checked-reexport":
return {
module: mode.module,
importedNames: Array.from(mode.map.values())
};
case "dynamic-reexport":
return {
module: mode.module,
importedNames: true
};
default:
throw new Error(`Unknown mode ${mode.type}`);
}
}
_discoverActiveExportsFromOtherStartExports() {
if(!this.otherStarExports)
return new Set();
const result = new Set();
// try to learn impossible exports from other star exports with provided exports
for(const otherStarExport of this.otherStarExports) {
const otherImportedModule = otherStarExport.importDependency.module;
const otherImportedModule = otherStarExport.module;
if(otherImportedModule && Array.isArray(otherImportedModule.providedExports)) {
for(const exportName of otherImportedModule.providedExports)
result.add(exportName);
@ -111,7 +211,7 @@ class HarmonyExportImportedSpecifierDependency extends NullDependency {
};
}
const importedModule = this.importDependency.module;
const importedModule = this.module;
if(!importedModule) {
// no imported module available
@ -141,7 +241,7 @@ class HarmonyExportImportedSpecifierDependency extends NullDependency {
updateHash(hash) {
super.updateHash(hash);
const hashValue = this.getHashValue(this.importDependency.module);
const hashValue = this.getHashValue(this.module);
hash.update(hashValue);
}
@ -158,91 +258,85 @@ class HarmonyExportImportedSpecifierDependency extends NullDependency {
module.exports = HarmonyExportImportedSpecifierDependency;
HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedSpecifierDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const content = this.getContent(dep);
HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedSpecifierDependencyTemplate extends HarmonyImportDependency.Template {
harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates) {
super.harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates);
const importVar = dep.getImportVar(requestShortener);
const content = this.getContent(dep, importVar);
source.insert(-1, content);
}
getContent(dep) {
const name = dep.importedVar;
getHarmonyInitOrder(dep) {
const used = dep.originModule.isUsed(dep.name);
const importedModule = dep.importDependency.module;
const importsExportsUnknown = !importedModule || !Array.isArray(importedModule.providedExports);
const getReexportStatement = this.reexportStatementCreator(dep.originModule, importsExportsUnknown, name);
// we want to rexport something, but the export isn't used
if(!used) {
return "/* unused harmony reexport " + dep.name + " */\n";
}
// we want to reexport the default export from a non-hamory module
const isNotAHarmonyModule = !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
if(dep.name && dep.id === "default" && isNotAHarmonyModule) {
return "/* harmony reexport (default from non-hamory) */ " + getReexportStatement(JSON.stringify(used), null);
}
// we want to reexport a key as new key
if(dep.name && dep.id) {
var idUsed = importedModule && importedModule.isUsed(dep.id);
return "/* harmony reexport (binding) */ " + getReexportStatement(JSON.stringify(used), JSON.stringify(idUsed));
}
// we want to reexport the module object as named export
if(dep.name) {
return "/* harmony reexport (module object) */ " + getReexportStatement(JSON.stringify(used), "");
}
const hasProvidedExports = importedModule && Array.isArray(importedModule.providedExports);
if(!used) return NaN;
if(!dep.name) {
const importedModule = dep.module;
const activeFromOtherStarExports = dep._discoverActiveExportsFromOtherStartExports();
// we know which exports are used
if(Array.isArray(dep.originModule.usedExports)) {
const items = dep.originModule.usedExports.map(id => {
if(id === "default") return;
if(dep.activeExports.has(id)) return;
if(importedModule.isProvided(id) === false) return;
if(activeFromOtherStarExports.has(id)) return;
var exportUsed = dep.originModule.isUsed(id);
var idUsed = importedModule && importedModule.isUsed(id);
return [exportUsed, idUsed];
}).filter(Boolean);
// we know which exports are used
if(items.length === 0) {
return "/* unused harmony namespace reexport */\n";
}
return items.map(function(item) {
return "/* harmony namespace reexport (by used) */ " + getReexportStatement(JSON.stringify(item[0]), JSON.stringify(item[1]));
}).join("");
}
const unused = dep.originModule.usedExports.every(function(id) {
if(id === "default") return true;
if(dep.activeExports.has(id)) return true;
if(importedModule.isProvided(id) === false) return true;
if(activeFromOtherStarExports.has(id)) return true;
return false;
});
if(unused) return NaN;
} else if(dep.originModule.usedExports && importedModule && Array.isArray(importedModule.providedExports)) {
// not sure which exports are used, but we know which are provided
if(dep.originModule.usedExports && importedModule && hasProvidedExports) {
const items = importedModule.providedExports.map(id => {
if(id === "default") return;
if(dep.activeExports.has(id)) return;
if(activeFromOtherStarExports.has(id)) return;
var exportUsed = dep.originModule.isUsed(id);
var idUsed = importedModule && importedModule.isUsed(id);
return [exportUsed, idUsed];
}).filter(Boolean);
if(items.length === 0) {
return "/* empty harmony namespace reexport */\n";
const unused = importedModule.providedExports.every(function(id) {
if(id === "default") return true;
if(dep.activeExports.has(id)) return true;
if(activeFromOtherStarExports.has(id)) return true;
return false;
});
if(unused) return NaN;
}
}
return super.getHarmonyInitOrder(dep);
}
return items.map(function(item) {
return "/* harmony namespace reexport (by provided) */ " + getReexportStatement(JSON.stringify(item[0]), JSON.stringify(item[1]));
getContent(dep, name) {
const mode = dep.getMode();
const module = dep.originModule;
const importedModule = dep.module;
const importVar = dep.getImportVar();
switch(mode.type) {
case "missing":
return `throw new Error(${JSON.stringify(`Cannot find module '${mode.userRequest}'`)});\n`;
case "unused":
return `${Template.toNormalComment(`unused harmony reexport ${mode.name}`)}\n`;
case "reexport-non-harmony-default":
return "/* harmony reexport (default from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, null);
case "reexport-namespace-object":
return "/* harmony reexport (module object) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, "");
case "empty-star":
return "/* empty/unused harmony star reexport */";
case "safe-reexport":
return Array.from(mode.map.entries()).map(item => {
return "/* harmony reexport (safe) */ " + this.getReexportStatement(module, module.isUsed(item[0]), importVar, importedModule.isUsed(item[1])) + "\n";
}).join("");
}
// not sure which exports are used and provided
if(dep.originModule.usedExports) {
const activeExports = Array.from(dep.activeExports).concat(Array.from(activeFromOtherStarExports));
let content = "/* harmony namespace reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " + name + ") ";
case "checked-reexport":
return Array.from(mode.map.entries()).map(item => {
return "/* harmony reexport (checked) */ " + this.getConditionalReexportStatement(module, item[0], importVar, item[1]) + "\n";
}).join("");
case "dynamic-reexport":
{
const activeExports = Array.from(dep.activeExports).concat(dep._discoverActiveExportsFromOtherStartExports());
let content = "/* harmony reexport (unknown) */ for(var __WEBPACK_IMPORT_KEY__ in " + importVar + ") ";
// Filter out exports which are defined by other exports
// and filter out default export because it cannot be reexported with *
@ -254,25 +348,21 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
return content + `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${name}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n`;
}
return "/* unused harmony reexport namespace */\n";
default:
throw new Error(`Unknown mode ${mode.type}`);
}
}
reexportStatementCreator(module, importsExportsUnknown, name) {
getReexportStatement(module, key, name, valueKey) {
const exportsName = module.exportsArgument || "exports";
const getReexportStatement = (key, valueKey) => {
const conditional = this.getConditional(importsExportsUnknown, valueKey, name);
const returnValue = this.getReturnValue(valueKey);
return `${conditional}__webpack_require__.d(${exportsName}, ${key}, function() { return ${name}${returnValue}; });\n`;
};
return getReexportStatement;
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return ${name}${returnValue}; });\n`;
}
getConditional(importsExportsUnknown, valueKey, name) {
if(!importsExportsUnknown || !valueKey) {
return "";
}
return `if(__webpack_require__.o(${name}, ${valueKey})) `;
getConditionalReexportStatement(module, key, name, valueKey) {
const exportsName = module.exportsArgument || "exports";
const returnValue = this.getReturnValue(valueKey);
return `if(__webpack_require__.o(${name}, ${JSON.stringify(valueKey)})) __webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return ${name}${returnValue}; });\n`;
}
getReturnValue(valueKey) {
@ -280,6 +370,6 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
return "_default.a";
}
return valueKey && "[" + valueKey + "]";
return valueKey && "[" + JSON.stringify(valueKey) + "]";
}
};

View File

@ -6,13 +6,11 @@
const NullDependency = require("./NullDependency");
class HarmonyExportSpecifierDependency extends NullDependency {
constructor(originModule, id, name, position, immutable) {
constructor(originModule, id, name) {
super();
this.originModule = originModule;
this.id = id;
this.name = name;
this.position = position;
this.immutable = immutable;
}
get type() {
@ -27,13 +25,15 @@ class HarmonyExportSpecifierDependency extends NullDependency {
}
HarmonyExportSpecifierDependency.Template = class HarmonyExportSpecifierDependencyTemplate {
apply(dep, source) {
const content = this.getPrefix(dep) + this.getContent(dep);
source.insert(dep.position, content);
apply(dep, source) {}
getHarmonyInitOrder(dep) {
return 0;
}
getPrefix(dep) {
return dep.position > 0 ? "\n" : "";
harmonyInit(dep, source, outputOptions, requestShortener) {
const content = this.getContent(dep);
source.insert(-1, content);
}
getContent(dep) {
@ -43,9 +43,6 @@ HarmonyExportSpecifierDependency.Template = class HarmonyExportSpecifierDependen
}
const exportsName = dep.originModule.exportsArgument || "exports";
if(dep.immutable) {
return `/* harmony export (immutable) */ ${exportsName}[${JSON.stringify(used)}] = ${dep.id};\n`;
}
return `/* harmony export (binding) */ __webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${dep.id}; });\n`;
}

View File

@ -3,17 +3,15 @@
Author Tobias Koppers @sokra
*/
"use strict";
var ModuleDependency = require("./ModuleDependency");
const ModuleDependency = require("./ModuleDependency");
const Template = require("../Template");
class HarmonyImportDependency extends ModuleDependency {
constructor(request, importedVar, range) {
constructor(request, originModule, sourceOrder, parserScope) {
super(request);
this.range = range;
this.importedVar = importedVar;
}
get type() {
return "harmony import";
this.originModule = originModule;
this.sourceOrder = sourceOrder;
this.parserScope = parserScope;
}
getReference() {
@ -25,48 +23,78 @@ class HarmonyImportDependency extends ModuleDependency {
};
}
updateHash(hash) {
super.updateHash(hash);
hash.update((this.module && (!this.module.meta || this.module.meta.harmonyModule)) + "");
}
getImportVar() {
let importVarMap = this.parserScope.importVarMap;
if(!importVarMap) this.parserScope.importVarMap = importVarMap = new Map();
let importVar = importVarMap.get(this.module);
if(importVar) return importVar;
importVar = `${Template.toIdentifier(`${this.userRequest}`)}__WEBPACK_IMPORTED_MODULE_${importVarMap.size}__`;
importVarMap.set(this.module, importVar);
return importVar;
}
HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const content = makeImportStatement(true, dep, outputOptions, requestShortener);
source.replace(dep.range[0], dep.range[1] - 1, "");
source.insert(-1, content);
}
};
getImportStatement(declare, outputOptions, requestShortener) {
const module = this.module;
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(this.request)) : "";
const optDeclaration = declare ? "var " : "";
const optNewline = declare ? "\n" : " ";
function getOptionalComment(pathinfo, shortenedRequest) {
if(!pathinfo) {
return "";
}
return `/*! ${shortenedRequest} */ `;
if(!module) {
const stringifiedError = JSON.stringify(`Cannot find module "${this.request}"`);
return `throw new Error(${stringifiedError});${optNewline}`;
}
function makeImportStatement(declare, dep, outputOptions, requestShortener) {
const comment = getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
const declaration = declare ? "var " : "";
const newline = declare ? "\n" : " ";
const importVar = this.getImportVar();
if(!dep.module) {
const stringifiedError = JSON.stringify(`Cannot find module "${dep.request}"`);
return `throw new Error(${stringifiedError});${newline}`;
}
if(dep.importedVar) {
const isHarmonyModule = dep.module.meta && dep.module.meta.harmonyModule;
const content = `/* harmony import */ ${declaration}${dep.importedVar} = __webpack_require__(${comment}${JSON.stringify(dep.module.id)});${newline}`;
if(importVar) {
const isHarmonyModule = module.meta && module.meta.harmonyModule;
const content = `/* harmony import */ ${optDeclaration}${importVar} = __webpack_require__(${comment}${JSON.stringify(module.id)});${optNewline}`;
if(isHarmonyModule) {
return content;
}
return `${content}/* harmony import */ ${declaration}${dep.importedVar}_default = __webpack_require__.n(${dep.importedVar});${newline}`;
return `${content}/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/__webpack_require__.n(${importVar});${optNewline}`;
}
return "";
}
HarmonyImportDependency.makeImportStatement = makeImportStatement;
updateHash(hash) {
super.updateHash(hash);
const importedModule = this.module;
hash.update((importedModule && (!importedModule.meta || importedModule.meta.harmonyModule)) + "");
hash.update((importedModule && importedModule.id) + "");
}
}
module.exports = HarmonyImportDependency;
const importEmittedMap = new WeakMap();
HarmonyImportDependency.Template = class HarmonyImportDependencyTemplate {
apply() {}
getHarmonyInitOrder(dep) {
return dep.sourceOrder;
}
static isImportEmitted(dep, source) {
let sourceInfo = importEmittedMap.get(source);
if(!sourceInfo) return false;
const key = dep.module || dep.request;
return key && sourceInfo.emittedImports.get(key);
}
harmonyInit(dep, source, outputOptions, requestShortener) {
let sourceInfo = importEmittedMap.get(source);
if(!sourceInfo) {
importEmittedMap.set(source, sourceInfo = {
emittedImports: new Map()
});
}
const key = dep.module || dep.request;
if(key && sourceInfo.emittedImports.get(key)) return;
sourceInfo.emittedImports.set(key, true);
const content = dep.getImportStatement(true, outputOptions, requestShortener);
source.insert(-1, content);
}
};

View File

@ -4,11 +4,11 @@
*/
"use strict";
const HarmonyImportDependency = require("./HarmonyImportDependency");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
const HarmonyAcceptImportDependency = require("./HarmonyAcceptImportDependency");
const HarmonyAcceptDependency = require("./HarmonyAcceptDependency");
const HarmonyModulesHelpers = require("./HarmonyModulesHelpers");
const ConstDependency = require("./ConstDependency");
module.exports = class HarmonyImportDependencyParserPlugin {
constructor(moduleOptions) {
@ -18,39 +18,46 @@ module.exports = class HarmonyImportDependencyParserPlugin {
apply(parser) {
parser.plugin("import", (statement, source) => {
const dep = new HarmonyImportDependency(source, HarmonyModulesHelpers.getNewModuleVar(parser.state, source), statement.range);
dep.loc = statement.loc;
parser.state.current.addDependency(dep);
parser.state.lastHarmonyImport = dep;
parser.state.lastHarmonyImportOrder = (parser.state.lastHarmonyImportOrder || 0) + 1;
const clearDep = new ConstDependency("", statement.range);
clearDep.loc = statement.loc;
parser.state.module.addDependency(clearDep);
const sideEffectDep = new HarmonyImportSideEffectDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope);
sideEffectDep.loc = statement.loc;
parser.state.module.addDependency(sideEffectDep);
return true;
});
parser.plugin("import specifier", (statement, source, id, name) => {
parser.scope.definitions.length--;
parser.scope.renames[`$${name}`] = "imported var";
if(!parser.state.harmonySpecifier) parser.state.harmonySpecifier = {};
parser.state.harmonySpecifier[`$${name}`] = [parser.state.lastHarmonyImport, HarmonyModulesHelpers.getModuleVar(parser.state, source), id];
parser.scope.definitions.delete(name);
parser.scope.renames.set(name, "imported var");
if(!parser.state.harmonySpecifier) parser.state.harmonySpecifier = new Map();
parser.state.harmonySpecifier.set(name, {
source,
id,
sourceOrder: parser.state.lastHarmonyImportOrder
});
return true;
});
parser.plugin("expression imported var", (expr) => {
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);
const settings = parser.state.harmonySpecifier.get(name);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, expr.range, this.strictExportPresence);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = true;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
return true;
});
parser.plugin("expression imported var.*", (expr) => {
const name = expr.object.name;
const settings = parser.state.harmonySpecifier[`$${name}`];
if(settings[2] !== null)
const settings = parser.state.harmonySpecifier.get(name);
if(settings.id !== null)
return false;
const dep = new HarmonyImportSpecifierDependency(settings[0], settings[1], expr.property.name || expr.property.value, name, expr.range, this.strictExportPresence);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, expr.property.name || expr.property.value, name, expr.range, this.strictExportPresence);
dep.shorthand = parser.scope.inShorthand;
dep.directImport = false;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
return true;
});
if(this.strictThisContextOnImports) {
@ -59,15 +66,15 @@ module.exports = class HarmonyImportDependencyParserPlugin {
if(expr.callee.type !== "MemberExpression") return;
if(expr.callee.object.type !== "Identifier") return;
const name = expr.callee.object.name;
const settings = parser.state.harmonySpecifier[`$${name}`];
if(settings[2] !== null)
const settings = parser.state.harmonySpecifier.get(name);
if(settings.id !== 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);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, 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);
parser.state.module.addDependency(dep);
if(expr.arguments)
parser.walkExpressions(expr.arguments);
return true;
@ -79,45 +86,43 @@ module.exports = class HarmonyImportDependencyParserPlugin {
expr = expr.callee;
if(expr.type !== "Identifier") return;
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);
const settings = parser.state.harmonySpecifier.get(name);
const dep = new HarmonyImportSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, expr.range, this.strictExportPresence);
dep.directImport = true;
dep.callArgs = args;
dep.call = fullExpr;
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.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))
.map(request => {
const dep = new HarmonyAcceptImportDependency(request, HarmonyModulesHelpers.getModuleVar(parser.state, request), expr.range);
const dep = new HarmonyAcceptImportDependency(request, parser.state.module, parser.state.harmonyParserScope);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
return dep;
});
if(dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(expr.range, dependencies, true);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
}
});
parser.plugin("hot accept without callback", (expr, requests) => {
const dependencies = requests
.filter(request => HarmonyModulesHelpers.checkModuleVar(parser.state, request))
.map(request => {
const dep = new HarmonyAcceptImportDependency(request, HarmonyModulesHelpers.getModuleVar(parser.state, request), expr.range);
const dep = new HarmonyAcceptImportDependency(request, parser.state.module, parser.state.harmonyParserScope);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
return dep;
});
if(dependencies.length > 0) {
const dep = new HarmonyAcceptDependency(expr.range, dependencies, false);
dep.loc = expr.loc;
parser.state.current.addDependency(dep);
parser.state.module.addDependency(dep);
}
});
}

View File

@ -0,0 +1,31 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("./HarmonyImportDependency");
class HarmonyImportSideEffectDependency extends HarmonyImportDependency {
constructor(request, originModule, sourceOrder, parserScope) {
super(request, originModule, sourceOrder, parserScope);
}
getReference() {
if(this.module && this.module.sideEffectFree) return null;
return super.getReference();
}
get type() {
return "harmony side effect evaluation";
}
}
HarmonyImportSideEffectDependency.Template = class HarmonyImportSideEffectDependencyTemplate extends HarmonyImportDependency.Template {
getHarmonyInitOrder(dep) {
if(dep.module && dep.module.sideEffectFree) return NaN;
return super.getHarmonyInitOrder(dep);
}
};
module.exports = HarmonyImportSideEffectDependency;

View File

@ -3,13 +3,11 @@
Author Tobias Koppers @sokra
*/
"use strict";
const NullDependency = require("./NullDependency");
const HarmonyImportDependency = require("./HarmonyImportDependency");
class HarmonyImportSpecifierDependency extends NullDependency {
constructor(importDependency, importedVar, id, name, range, strictExportPresence) {
super();
this.importDependency = importDependency;
this.importedVar = importedVar;
class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
constructor(request, originModule, sourceOrder, parserScope, id, name, range, strictExportPresence) {
super(request, originModule, sourceOrder, parserScope);
this.id = id;
this.name = name;
this.range = range;
@ -25,9 +23,9 @@ class HarmonyImportSpecifierDependency extends NullDependency {
}
getReference() {
if(!this.importDependency.module) return null;
if(!this.module) return null;
return {
module: this.importDependency.module,
module: this.module,
importedNames: this.id && !this.namespaceObjectAsContext ? [this.id] : true
};
}
@ -47,7 +45,7 @@ class HarmonyImportSpecifierDependency extends NullDependency {
}
_getErrors() {
const importedModule = this.importDependency.module;
const importedModule = this.module;
if(!importedModule || !importedModule.meta || !importedModule.meta.harmonyModule) {
return;
}
@ -61,38 +59,42 @@ class HarmonyImportSpecifierDependency extends NullDependency {
}
const idIsNotNameMessage = this.id !== this.name ? ` (imported as '${this.name}')` : "";
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.importDependency.userRequest}'`;
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`;
const err = new Error(errorMessage);
err.hideStack = true;
return [err];
}
// implement this method to allow the occurence order plugin to count correctly
getNumberOfIdOccurrences() {
return 0;
}
updateHash(hash) {
super.updateHash(hash);
const importedModule = this.importDependency.module;
hash.update((importedModule && importedModule.id) + "");
const importedModule = this.module;
hash.update((importedModule && this.id) + "");
hash.update((importedModule && this.importedVar) + "");
hash.update((importedModule && this.id && importedModule.isUsed(this.id)) + "");
hash.update((importedModule && (!importedModule.meta || importedModule.meta.harmonyModule)) + "");
hash.update((importedModule && (importedModule.used + JSON.stringify(importedModule.usedExports))) + "");
}
}
HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependencyTemplate {
apply(dep, source) {
const content = this.getContent(dep);
HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependencyTemplate extends HarmonyImportDependency.Template {
apply(dep, source, outputOptions, requestShortener) {
super.apply(dep, source, outputOptions, requestShortener);
const importedVar = dep.getImportVar(requestShortener);
const content = this.getContent(dep, importedVar);
source.replace(dep.range[0], dep.range[1] - 1, content);
}
getContent(dep) {
const importedModule = dep.importDependency.module;
const defaultImport = dep.directImport && dep.id === "default" && !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
const shortHandPrefix = this.getShortHandPrefix(dep);
const importedVar = dep.importedVar;
const importedVarSuffix = this.getImportVarSuffix(dep, defaultImport, importedModule);
getContent(dep, importedVar) {
const importedModule = dep.module;
const nonHarmonyDefaultImport = dep.directImport && dep.id === "default" && !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
const shortHandPrefix = dep.shorthand ? `${dep.name}: ` : "";
const importedVarSuffix = this.getImportVarSuffix(dep.id, nonHarmonyDefaultImport, importedModule);
if(dep.call && defaultImport) {
if(dep.call && nonHarmonyDefaultImport) {
return `${shortHandPrefix}${importedVar}_default()`;
}
@ -103,27 +105,19 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
return `${shortHandPrefix}${importedVar}${importedVarSuffix}`;
}
getImportVarSuffix(dep, defaultImport, importedModule) {
if(defaultImport) {
getImportVarSuffix(id, nonHarmonyDefaultImport, importedModule) {
if(nonHarmonyDefaultImport) {
return "_default.a";
}
if(dep.id) {
const used = importedModule ? importedModule.isUsed(dep.id) : dep.id;
const optionalComment = dep.id !== used ? " /* " + dep.id + " */" : "";
if(id) {
const used = importedModule ? importedModule.isUsed(id) : id;
const optionalComment = id !== used ? " /* " + id + " */" : "";
return `[${JSON.stringify(used)}${optionalComment}]`;
}
return "";
}
getShortHandPrefix(dep) {
if(!dep.shorthand) {
return "";
}
return dep.name + ": ";
}
};
module.exports = HarmonyImportSpecifierDependency;

View File

@ -0,0 +1,51 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const NullDependency = require("./NullDependency");
class HarmonyInitDependency extends NullDependency {
constructor(originModule) {
super();
this.originModule = originModule;
}
get type() {
return "harmony init";
}
}
module.exports = HarmonyInitDependency;
HarmonyInitDependency.Template = class HarmonyInitDependencyTemplate {
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.originModule;
const list = [];
for(const dependency of module.dependencies) {
const template = dependencyTemplates.get(dependency.constructor);
if(template && typeof template.harmonyInit === "function" && typeof template.getHarmonyInitOrder === "function") {
const order = template.getHarmonyInitOrder(dependency);
if(!isNaN(order)) {
list.push({
order,
listOrder: list.length,
dependency,
template,
});
}
}
}
list.sort((a, b) => {
const x = a.order - b.order;
if(x) return x;
return a.listOrder - b.listOrder;
});
for(const item of list) {
item.template.harmonyInit(item.dependency, source, outputOptions, requestShortener, dependencyTemplates);
}
}
};

View File

@ -1,32 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class HarmonyModulesHelpers {
static getModuleVar(state, request) {
if(!state.harmonyModules) state.harmonyModules = [];
let idx = state.harmonyModules.indexOf(request);
if(idx < 0) {
idx = state.harmonyModules.length;
state.harmonyModules.push(request);
}
return `__WEBPACK_IMPORTED_MODULE_${idx}_${request.replace(/[^A-Za-z0-9_]/g, "_").replace(/__+/g, "_")}__`;
}
static getNewModuleVar(state, request) {
if(state.harmonyModules && state.harmonyModules.indexOf(request) >= 0)
return null;
return this.getModuleVar(state, request);
}
static checkModuleVar(state, request) {
if(!state.harmonyModules || state.harmonyModules.indexOf(request) < 0)
return null;
return this.getModuleVar(state, request);
}
}
module.exports = HarmonyModulesHelpers;

View File

@ -3,9 +3,10 @@
Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("./HarmonyImportDependency");
const HarmonyCompatibilityDependency = require("./HarmonyCompatibilityDependency");
const HarmonyInitDependency = require("./HarmonyInitDependency");
const HarmonyImportSpecifierDependency = require("./HarmonyImportSpecifierDependency");
const HarmonyCompatiblilityDependency = require("./HarmonyCompatibilityDependency");
const HarmonyImportSideEffectDependency = require("./HarmonyImportSideEffectDependency");
const HarmonyExportHeaderDependency = require("./HarmonyExportHeaderDependency");
const HarmonyExportExpressionDependency = require("./HarmonyExportExpressionDependency");
const HarmonyExportSpecifierDependency = require("./HarmonyExportSpecifierDependency");
@ -28,15 +29,18 @@ class HarmonyModulesPlugin {
compiler.plugin("compilation", (compilation, params) => {
const normalModuleFactory = params.normalModuleFactory;
compilation.dependencyFactories.set(HarmonyImportDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyImportDependency, new HarmonyImportDependency.Template());
compilation.dependencyFactories.set(HarmonyCompatibilityDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyCompatibilityDependency, new HarmonyCompatibilityDependency.Template());
compilation.dependencyFactories.set(HarmonyImportSpecifierDependency, new NullFactory());
compilation.dependencyFactories.set(HarmonyInitDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyInitDependency, new HarmonyInitDependency.Template());
compilation.dependencyFactories.set(HarmonyImportSideEffectDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyImportSideEffectDependency, new HarmonyImportSideEffectDependency.Template());
compilation.dependencyFactories.set(HarmonyImportSpecifierDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyImportSpecifierDependency, new HarmonyImportSpecifierDependency.Template());
compilation.dependencyFactories.set(HarmonyCompatiblilityDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyCompatiblilityDependency, new HarmonyCompatiblilityDependency.Template());
compilation.dependencyFactories.set(HarmonyExportHeaderDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyExportHeaderDependency, new HarmonyExportHeaderDependency.Template());
@ -46,7 +50,7 @@ class HarmonyModulesPlugin {
compilation.dependencyFactories.set(HarmonyExportSpecifierDependency, new NullFactory());
compilation.dependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependency.Template());
compilation.dependencyFactories.set(HarmonyExportImportedSpecifierDependency, new NullFactory());
compilation.dependencyFactories.set(HarmonyExportImportedSpecifierDependency, normalModuleFactory);
compilation.dependencyTemplates.set(HarmonyExportImportedSpecifierDependency, new HarmonyExportImportedSpecifierDependency.Template());
compilation.dependencyFactories.set(HarmonyAcceptDependency, new NullFactory());

View File

@ -4,6 +4,7 @@
*/
"use strict";
const ModuleDependency = require("./ModuleDependency");
const Template = require("../Template");
const DepBlockHelpers = require("./DepBlockHelpers");
const webpackMissingPromiseModule = require("./WebpackMissingModule").promise;
@ -22,20 +23,12 @@ ImportDependency.Template = class ImportDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const depBlock = dep.block;
const promise = DepBlockHelpers.getDepBlockPromise(depBlock, outputOptions, requestShortener, "import()");
const comment = this.getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dep.request)) : "";
const content = this.getContent(promise, dep, comment);
source.replace(depBlock.range[0], depBlock.range[1] - 1, content);
}
getOptionalComment(pathinfo, shortenedRequest) {
if(!pathinfo) {
return "";
}
return `/*! ${shortenedRequest} */ `;
}
getContent(promise, dep, comment) {
if(promise && dep.module) {
const stringifiedId = JSON.stringify(dep.module.id);

View File

@ -4,6 +4,7 @@
*/
"use strict";
const ModuleDependency = require("./ModuleDependency");
const Template = require("../Template");
const webpackMissingPromiseModule = require("./WebpackMissingModule").promise;
class ImportEagerDependency extends ModuleDependency {
@ -19,20 +20,12 @@ class ImportEagerDependency extends ModuleDependency {
ImportEagerDependency.Template = class ImportEagerDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const comment = this.getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dep.request)) : "";
const content = this.getContent(dep, comment);
source.replace(dep.range[0], dep.range[1] - 1, content);
}
getOptionalComment(pathinfo, shortenedRequest) {
if(!pathinfo) {
return "";
}
return `/*! ${shortenedRequest} */ `;
}
getContent(dep, comment) {
if(dep.module) {
const stringifiedId = JSON.stringify(dep.module.id);

View File

@ -4,6 +4,7 @@
*/
"use strict";
const ModuleDependency = require("./ModuleDependency");
const Template = require("../Template");
const webpackMissingPromiseModule = require("./WebpackMissingModule").promise;
class ImportWeakDependency extends ModuleDependency {
@ -20,20 +21,12 @@ class ImportWeakDependency extends ModuleDependency {
ImportWeakDependency.Template = class ImportDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const comment = this.getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
const comment = outputOptions.pathinfo ? Template.toComment(requestShortener.shorten(dep.request)) : "";
const content = this.getContent(dep, comment);
source.replace(dep.range[0], dep.range[1] - 1, content);
}
getOptionalComment(pathinfo, shortenedRequest) {
if(!pathinfo) {
return "";
}
return `/*! ${shortenedRequest} */ `;
}
getContent(dep, comment) {
if(dep.module) {
const stringifiedId = JSON.stringify(dep.module.id);

View File

@ -4,12 +4,14 @@
*/
"use strict";
const Template = require("../Template");
class ModuleDependencyTemplateAsId {
apply(dep, source, outputOptions, requestShortener) {
if(!dep.range) return;
const comment = outputOptions.pathinfo ?
`/*! ${requestShortener.shorten(dep.request)} */ ` : "";
Template.toComment(requestShortener.shorten(dep.request)) + " " : "";
let content;
if(dep.module)
content = comment + JSON.stringify(dep.module.id);

View File

@ -4,12 +4,14 @@
*/
"use strict";
const Template = require("../Template");
class ModuleDependencyTemplateAsRequireId {
apply(dep, source, outputOptions, requestShortener) {
if(!dep.range) return;
const comment = outputOptions.pathinfo ?
`/*! ${requestShortener.shorten(dep.request)} */ ` : "";
Template.toComment(requestShortener.shorten(dep.request)) + " " : "";
let content;
if(dep.module)
content = `__webpack_require__(${comment}${JSON.stringify(dep.module.id)})`;

View File

@ -3,7 +3,9 @@
Author Tobias Koppers @sokra
*/
"use strict";
const ModuleDependency = require("./ModuleDependency");
const Template = require("../Template");
class RequireIncludeDependency extends ModuleDependency {
constructor(request, range) {
@ -18,16 +20,9 @@ class RequireIncludeDependency extends ModuleDependency {
RequireIncludeDependency.Template = class RequireIncludeDependencyTemplate {
apply(dep, source, outputOptions, requestShortener) {
const comment = this.getOptionalComment(outputOptions.pathinfo && dep.module, requestShortener.shorten(dep.request));
const comment = outputOptions.pathinfo ? Template.toComment(`require.include ${requestShortener.shorten(dep.request)}`) : "";
source.replace(dep.range[0], dep.range[1] - 1, `undefined${comment}`);
}
getOptionalComment(shouldHaveComment, shortenedRequest) {
if(shouldHaveComment) {
return "";
}
return `/*! require.include ${shortenedRequest} */`;
}
};
module.exports = RequireIncludeDependency;

View File

@ -14,7 +14,7 @@ module.exports = class NodeMainTemplatePlugin {
apply(mainTemplate) {
const asyncChunkLoading = this.asyncChunkLoading;
mainTemplate.plugin("local-vars", function(source, chunk) {
if(chunk.chunks.length > 0) {
if(chunk.getNumberOfChunks() > 0) {
return this.asString([
source,
"",
@ -28,7 +28,7 @@ module.exports = class NodeMainTemplatePlugin {
return source;
});
mainTemplate.plugin("require-extensions", function(source, chunk) {
if(chunk.chunks.length > 0) {
if(chunk.getNumberOfChunks() > 0) {
return this.asString([
source,
"",

View File

@ -17,7 +17,7 @@ class AggressiveMergingPlugin {
const minSizeReduce = options.minSizeReduce || 1.5;
function getParentsWeight(chunk) {
return chunk.parents.map((p) => {
return chunk.mapParents((p) => {
return p.isInitial() ? options.entryChunkMultiplicator || 10 : 1;
}).reduce((a, b) => {
return a + b;
@ -77,30 +77,30 @@ class AggressiveMergingPlugin {
if(pair.improvement < minSizeReduce) return;
if(options.moveToParents) {
const commonModules = pair.b.modules.filter((m) => {
return pair.a.modules.indexOf(m) >= 0;
const commonModules = new Set(pair.b.getModules().filter((m) => {
return pair.a.containsModule(m);
}));
const aOnlyModules = pair.b.getModules().filter((m) => {
return !commonModules.has(m);
});
const aOnlyModules = pair.b.modules.filter((m) => {
return commonModules.indexOf(m) < 0;
});
const bOnlyModules = pair.a.modules.filter((m) => {
return commonModules.indexOf(m) < 0;
const bOnlyModules = pair.a.getModules().filter((m) => {
return !commonModules.has(m);
});
aOnlyModules.forEach((m) => {
pair.b.removeModule(m);
m.removeChunk(pair.b);
pair.b.parents.forEach((c) => {
for(const c of pair.b.parentsIterable) {
c.addModule(m);
m.addChunk(c);
});
}
});
bOnlyModules.forEach((m) => {
pair.a.removeModule(m);
m.removeChunk(pair.a);
pair.a.parents.forEach((c) => {
for(const c of pair.a.parentsIterable) {
c.addModule(m);
m.addChunk(c);
});
}
});
}
if(pair.b.integrate(pair.a, "aggressive-merge")) {

View File

@ -153,6 +153,8 @@ class AggressiveSplittingPlugin {
// 3. save to made splittings to records
const minSize = this.options.minSize;
if(!records.aggressiveSplits) records.aggressiveSplits = [];
const newSplits = [];
let splittingInvalid = false;
compilation.chunks.forEach((chunk) => {
if(chunk.hasEntryModule()) return;
const size = chunk.size(this.options);
@ -160,8 +162,9 @@ class AggressiveSplittingPlugin {
const modules = chunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache));
if(typeof chunk._fromAggressiveSplittingIndex === "undefined") {
if(incorrectSize) return;
// this is a new chunk splitting, we record it so we reuse it next time
chunk.recorded = true;
records.aggressiveSplits.push({
newSplits.push({
modules: modules,
hash: chunk.hash,
id: chunk.id
@ -172,15 +175,20 @@ class AggressiveSplittingPlugin {
if(chunk._fromAggressiveSplitting) {
chunk._aggressiveSplittingInvalid = true;
splitData.invalid = true;
splittingInvalid = true;
} else {
splitData.hash = chunk.hash;
}
}
}
});
if(splittingInvalid) {
records.aggressiveSplits = records.aggressiveSplits.filter((splitData) => {
return !splitData.invalid;
});
} else {
records.aggressiveSplits = records.aggressiveSplits.concat(newSplits);
}
});
compilation.plugin("need-additional-seal", (callback) => {
const invalid = compilation.chunks.some((chunk) => {

View File

@ -215,17 +215,16 @@ Take a look at the "name"/"names" or async/children option.`);
}
getAffectedUnnamedChunks(affectedChunks, targetChunk, rootChunk, asyncOption, deepChildrenOption) {
let chunks = targetChunk.chunks;
chunks && chunks.forEach((chunk) => {
for(const chunk of targetChunk.chunksIterable) {
if(chunk.isInitial()) {
return;
continue;
}
// If all the parents of a chunk are either
// a) the target chunk we started with
// b) themselves affected chunks
// we can assume that this chunk is an affected chunk too, as there is no way a chunk that
// isn't only depending on the target chunk is a parent of the chunk tested
if(asyncOption || chunk.parents.every((parentChunk) => parentChunk === rootChunk || affectedChunks.has(parentChunk))) {
if(asyncOption || chunk.getParents().every((parentChunk) => parentChunk === rootChunk || affectedChunks.has(parentChunk))) {
// This check not only dedupes the affectedChunks but also guarantees we avoid endless loops
if(!affectedChunks.has(chunk)) {
// We mutate the affected chunks before going deeper, so the deeper levels and other branches
@ -242,7 +241,7 @@ Take a look at the "name"/"names" or async/children option.`);
}
}
}
});
}
}
getAffectedChunks(compilation, allChunks, targetChunk, targetChunks, currentIndex, selectedChunks, asyncOption, childrenOption, deepChildrenOption) {
@ -265,7 +264,7 @@ Take a look at the "name"/"names" or async/children option.`);
/**
* past this point only entry chunks are allowed to become commonChunks
*/
if(targetChunk.parents.length > 0) {
if(targetChunk.getNumberOfParents() > 0) {
compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + targetChunk.name + ")"));
return;
}
@ -283,7 +282,7 @@ Take a look at the "name"/"names" or async/children option.`);
return allChunks.filter((chunk) => {
const found = targetChunks.indexOf(chunk);
if(found >= currentIndex) return false;
return chunk.hasRuntime();
return chunk.isInitial();
});
}
@ -366,11 +365,11 @@ Take a look at the "name"/"names" or async/children option.`);
makeTargetChunkParentOfAffectedChunks(usedChunks, commonChunk) {
for(const chunk of usedChunks) {
// set commonChunk as new sole parent
chunk.parents = [commonChunk];
chunk.setParents([commonChunk]);
// add chunk to commonChunk
commonChunk.addChunk(chunk);
for(const entrypoint of chunk.entrypoints) {
for(const entrypoint of chunk.entrypoints.slice()) {
entrypoint.insertChunk(commonChunk, chunk);
}
}
@ -379,7 +378,7 @@ Take a look at the "name"/"names" or async/children option.`);
moveExtractedChunkBlocksToTargetChunk(chunks, targetChunk) {
for(const chunk of chunks) {
if(chunk === targetChunk) continue;
for(const block of chunk.blocks) {
for(const block of chunk.blocksIterable) {
if(block.chunks.indexOf(targetChunk) === -1) {
block.chunks.unshift(targetChunk);
}

View File

@ -12,6 +12,7 @@ const escope = require("escope");
const ReplaceSource = require("webpack-sources/lib/ReplaceSource");
const ConcatSource = require("webpack-sources/lib/ConcatSource");
const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency");
const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency");
@ -38,8 +39,7 @@ function getExternalImport(importedModule, info, exportName, asCall) {
if(info.interop && exportName === "default") {
return asCall ? `${info.interopName}()` : `${info.interopName}.a`;
}
// TODO use Template.toNormalComment when merging with pure-module
const comment = used !== exportName ? ` /* ${exportName} */` : "";
const comment = used !== exportName ? ` ${Template.toNormalComment(exportName)}` : "";
const reference = `${info.name}[${JSON.stringify(used)}${comment}]`;
if(asCall)
return `Object(${reference})`;
@ -70,8 +70,7 @@ function getFinalName(info, exportName, moduleToInfoMap, requestShortener, asCal
const problem = `Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(requestShortener)}"` +
` (known exports: ${Array.from(info.exportMap.keys()).filter(name => name !== true).join(" ")}, ` +
`known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`;
// TODO use Template.toNormalComment when merging with pure-module
return `/* ${problem} */ undefined`;
return `${Template.toNormalComment(problem)} undefined`;
}
case "external":
{
@ -261,15 +260,18 @@ class ConcatenatedModule extends Module {
const set = new Set();
function getConcatenatedImports(module) {
// TODO need changes when merging with the pure-module branch
const allDeps = module.dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module);
return allDeps.map(dep => () => dep.module);
return module.dependencies
.filter(dep => dep instanceof HarmonyImportDependency)
.sort((a, b) => a.sourceOrder - b.sourceOrder)
.map(dep => () => {
const ref = dep.getReference();
return ref && ref.module;
});
}
function enterModule(getModule) {
const module = getModule();
if(!module) return;
if(set.has(module)) return;
set.add(module);
if(modulesSet.has(module)) {
@ -315,7 +317,7 @@ class ConcatenatedModule extends Module {
} else if(dep instanceof HarmonyExportImportedSpecifierDependency) {
const exportName = dep.name;
const importName = dep.id;
const importedModule = dep.importDependency.module;
const importedModule = dep.module;
if(exportName && importName) {
if(!reexportMap.has(exportName)) {
reexportMap.set(exportName, {
@ -352,6 +354,7 @@ class ConcatenatedModule extends Module {
module: info.module,
index: idx,
ast: undefined,
internalSource: undefined,
source: undefined,
globalScope: undefined,
moduleScope: undefined,
@ -387,8 +390,8 @@ class ConcatenatedModule extends Module {
dependencyTemplates.get(HarmonyImportSpecifierDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyImportDependency, new HarmonyImportDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportDependency),
innerDependencyTemplates.set(HarmonyImportSideEffectDependency, new HarmonyImportSideEffectDependencyConcatenatedTemplate(
dependencyTemplates.get(HarmonyImportSideEffectDependency),
moduleToInfoMap
));
innerDependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependencyConcatenatedTemplate(
@ -446,6 +449,7 @@ class ConcatenatedModule extends Module {
const moduleScope = globalScope.childScopes[0];
const resultSource = new ReplaceSource(source);
info.ast = ast;
info.internalSource = source;
info.source = resultSource;
info.globalScope = globalScope;
info.moduleScope = moduleScope;
@ -662,8 +666,26 @@ class HarmonyImportSpecifierDependencyConcatenatedTemplate {
this.modulesMap = modulesMap;
}
getHarmonyInitOrder(dep) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.importDependency.module;
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
@ -685,12 +707,30 @@ class HarmonyImportSpecifierDependencyConcatenatedTemplate {
}
}
class HarmonyImportDependencyConcatenatedTemplate {
class HarmonyImportSideEffectDependencyConcatenatedTemplate {
constructor(originalTemplate, modulesMap) {
this.originalTemplate = originalTemplate;
this.modulesMap = modulesMap;
}
getHarmonyInitOrder(dep) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
@ -698,7 +738,6 @@ class HarmonyImportDependencyConcatenatedTemplate {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
source.replace(dep.range[0], dep.range[1] - 1, "");
}
}
@ -708,6 +747,20 @@ class HarmonyExportSpecifierDependencyConcatenatedTemplate {
this.rootModule = rootModule;
}
getHarmonyInitOrder(dep) {
if(dep.originModule === this.rootModule) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.originalTemplate.harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates);
@ -747,7 +800,7 @@ class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
}
getExports(dep) {
const importModule = dep.importDependency.module;
const importModule = dep.module;
if(dep.id) {
// export { named } from "module"
return [{
@ -774,9 +827,27 @@ class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate {
});
}
getHarmonyInitOrder(dep) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
return this.originalTemplate.getHarmonyInitOrder(dep);
}
return NaN;
}
harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates) {
const module = dep.module;
const info = this.modulesMap.get(module);
if(!info) {
this.originalTemplate.harmonyInit(dep, source, outputOptions, requestShortener, dependencyTemplates);
return;
}
}
apply(dep, source, outputOptions, requestShortener, dependencyTemplates) {
if(dep.originModule === this.rootModule) {
if(this.modulesMap.get(dep.importDependency.module)) {
if(this.modulesMap.get(dep.module)) {
const exportDefs = this.getExports(dep);
exportDefs.forEach(def => {
const info = this.modulesMap.get(def.module);

View File

@ -19,13 +19,13 @@ class EnsureChunkConditionsPlugin {
if(!usedChunks) triesMap.set(module, usedChunks = new Set());
usedChunks.add(chunk);
const newChunks = [];
chunk.parents.forEach((parent) => {
for(const parent of chunk.parentsIterable) {
if(!usedChunks.has(parent)) {
parent.addModule(module);
module.addChunk(parent);
newChunks.push(parent);
}
});
}
module.rewriteChunkInReasons(chunk, newChunks);
chunk.removeModule(module);
changed = true;

View File

@ -11,7 +11,7 @@ class MergeDuplicateChunksPlugin {
compilation.plugin("optimize-chunks-basic", (chunks) => {
const map = Object.create(null);
chunks.slice().forEach((chunk) => {
if(chunk.hasRuntime() || chunk.hasEntryModule()) return;
if(chunk.hasEntryModule()) return;
const ident = chunk.getModulesIdent();
const otherChunk = map[ident];
if(otherChunk) {

View File

@ -8,7 +8,6 @@ const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency
const ModuleHotAcceptDependency = require("../dependencies/ModuleHotAcceptDependency");
const ModuleHotDeclineDependency = require("../dependencies/ModuleHotDeclineDependency");
const ConcatenatedModule = require("./ConcatenatedModule");
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
function formatBailoutReason(msg) {
@ -189,18 +188,17 @@ class ModuleConcatenationPlugin {
// Only harmony Dependencies
.filter(dep => dep instanceof HarmonyImportDependency && dep.module)
// Get reference info for this dependency
.map(dep => dep.getReference())
// Reference is valid and has a module
.filter(ref => ref && ref.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)
);
})
.filter(ref => Array.isArray(ref.importedNames) || Array.isArray(ref.module.providedExports))
// Take the imported module
.map(dep => dep.module)
.map(ref => ref.module)
));
}
@ -235,6 +233,10 @@ class ModuleConcatenationPlugin {
// Every module which depends on the added module must be in the configuration too.
for(const reason of module.reasons) {
// Modules that are not used can be ignored
if(reason.module.sideEffectFree && reason.module.used === false) continue;
const problem = this.tryToAdd(testConfig, reason.module, possibleModules, failureCache);
if(problem) {
failureCache.set(module, problem); // cache failures for performance

View File

@ -37,7 +37,11 @@ class OccurrenceOrderPlugin {
};
const countOccurs = (sum, r) => {
if(!r.module) return sum;
return sum + r.module.getNumberOfChunks();
let factor = 1;
if(typeof r.dependency.getNumberOfIdOccurrences === "function")
factor = r.dependency.getNumberOfIdOccurrences();
if(factor === 0) return sum;
return sum + factor * r.module.getNumberOfChunks();
};
if(preferEntry) {
@ -72,7 +76,7 @@ class OccurrenceOrderPlugin {
const occursInInitialChunksMap = new Map();
chunks.forEach(c => {
const result = c.parents.reduce((sum, p) => {
const result = c.getParents().reduce((sum, p) => {
if(p.isInitial()) return sum + 1;
return sum;
}, 0);
@ -80,7 +84,7 @@ class OccurrenceOrderPlugin {
});
function occurs(c) {
return c.blocks.length;
return c.getNumberOfBlocks();
}
chunks.sort((a, b) => {

View File

@ -6,8 +6,8 @@
function hasModule(chunk, module, checkedChunks) {
if(chunk.containsModule(module)) return [chunk];
if(chunk.parents.length === 0) return false;
return allHaveModule(chunk.parents.filter((c) => {
if(chunk.getNumberOfParents() === 0) return false;
return allHaveModule(chunk.getParents().filter((c) => {
return !checkedChunks.has(c);
}), module, checkedChunks);
}
@ -35,7 +35,7 @@ class RemoveParentModulesPlugin {
compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => {
for(var index = 0; index < chunks.length; index++) {
var chunk = chunks[index];
if(chunk.parents.length === 0) continue;
if(chunk.getNumberOfParents() === 0) continue;
// TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311
var cache = Object.create(null);
@ -46,11 +46,11 @@ class RemoveParentModulesPlugin {
var dId = module.getChunkIdsIdent();
var parentChunksWithModule;
if(dId === null) {
parentChunksWithModule = allHaveModule(chunk.parents, module);
parentChunksWithModule = allHaveModule(chunk.getParents(), module);
} else if(dId in cache) {
parentChunksWithModule = cache[dId];
} else {
parentChunksWithModule = cache[dId] = allHaveModule(chunk.parents, module);
parentChunksWithModule = cache[dId] = allHaveModule(chunk.getParents(), module);
}
if(parentChunksWithModule) {
module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule));

View File

@ -0,0 +1,116 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");
const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");
class SideEffectsFlagPlugin {
apply(compiler) {
compiler.plugin("normal-module-factory", nmf => {
nmf.plugin("module", (module, data) => {
const resolveData = data.resourceResolveData;
if(resolveData && resolveData.descriptionFileData && resolveData.relativePath) {
const sideEffects = resolveData.descriptionFileData.sideEffects;
const isSideEffectFree = sideEffects === false; // TODO allow more complex expressions
if(isSideEffectFree) {
module.sideEffectFree = true;
}
}
return module;
});
});
compiler.plugin("compilation", compilation => {
compilation.plugin("optimize-dependencies", modules => {
const reexportMaps = new Map();
// Capture reexports of sideEffectFree modules
for(const module of modules) {
const removeDependencies = [];
for(const dep of module.dependencies) {
if(dep instanceof HarmonyImportSideEffectDependency) {
if(dep.module && dep.module.sideEffectFree) {
removeDependencies.push(dep);
}
} else if(dep instanceof HarmonyExportImportedSpecifierDependency) {
if(module.sideEffectFree) {
const mode = dep.getMode(true);
if(mode.type === "safe-reexport") {
let map = reexportMaps.get(module);
if(!map) {
reexportMaps.set(module, map = new Map());
}
for(const pair of mode.map) {
map.set(pair[0], {
module: mode.module,
exportName: pair[1]
});
}
}
}
}
}
for(const dep of removeDependencies) {
module.removeDependency(dep);
dep.module.reasons = dep.module.reasons.filter(r => r.dependency !== dep);
}
}
// Flatten reexports
for(const map of reexportMaps.values()) {
for(const pair of map) {
let mapping = pair[1];
while(mapping) {
const innerMap = reexportMaps.get(mapping.module);
if(!innerMap) break;
const newMapping = innerMap.get(mapping.exportName);
if(newMapping) {
map.set(pair[0], newMapping);
}
mapping = newMapping;
}
}
}
// Update imports along the reexports from sideEffectFree modules
const updates = [];
for(const pair of reexportMaps) {
const module = pair[0];
const map = pair[1];
for(const reason of module.reasons) {
const dep = reason.dependency;
if(dep instanceof HarmonyImportSpecifierDependency) {
const mapping = map.get(dep.id);
if(mapping) {
updates.push({
dep,
mapping,
module,
reason
});
}
}
}
}
// Execute updates
for(const update of updates) {
const dep = update.dep;
const mapping = update.mapping;
const module = update.module;
const reason = update.reason;
dep.module = mapping.module;
dep.id = mapping.exportName;
module.removeReason(reason.module, dep);
mapping.module.addReason(reason.module, dep);
}
});
});
}
}
module.exports = SideEffectsFlagPlugin;

View File

@ -6,6 +6,7 @@ module.exports = class SortableSet extends Set {
super(initialIterable);
this._sortFn = defaultSort;
this._lastActiveSortFn = null;
this._frozenArray = null;
}
/**
@ -14,10 +15,21 @@ module.exports = class SortableSet extends Set {
*/
add(value) {
this._lastActiveSortFn = null;
this._frozenArray = null;
super.add(value);
return this;
}
delete(value) {
this._frozenArray = null;
return super.delete(value);
}
clear() {
this._frozenArray = null;
return super.clear();
}
/**
* @param {Function} sortFn - function to sort the set
* @returns {void}
@ -31,9 +43,10 @@ module.exports = class SortableSet extends Set {
const sortedArray = Array.from(this).sort(sortFn);
super.clear();
for(let i = 0; i < sortedArray.length; i += 1) {
this.add(sortedArray[i]);
super.add(sortedArray[i]);
}
this._lastActiveSortFn = sortFn;
this._frozenArray = null;
}
/**
@ -42,4 +55,14 @@ module.exports = class SortableSet extends Set {
sort() {
this.sortWith(this._sortFn);
}
/**
* @returns {any[]} - returns content as frozen array
*/
getFrozenArray() {
if(this._frozenArray === null) {
this._frozenArray = Object.freeze(Array.from(this));
}
return this._frozenArray;
}
};

108
lib/util/StackedSetMap.js Normal file
View File

@ -0,0 +1,108 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const util = require("util");
const TOMBSTONE = {};
const UNDEFINED_MARKER = {};
class StackedSetMap {
constructor(parentStack) {
this.stack = parentStack === undefined ? [] : parentStack.slice();
this.map = new Map();
this.stack.push(this.map);
}
add(item) {
this.map.set(item, true);
}
set(item, value) {
this.map.set(item, value === undefined ? UNDEFINED_MARKER : value);
}
delete(item) {
if(this.stack.length > 1)
this.map.set(item, TOMBSTONE);
else
this.map.delete(item);
}
has(item) {
const topValue = this.map.get(item);
if(topValue !== undefined)
return topValue !== TOMBSTONE;
if(this.stack.length > 1) {
for(var i = this.stack.length - 2; i >= 0; i--) {
const value = this.stack[i].get(item);
if(value !== undefined) {
this.map.set(item, value);
return value !== TOMBSTONE;
}
}
this.map.set(item, TOMBSTONE);
}
return false;
}
get(item) {
const topValue = this.map.get(item);
if(topValue !== undefined)
return topValue === TOMBSTONE || topValue === UNDEFINED_MARKER ? undefined : topValue;
if(this.stack.length > 1) {
for(var i = this.stack.length - 2; i >= 0; i--) {
const value = this.stack[i].get(item);
if(value !== undefined) {
this.map.set(item, value);
return value === TOMBSTONE || value === UNDEFINED_MARKER ? undefined : value;
}
}
this.map.set(item, TOMBSTONE);
}
return undefined;
}
_compress() {
this.map = new Map();
for(const data of this.stack) {
for(const pair of data) {
if(pair[1] === TOMBSTONE)
this.map.delete(pair[0]);
else
this.map.set(pair[0], pair[1]);
}
}
this.stack = [this.map];
}
asSet() {
this._compress();
return new Set(Array.from(this.map.entries()).map(pair => pair[0]));
}
asMap() {
this._compress();
return new Map(this.map.entries());
}
createChild() {
return new StackedSetMap(this.stack);
}
get length() {
throw new Error("This is no longer an Array");
}
set length(value) {
throw new Error("This is no longer an Array");
}
}
StackedSetMap.prototype.push = util.deprecate(function(item) {
this.add(item);
}, "This is no longer an Array: Use add instead.");
module.exports = StackedSetMap;

View File

@ -22,8 +22,7 @@ function webpack(options, callback) {
if(Array.isArray(options)) {
compiler = new MultiCompiler(options.map(options => webpack(options)));
} else if(typeof options === "object") {
// TODO webpack 4: process returns options
new WebpackOptionsDefaulter().process(options);
options = new WebpackOptionsDefaulter().process(options);
compiler = new Compiler();
compiler.context = options.context;
@ -115,6 +114,7 @@ exportPlugins(exports.optimize = {}, {
"LimitChunkCountPlugin": () => require("./optimize/LimitChunkCountPlugin"),
"MinChunkSizePlugin": () => require("./optimize/MinChunkSizePlugin"),
"ModuleConcatenationPlugin": () => require("./optimize/ModuleConcatenationPlugin"),
"SideEffectsFlagPlugin": () => require("./optimize/SideEffectsFlagPlugin"),
"OccurrenceOrderPlugin": () => require("./optimize/OccurrenceOrderPlugin"),
"UglifyJsPlugin": () => require("./optimize/UglifyJsPlugin")
});

View File

@ -13,7 +13,6 @@ function webpack(options, callback) {
new WebpackOptionsDefaulter().process(options);
const compiler = new Compiler();
compiler.options = options;
compiler.options = new WebpackOptionsApply().process(options, compiler);
new WebEnvironmentPlugin(options.inputFileSystem, options.outputFileSystem).apply(compiler);
if(callback) {

View File

@ -9,7 +9,7 @@ const Template = require("../Template");
class WebWorkerMainTemplatePlugin {
apply(mainTemplate) {
mainTemplate.plugin("local-vars", function(source, chunk) {
if(chunk.chunks.length > 0) {
if(chunk.getNumberOfChunks() > 0) {
return this.asString([
source,
"",
@ -48,7 +48,7 @@ class WebWorkerMainTemplatePlugin {
]);
});
mainTemplate.plugin("bootstrap", function(source, chunk, hash) {
if(chunk.chunks.length > 0) {
if(chunk.getNumberOfChunks() > 0) {
const chunkCallbackName = this.outputOptions.chunkCallbackName || Template.toIdentifier("webpackChunk" + (this.outputOptions.library || ""));
return this.asString([
source,

View File

@ -20,7 +20,7 @@
"node-libs-browser": "^2.0.0",
"source-map": "^0.5.3",
"supports-color": "^4.2.1",
"tapable": "^0.2.7",
"tapable": "^0.2.8",
"uglifyjs-webpack-plugin": "^0.4.6",
"watchpack": "^1.4.0",
"webpack-sources": "^1.0.1",
@ -88,13 +88,13 @@
"schemas/"
],
"scripts": {
"test": "mocha test/*.test.js --max-old-space-size=4096 --harmony --check-leaks",
"test": "mocha test/*.test.js --max-old-space-size=4096 --harmony --trace-deprecation --check-leaks",
"travis:test": "npm run cover:min",
"travis:lint": "npm run lint-files && npm run nsp",
"travis:benchmark": "npm run benchmark",
"appveyor:test": "node node_modules\\mocha\\bin\\mocha --max-old-space-size=4096 --harmony test/*.test.js",
"appveyor:test": "node node_modules\\mocha\\bin\\mocha --max-old-space-size=4096 --harmony --trace-deprecation test/*.test.js",
"appveyor:benchmark": "npm run benchmark",
"circleci:test": "node node_modules/mocha/bin/mocha --max-old-space-size=4096 --harmony test/*.test.js",
"circleci:test": "node node_modules/mocha/bin/mocha --max-old-space-size=4096 --harmony --trace-deprecation test/*.test.js",
"circleci:lint": "npm run lint-files && npm run nsp",
"build:examples": "cd examples && node buildAll.js",
"pretest": "npm run lint-files",
@ -103,9 +103,9 @@
"fix": "npm run lint -- --fix",
"beautify-lint": "beautify-lint \"lib/**/*.js\" \"hot/**/*.js\" \"bin/**/*.js\" \"benchmark/*.js\" \"test/*.js\"",
"nsp": "nsp check --output summary",
"benchmark": "mocha --max-old-space-size=4096 --harmony test/*.benchmark.js -R spec",
"cover": "node --max-old-space-size=4096 --harmony ./node_modules/istanbul/lib/cli.js cover -x '**/*.runtime.js' node_modules/mocha/bin/_mocha -- test/*.test.js",
"cover:min": "node --max-old-space-size=4096 --harmony ./node_modules/istanbul/lib/cli.js cover -x '**/*.runtime.js' --report lcovonly node_modules/mocha/bin/_mocha -- test/*.test.js",
"benchmark": "mocha --max-old-space-size=4096 --harmony --trace-deprecation test/*.benchmark.js -R spec",
"cover": "node --max-old-space-size=4096 --harmony --trace-deprecation ./node_modules/istanbul/lib/cli.js cover -x '**/*.runtime.js' node_modules/mocha/bin/_mocha -- test/*.test.js",
"cover:min": "node --max-old-space-size=4096 --harmony --trace-deprecation ./node_modules/istanbul/lib/cli.js cover -x '**/*.runtime.js' --report lcovonly node_modules/mocha/bin/_mocha -- test/*.test.js",
"publish-patch": "npm run lint && npm run beautify-lint && mocha && npm version patch && git push && git push --tags && npm publish"
}
}

View File

@ -138,14 +138,6 @@
"exprContextRequest": {
"type": "string"
},
"loaders": {
"allOf": [
{
"$ref": "#/definitions/ruleSet-rules"
}
],
"description": "An array of automatically applied loaders."
},
"noParse": {
"description": "Don't parse files matching. It's matched against the full resolved request.",
"anyOf": [

View File

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

View File

@ -12,7 +12,7 @@ describe("Compiler (caching)", function() {
this.timeout(15000);
function compile(entry, options, callback) {
new WebpackOptionsDefaulter().process(options);
options = new WebpackOptionsDefaulter().process(options);
options.entry = entry;
options.context = path.join(__dirname, "fixtures");
options.output.path = "/";

View File

@ -12,7 +12,7 @@ const Compiler = require("../lib/Compiler");
describe("Compiler", () => {
function compile(entry, options, callback) {
const noOutputPath = !options.output || !options.output.path;
new WebpackOptionsDefaulter().process(options);
options = new WebpackOptionsDefaulter().process(options);
options.entry = entry;
options.context = path.join(__dirname, "fixtures");
if(noOutputPath) options.output.path = "/";
@ -161,7 +161,7 @@ describe("Compiler", () => {
bundle.should.not.containEql("fixtures");
chunk.should.not.containEql("fixtures");
bundle.should.containEql("webpackJsonp");
chunk.should.containEql("webpackJsonp(");
chunk.should.containEql("window[\"webpackJsonp\"] || []).push");
done();
});
});

View File

@ -33,6 +33,13 @@ describe("Examples", () => {
options.output.filename = "output.js";
if(!options.entry)
options.entry = "./example.js";
if(!options.plugins)
options.plugins = [];
// To support deprecated loaders
// TODO remove in webpack 5
options.plugins.push(new webpack.LoaderOptionsPlugin({
options: {}
}));
}
webpack(options, (err, stats) => {
if(err) return done(err);

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