Repository: webpack/webpack
Pull request #7597 (2018-06-26 09:11:29)

Disable legacy plugins
- Disable RequireJS support by default
- Disable `System.import()` by default

**What kind of change does this PR introduce?**

refactoring

**Did you add tests for your changes?**

no

**Does this PR introduce a breaking change?**

yes

**What needs to be documented once your changes are merged?**

- `parser.requireJs` now defaults to `false`
- `parser.system` now defaults to `false`



Patch:
diff --git a/.travis.yml b/.travis.yml
index 208440d70..184cf5ee2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,6 +40,10 @@ matrix:
       node_js: "8"
       env: NO_WATCH_TESTS=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=integration
       stage: versions
+    - os: linux
+      node_js: "6"
+      env: NO_WATCH_TESTS=1 JEST="--maxWorkers=2 --cacheDirectory .jest-cache" JOB_PART=integration
+      stage: versions
   fast_finish: true
   allow_failures:
     - os: osx
diff --git a/appveyor.yml b/appveyor.yml
index db4d6e685..bd61e1135 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,7 +22,7 @@ environment:
     - nodejs_version: 10
       job_part: integration
       jest: --maxWorkers=2 --cacheDirectory .jest-cache
-    - nodejs_version: 8
+    - nodejs_version: 6
       job_part: integration
       jest: --maxWorkers=2 --cacheDirectory .jest-cache
 
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 000000000..769cc7252
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,23 @@
+machine:
+  environment:
+    NO_WATCH_TESTS: 1
+
+machine:
+  environment:
+    PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin"
+
+dependencies:
+  pre:
+    - case $CIRCLE_NODE_INDEX in 0) NODE_VERSION=6 ;; 1) NODE_VERSION=8 ;; esac; nvm install $NODE_VERSION && nvm alias default $NODE_VERSION
+  override:
+    - yarn
+    - yarn link || true && yarn link webpack
+  cache_directories:
+    - ~/.cache/yarn
+
+test:
+  override:
+    - node -v
+    - yarn --version
+    - yarn run circleci:lint
+    - yarn run circleci:test
diff --git a/examples/i18n/README.md b/examples/i18n/README.md
new file mode 100644
index 000000000..5750f3296
--- /dev/null
+++ b/examples/i18n/README.md
@@ -0,0 +1,287 @@
+This example uses the I18nPlugin in combination with the multi-compiler feature.
+
+The `webpack.config.js` exports an array of all config combinations that should be compiled. In this example two different parameters for the I18nPlugin are used.
+
+The I18nPlugin replaces every occurrence of the i18n function `__(...)` with a const string. i. e. `__("Hello World")` with `"Hello World"` resp. `"Hallo Welt"`.
+
+
+# example.js
+
+``` javascript
+console.log(__("Hello World"));
+console.log(__("Missing Text"));
+```
+
+# webpack.config.js
+
+``` javascript
+var path = require("path");
+var I18nPlugin = require("i18n-webpack-plugin");
+var languages = {
+	en: null,
+	de: require("./de.json")
+};
+module.exports = Object.keys(languages).map(function(language) {
+	return {
+		name: language,
+		// mode: "development || "production",
+		entry: "./example",
+		output: {
+			path: path.join(__dirname, "dist"),
+			filename: language + ".output.js"
+		},
+		plugins: [new I18nPlugin(languages[language])]
+	};
+});
+```
+
+# de.json
+
+``` javascript
+{
+	"Hello World": "Hallo Welt"
+}
+```
+
+# dist/de.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
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// define __esModule on exports
+/******/ 	__webpack_require__.r = function(exports) {
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
+/******/ 	};
+/******/
+/******/ 	// 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 = "dist/";
+/******/
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+```
+
+</details>
+
+``` javascript
+/******/ ([
+/* 0 */
+/*!********************!*\
+  !*** ./example.js ***!
+  \********************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+console.log("Hallo Welt");
+console.log("Missing Text");
+
+/***/ })
+/******/ ]);
+```
+
+# dist/en.output.js
+
+``` 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
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// define __esModule on exports
+/******/ 	__webpack_require__.r = function(exports) {
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
+/******/ 	};
+/******/
+/******/ 	// 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 = "dist/";
+/******/
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/*!********************!*\
+  !*** ./example.js ***!
+  \********************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+console.log("Hello World");
+console.log("Missing Text");
+
+/***/ })
+/******/ ]);
+```
+
+# Info
+
+## Unoptimized
+
+```
+Hash: 0a1b2c3d4e5f6a7b8c9d
+Version: webpack 4.8.0
+Child en:
+    Hash: 0a1b2c3d4e5f6a7b8c9d
+           Asset     Size  Chunks             Chunk Names
+    en.output.js  2.8 KiB       0  [emitted]  main
+    Entrypoint main = en.output.js
+    chunk    {0} en.output.js (main) 65 bytes [entry] [rendered]
+        > ./example main
+     [0] ./example.js 65 bytes {0} [built]
+         single entry ./example  main
+Child de:
+    Hash: 0a1b2c3d4e5f6a7b8c9d
+           Asset     Size  Chunks             Chunk Names
+    de.output.js  2.8 KiB       0  [emitted]  main
+    Entrypoint main = de.output.js
+    chunk    {0} de.output.js (main) 65 bytes [entry] [rendered]
+        > ./example main
+     [0] ./example.js 65 bytes {0} [built] [1 warning]
+         single entry ./example  main
+    
+    WARNING in ./example.js
+    Missing localization: Missing Text
+```
+
+## Production mode
+
+```
+Hash: 0a1b2c3d4e5f6a7b8c9d
+Version: webpack 4.8.0
+Child en:
+    Hash: 0a1b2c3d4e5f6a7b8c9d
+           Asset       Size  Chunks             Chunk Names
+    en.output.js  606 bytes       0  [emitted]  main
+    Entrypoint main = en.output.js
+    chunk    {0} en.output.js (main) 65 bytes [entry] [rendered]
+        > ./example main
+     [0] ./example.js 65 bytes {0} [built]
+         single entry ./example  main
+Child de:
+    Hash: 0a1b2c3d4e5f6a7b8c9d
+           Asset       Size  Chunks             Chunk Names
+    de.output.js  605 bytes       0  [emitted]  main
+    Entrypoint main = de.output.js
+    chunk    {0} de.output.js (main) 65 bytes [entry] [rendered]
+        > ./example main
+     [0] ./example.js 65 bytes {0} [built] [1 warning]
+         single entry ./example  main
+    
+    WARNING in ./example.js
+    Missing localization: Missing Text
+```
\ No newline at end of file
diff --git a/examples/i18n/build.js b/examples/i18n/build.js
new file mode 100644
index 000000000..39292a5b7
--- /dev/null
+++ b/examples/i18n/build.js
@@ -0,0 +1,2 @@
+global.NO_TARGET_ARGS = true;
+require("../build-common");
\ No newline at end of file
diff --git a/examples/i18n/de.json b/examples/i18n/de.json
new file mode 100644
index 000000000..9c27533a9
--- /dev/null
+++ b/examples/i18n/de.json
@@ -0,0 +1,3 @@
+{
+	"Hello World": "Hallo Welt"
+}
\ No newline at end of file
diff --git a/examples/i18n/example.js b/examples/i18n/example.js
new file mode 100644
index 000000000..463dccb3a
--- /dev/null
+++ b/examples/i18n/example.js
@@ -0,0 +1,2 @@
+console.log(__("Hello World"));
+console.log(__("Missing Text"));
\ No newline at end of file
diff --git a/examples/i18n/template.md b/examples/i18n/template.md
new file mode 100644
index 000000000..88d5f719c
--- /dev/null
+++ b/examples/i18n/template.md
@@ -0,0 +1,50 @@
+This example uses the I18nPlugin in combination with the multi-compiler feature.
+
+The `webpack.config.js` exports an array of all config combinations that should be compiled. In this example two different parameters for the I18nPlugin are used.
+
+The I18nPlugin replaces every occurrence of the i18n function `__(...)` with a const string. i. e. `__("Hello World")` with `"Hello World"` resp. `"Hallo Welt"`.
+
+
+# example.js
+
+``` javascript
+{{example.js}}
+```
+
+# webpack.config.js
+
+``` javascript
+{{webpack.config.js}}
+```
+
+# de.json
+
+``` javascript
+{{de.json}}
+```
+
+# dist/de.output.js
+
+``` javascript
+{{dist/de.output.js}}
+```
+
+# dist/en.output.js
+
+``` javascript
+{{dist/en.output.js}}
+```
+
+# Info
+
+## Unoptimized
+
+```
+{{stdout}}
+```
+
+## Production mode
+
+```
+{{production:stdout}}
+```
\ No newline at end of file
diff --git a/examples/i18n/webpack.config.js b/examples/i18n/webpack.config.js
new file mode 100644
index 000000000..cc0267fa9
--- /dev/null
+++ b/examples/i18n/webpack.config.js
@@ -0,0 +1,18 @@
+var path = require("path");
+var I18nPlugin = require("i18n-webpack-plugin");
+var languages = {
+	en: null,
+	de: require("./de.json")
+};
+module.exports = Object.keys(languages).map(function(language) {
+	return {
+		name: language,
+		// mode: "development || "production",
+		entry: "./example",
+		output: {
+			path: path.join(__dirname, "dist"),
+			filename: language + ".output.js"
+		},
+		plugins: [new I18nPlugin(languages[language])]
+	};
+});
diff --git a/lib/AsyncDependenciesBlock.js b/lib/AsyncDependenciesBlock.js
index 2dc26f91c..3bf22d2e2 100644
--- a/lib/AsyncDependenciesBlock.js
+++ b/lib/AsyncDependenciesBlock.js
@@ -28,6 +28,14 @@ module.exports = class AsyncDependenciesBlock extends DependenciesBlock {
 		this.groupOptions.name = value;
 	}
 
+	get chunks() {
+		throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
+	}
+
+	set chunks(value) {
+		throw new Error("Moved to AsyncDependenciesBlock.chunkGroup");
+	}
+
 	updateHash(hash) {
 		hash.update(JSON.stringify(this.groupOptions));
 		hash.update(
diff --git a/lib/Chunk.js b/lib/Chunk.js
index a92c611af..774eb445a 100644
--- a/lib/Chunk.js
+++ b/lib/Chunk.js
@@ -4,11 +4,14 @@ Author Tobias Koppers @sokra
 */
 "use strict";
 
+const util = require("util");
 const SortableSet = require("./util/SortableSet");
 const intersect = require("./util/SetHelpers").intersect;
 const GraphHelpers = require("./GraphHelpers");
-
 let debugId = 1000;
+const ERR_CHUNK_ENTRY = "Chunk.entry was removed. Use hasRuntime()";
+const ERR_CHUNK_INITIAL =
+	"Chunk.initial was removed. Use canBeInitial/isOnlyInitial()";
 
 /** @typedef {import("./Module.js")} Module */
 /** @typedef {import("./ChunkGroup")} ChunkGroup */
@@ -130,6 +133,40 @@ class Chunk {
 		this.removedModules = undefined;
 	}
 
+	/**
+	 * @deprecated Chunk.entry has been deprecated. Please use .hasRuntime() instead
+	 * @returns {never} Throws an error trying to access this property
+	 */
+	get entry() {
+		throw new Error(ERR_CHUNK_ENTRY);
+	}
+
+	/**
+	 * @deprecated .entry has been deprecated. Please use .hasRuntime() instead
+	 * @param {never} data The data that was attempting to be set
+	 * @returns {never} Throws an error trying to access this property
+	 */
+	set entry(data) {
+		throw new Error(ERR_CHUNK_ENTRY);
+	}
+
+	/**
+	 * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
+	 * @returns {never} Throws an error trying to access this property
+	 */
+	get initial() {
+		throw new Error(ERR_CHUNK_INITIAL);
+	}
+
+	/**
+	 * @deprecated Chunk.initial was removed. Use canBeInitial/isOnlyInitial()
+	 * @param {never} data The data attempting to be set
+	 * @returns {never} Throws an error trying to access this property
+	 */
+	set initial(data) {
+		throw new Error(ERR_CHUNK_INITIAL);
+	}
+
 	/**
 	 * @returns {boolean} whether or not the Chunk will have a runtime
 	 */
@@ -659,4 +696,66 @@ class Chunk {
 	}
 }
 
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "forEachModule", {
+	configurable: false,
+	value: util.deprecate(function(fn) {
+		this._modules.forEach(fn);
+	}, "Chunk.forEachModule: Use for(const module of chunk.modulesIterable) instead")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "mapModules", {
+	configurable: false,
+	value: util.deprecate(function(fn) {
+		return Array.from(this._modules, fn);
+	}, "Chunk.mapModules: Use Array.from(chunk.modulesIterable, fn) instead")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "chunks", {
+	configurable: false,
+	get() {
+		throw new Error("Chunk.chunks: Use ChunkGroup.getChildren() instead");
+	},
+	set() {
+		throw new Error("Chunk.chunks: Use ChunkGroup.add/removeChild() instead");
+	}
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "parents", {
+	configurable: false,
+	get() {
+		throw new Error("Chunk.parents: Use ChunkGroup.getParents() instead");
+	},
+	set() {
+		throw new Error("Chunk.parents: Use ChunkGroup.add/removeParent() instead");
+	}
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "blocks", {
+	configurable: false,
+	get() {
+		throw new Error("Chunk.blocks: Use ChunkGroup.getBlocks() instead");
+	},
+	set() {
+		throw new Error("Chunk.blocks: Use ChunkGroup.add/removeBlock() instead");
+	}
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Chunk.prototype, "entrypoints", {
+	configurable: false,
+	get() {
+		throw new Error(
+			"Chunk.entrypoints: Use Chunks.groupsIterable and filter by instanceof Entrypoint instead"
+		);
+	},
+	set() {
+		throw new Error("Chunk.entrypoints: Use Chunks.addGroup instead");
+	}
+});
+
 module.exports = Chunk;
diff --git a/lib/ChunkTemplate.js b/lib/ChunkTemplate.js
index 361e51c4c..c22586f1f 100644
--- a/lib/ChunkTemplate.js
+++ b/lib/ChunkTemplate.js
@@ -4,7 +4,7 @@
 */
 "use strict";
 
-const { SyncWaterfallHook, SyncHook } = require("tapable");
+const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
 
 /** @typedef {import("./ModuleTemplate")} ModuleTemplate */
 /** @typedef {import("./Chunk")} Chunk */
@@ -21,8 +21,9 @@ const { SyncWaterfallHook, SyncHook } = require("tapable");
  * @property {Map<TODO, TODO>} dependencyTemplates
  */
 
-module.exports = class ChunkTemplate {
+module.exports = class ChunkTemplate extends Tapable {
 	constructor(outputOptions) {
+		super();
 		this.outputOptions = outputOptions || {};
 		this.hooks = {
 			renderManifest: new SyncWaterfallHook(["result", "options"]),
diff --git a/lib/Compilation.js b/lib/Compilation.js
index e870f4627..1fd02ed4f 100644
--- a/lib/Compilation.js
+++ b/lib/Compilation.js
@@ -5,8 +5,10 @@
 "use strict";
 
 const asyncLib = require("neo-async");
+const util = require("util");
 const { CachedSource } = require("webpack-sources");
 const {
+	Tapable,
 	SyncHook,
 	SyncBailHook,
 	SyncWaterfallHook,
@@ -93,8 +95,9 @@ function addAllToSet(set, otherSet) {
 	}
 }
 
-class Compilation {
+class Compilation extends Tapable {
 	constructor(compiler) {
+		super();
 		this.hooks = {
 			buildModule: new SyncHook(["module"]),
 			rebuildModule: new SyncHook(["module"]),
@@ -194,7 +197,17 @@ class Compilation {
 			optimizeExtractedChunksAdvanced: new SyncBailHook(["chunks"]),
 			afterOptimizeExtractedChunks: new SyncHook(["chunks"])
 		};
-
+		this._pluginCompat.tap("Compilation", options => {
+			switch (options.name) {
+				case "optimize-tree":
+				case "additional-assets":
+				case "optimize-chunk-assets":
+				case "optimize-assets":
+				case "after-seal":
+					options.async = true;
+					break;
+			}
+		});
 		this.name = undefined;
 		this.compiler = compiler;
 		this.resolverFactory = compiler.resolverFactory;
@@ -1914,4 +1927,22 @@ class Compilation {
 	}
 }
 
+// TODO remove in webpack 5
+Compilation.prototype.applyPlugins = util.deprecate(function(name, ...args) {
+	this.hooks[
+		name.replace(/[- ]([a-z])/g, match => match[1].toUpperCase())
+	].call(...args);
+}, "Compilation.applyPlugins is deprecated. Use new API on `.hooks` instead");
+
+// TODO remove in webpack 5
+Object.defineProperty(Compilation.prototype, "moduleTemplate", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.moduleTemplates.javascript;
+	}, "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead"),
+	set: util.deprecate(function(value) {
+		this.moduleTemplates.javascript = value;
+	}, "Compilation.moduleTemplate: Use Compilation.moduleTemplates.javascript instead.")
+});
+
 module.exports = Compilation;
diff --git a/lib/Compiler.js b/lib/Compiler.js
index bded46a81..11d51471b 100644
--- a/lib/Compiler.js
+++ b/lib/Compiler.js
@@ -7,7 +7,9 @@
 const parseJson = require("json-parse-better-errors");
 const asyncLib = require("neo-async");
 const path = require("path");
+const util = require("util");
 const {
+	Tapable,
 	SyncHook,
 	SyncBailHook,
 	AsyncParallelHook,
@@ -25,8 +27,9 @@ const RequestShortener = require("./RequestShortener");
 const { makePathsRelative } = require("./util/identifier");
 const ConcurrentCompilationError = require("./ConcurrentCompilationError");
 
-class Compiler {
+class Compiler extends Tapable {
 	constructor(context) {
+		super();
 		this.hooks = {
 			shouldEmit: new SyncBailHook(["compilation"]),
 			done: new AsyncSeriesHook(["stats"]),
@@ -56,6 +59,21 @@ class Compiler {
 			afterResolvers: new SyncHook(["compiler"]),
 			entryOption: new SyncBailHook(["context", "entry"])
 		};
+		this._pluginCompat.tap("Compiler", options => {
+			switch (options.name) {
+				case "additional-pass":
+				case "before-run":
+				case "run":
+				case "emit":
+				case "after-emit":
+				case "before-compile":
+				case "make":
+				case "after-compile":
+				case "watch-run":
+					options.async = true;
+					break;
+			}
+		});
 
 		this.name = undefined;
 		this.parentCompilation = undefined;
@@ -72,6 +90,46 @@ class Compiler {
 
 		this.resolverFactory = new ResolverFactory();
 
+		// TODO remove in webpack 5
+		this.resolvers = {
+			normal: {
+				plugins: util.deprecate((hook, fn) => {
+					this.resolverFactory.plugin("resolver normal", resolver => {
+						resolver.plugin(hook, fn);
+					});
+				}, "webpack: Using compiler.resolvers.normal is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver normal", resolver => {\n  resolver.plugin(/* … */);\n}); instead.'),
+				apply: util.deprecate((...args) => {
+					this.resolverFactory.plugin("resolver normal", resolver => {
+						resolver.apply(...args);
+					});
+				}, "webpack: Using compiler.resolvers.normal is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver normal", resolver => {\n  resolver.apply(/* … */);\n}); instead.')
+			},
+			loader: {
+				plugins: util.deprecate((hook, fn) => {
+					this.resolverFactory.plugin("resolver loader", resolver => {
+						resolver.plugin(hook, fn);
+					});
+				}, "webpack: Using compiler.resolvers.loader is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver loader", resolver => {\n  resolver.plugin(/* … */);\n}); instead.'),
+				apply: util.deprecate((...args) => {
+					this.resolverFactory.plugin("resolver loader", resolver => {
+						resolver.apply(...args);
+					});
+				}, "webpack: Using compiler.resolvers.loader is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver loader", resolver => {\n  resolver.apply(/* … */);\n}); instead.')
+			},
+			context: {
+				plugins: util.deprecate((hook, fn) => {
+					this.resolverFactory.plugin("resolver context", resolver => {
+						resolver.plugin(hook, fn);
+					});
+				}, "webpack: Using compiler.resolvers.context is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver context", resolver => {\n  resolver.plugin(/* … */);\n}); instead.'),
+				apply: util.deprecate((...args) => {
+					this.resolverFactory.plugin("resolver context", resolver => {
+						resolver.apply(...args);
+					});
+				}, "webpack: Using compiler.resolvers.context is deprecated.\n" + 'Use compiler.resolverFactory.plugin("resolver context", resolver => {\n  resolver.apply(/* … */);\n}); instead.')
+			}
+		};
+
 		this.options = {};
 
 		this.context = context;
diff --git a/lib/ContextModule.js b/lib/ContextModule.js
index 0ea336138..175187a69 100644
--- a/lib/ContextModule.js
+++ b/lib/ContextModule.js
@@ -3,8 +3,8 @@
 	Author Tobias Koppers @sokra
 */
 "use strict";
-
 const path = require("path");
+const util = require("util");
 const { OriginalSource, RawSource } = require("webpack-sources");
 const Module = require("./Module");
 const AsyncDependenciesBlock = require("./AsyncDependenciesBlock");
@@ -693,4 +693,59 @@ webpackEmptyAsyncContext.id = ${JSON.stringify(id)};`;
 	}
 }
 
+// TODO remove in webpack 5
+Object.defineProperty(ContextModule.prototype, "recursive", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.options.recursive;
+	}, "ContextModule.recursive has been moved to ContextModule.options.recursive"),
+	set: util.deprecate(function(value) {
+		this.options.recursive = value;
+	}, "ContextModule.recursive has been moved to ContextModule.options.recursive")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(ContextModule.prototype, "regExp", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.options.regExp;
+	}, "ContextModule.regExp has been moved to ContextModule.options.regExp"),
+	set: util.deprecate(function(value) {
+		this.options.regExp = value;
+	}, "ContextModule.regExp has been moved to ContextModule.options.regExp")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(ContextModule.prototype, "addon", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.options.addon;
+	}, "ContextModule.addon has been moved to ContextModule.options.addon"),
+	set: util.deprecate(function(value) {
+		this.options.addon = value;
+	}, "ContextModule.addon has been moved to ContextModule.options.addon")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(ContextModule.prototype, "async", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.options.mode;
+	}, "ContextModule.async has been moved to ContextModule.options.mode"),
+	set: util.deprecate(function(value) {
+		this.options.mode = value;
+	}, "ContextModule.async has been moved to ContextModule.options.mode")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(ContextModule.prototype, "chunkName", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.options.chunkName;
+	}, "ContextModule.chunkName has been moved to ContextModule.options.chunkName"),
+	set: util.deprecate(function(value) {
+		this.options.chunkName = value;
+	}, "ContextModule.chunkName has been moved to ContextModule.options.chunkName")
+});
+
 module.exports = ContextModule;
diff --git a/lib/ContextModuleFactory.js b/lib/ContextModuleFactory.js
index 562ddf31f..2a52a58f0 100644
--- a/lib/ContextModuleFactory.js
+++ b/lib/ContextModuleFactory.js
@@ -7,20 +7,34 @@
 const asyncLib = require("neo-async");
 const path = require("path");
 
-const { AsyncSeriesWaterfallHook, SyncWaterfallHook } = require("tapable");
+const {
+	Tapable,
+	AsyncSeriesWaterfallHook,
+	SyncWaterfallHook
+} = require("tapable");
 const ContextModule = require("./ContextModule");
 const ContextElementDependency = require("./dependencies/ContextElementDependency");
 
 const EMPTY_RESOLVE_OPTIONS = {};
 
-module.exports = class ContextModuleFactory {
+module.exports = class ContextModuleFactory extends Tapable {
 	constructor(resolverFactory) {
+		super();
 		this.hooks = {
 			beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
 			afterResolve: new AsyncSeriesWaterfallHook(["data"]),
 			contextModuleFiles: new SyncWaterfallHook(["files"]),
 			alternatives: new AsyncSeriesWaterfallHook(["modules"])
 		};
+		this._pluginCompat.tap("ContextModuleFactory", options => {
+			switch (options.name) {
+				case "before-resolve":
+				case "after-resolve":
+				case "alternatives":
+					options.async = true;
+					break;
+			}
+		});
 		this.resolverFactory = resolverFactory;
 	}
 
diff --git a/lib/DllModuleFactory.js b/lib/DllModuleFactory.js
index 5ad89a1c5..f5d12ddc4 100644
--- a/lib/DllModuleFactory.js
+++ b/lib/DllModuleFactory.js
@@ -4,10 +4,12 @@
 */
 "use strict";
 
+const { Tapable } = require("tapable");
 const DllModule = require("./DllModule");
 
-class DllModuleFactory {
+class DllModuleFactory extends Tapable {
 	constructor() {
+		super();
 		this.hooks = {};
 	}
 	create(data, callback) {
diff --git a/lib/HotUpdateChunkTemplate.js b/lib/HotUpdateChunkTemplate.js
index 3f3151c2b..92ab8db08 100644
--- a/lib/HotUpdateChunkTemplate.js
+++ b/lib/HotUpdateChunkTemplate.js
@@ -6,10 +6,11 @@
 
 const Template = require("./Template");
 const HotUpdateChunk = require("./HotUpdateChunk");
-const { SyncWaterfallHook, SyncHook } = require("tapable");
+const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
 
-module.exports = class HotUpdateChunkTemplate {
+module.exports = class HotUpdateChunkTemplate extends Tapable {
 	constructor(outputOptions) {
+		super();
 		this.outputOptions = outputOptions || {};
 		this.hooks = {
 			modules: new SyncWaterfallHook([
diff --git a/lib/MainTemplate.js b/lib/MainTemplate.js
index 57750e489..3463546fe 100644
--- a/lib/MainTemplate.js
+++ b/lib/MainTemplate.js
@@ -10,7 +10,12 @@ const {
 	PrefixSource,
 	RawSource
 } = require("webpack-sources");
-const { SyncWaterfallHook, SyncHook, SyncBailHook } = require("tapable");
+const {
+	Tapable,
+	SyncWaterfallHook,
+	SyncHook,
+	SyncBailHook
+} = require("tapable");
 const Template = require("./Template");
 
 /** @typedef {import("webpack-sources").ConcatSource} ConcatSource */
@@ -46,12 +51,13 @@ const Template = require("./Template");
 // __webpack_require__.oe = the uncaught error handler for the webpack runtime
 // __webpack_require__.nc = the script nonce
 
-module.exports = class MainTemplate {
+module.exports = class MainTemplate extends Tapable {
 	/**
 	 *
 	 * @param {TODO=} outputOptions output options for the MainTemplate
 	 */
 	constructor(outputOptions) {
+		super();
 		/** @type {TODO?} */
 		this.outputOptions = outputOptions || {};
 		this.hooks = {
diff --git a/lib/Module.js b/lib/Module.js
index 4bbb8ee9e..4341414ac 100644
--- a/lib/Module.js
+++ b/lib/Module.js
@@ -4,6 +4,8 @@
 */
 "use strict";
 
+const util = require("util");
+
 const DependenciesBlock = require("./DependenciesBlock");
 const ModuleReason = require("./ModuleReason");
 const SortableSet = require("./util/SortableSet");
@@ -320,8 +322,54 @@ class Module extends DependenciesBlock {
 		this.buildInfo = undefined;
 		this.disconnect();
 	}
+
+	get arguments() {
+		throw new Error("Module.arguments was removed, there is no replacement.");
+	}
+
+	set arguments(value) {
+		throw new Error("Module.arguments was removed, there is no replacement.");
+	}
 }
 
+// TODO remove in webpack 5
+Object.defineProperty(Module.prototype, "forEachChunk", {
+	configurable: false,
+	value: util.deprecate(function(fn) {
+		this._chunks.forEach(fn);
+	}, "Module.forEachChunk: Use for(const chunk of module.chunksIterable) instead")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Module.prototype, "mapChunks", {
+	configurable: false,
+	value: util.deprecate(function(fn) {
+		return Array.from(this._chunks, fn);
+	}, "Module.mapChunks: Use Array.from(module.chunksIterable, fn) instead")
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Module.prototype, "entry", {
+	configurable: false,
+	get() {
+		throw new Error("Module.entry was removed. Use Chunk.entryModule");
+	},
+	set() {
+		throw new Error("Module.entry was removed. Use Chunk.entryModule");
+	}
+});
+
+// TODO remove in webpack 5
+Object.defineProperty(Module.prototype, "meta", {
+	configurable: false,
+	get: util.deprecate(function() {
+		return this.buildMeta;
+	}, "Module.meta was renamed to Module.buildMeta"),
+	set: util.deprecate(function(value) {
+		this.buildMeta = value;
+	}, "Module.meta was renamed to Module.buildMeta")
+});
+
 /** @type {function(): string} */
 Module.prototype.identifier = null;
 
diff --git a/lib/ModuleTemplate.js b/lib/ModuleTemplate.js
index 7096f1e7e..06e787ed9 100644
--- a/lib/ModuleTemplate.js
+++ b/lib/ModuleTemplate.js
@@ -4,13 +4,14 @@
 */
 "use strict";
 
-const { SyncWaterfallHook, SyncHook } = require("tapable");
+const { Tapable, SyncWaterfallHook, SyncHook } = require("tapable");
 
 /** @typedef {import("webpack-sources").Source} Source */
 /** @typedef {import("./Module")} Module */
 
-module.exports = class ModuleTemplate {
+module.exports = class ModuleTemplate extends Tapable {
 	constructor(runtimeTemplate, type) {
+		super();
 		this.runtimeTemplate = runtimeTemplate;
 		this.type = type;
 		this.hooks = {
diff --git a/lib/MultiCompiler.js b/lib/MultiCompiler.js
index 5315c8f36..cdc7fb508 100644
--- a/lib/MultiCompiler.js
+++ b/lib/MultiCompiler.js
@@ -4,14 +4,15 @@
 */
 "use strict";
 
-const { SyncHook, MultiHook } = require("tapable");
+const { Tapable, SyncHook, MultiHook } = require("tapable");
 const asyncLib = require("neo-async");
 const MultiWatching = require("./MultiWatching");
 const MultiStats = require("./MultiStats");
 const ConcurrentCompilationError = require("./ConcurrentCompilationError");
 
-module.exports = class MultiCompiler {
+module.exports = class MultiCompiler extends Tapable {
 	constructor(compilers) {
+		super();
 		this.hooks = {
 			done: new SyncHook(["stats"]),
 			invalid: new MultiHook(compilers.map(c => c.hooks.invalid)),
diff --git a/lib/MultiModuleFactory.js b/lib/MultiModuleFactory.js
index 10dd9b8e4..5d29b2056 100644
--- a/lib/MultiModuleFactory.js
+++ b/lib/MultiModuleFactory.js
@@ -4,10 +4,12 @@
 */
 "use strict";
 
+const { Tapable } = require("tapable");
 const MultiModule = require("./MultiModule");
 
-module.exports = class MultiModuleFactory {
+module.exports = class MultiModuleFactory extends Tapable {
 	constructor() {
+		super();
 		this.hooks = {};
 	}
 
diff --git a/lib/NormalModuleFactory.js b/lib/NormalModuleFactory.js
index 4f5bf2602..23ef0ae1d 100644
--- a/lib/NormalModuleFactory.js
+++ b/lib/NormalModuleFactory.js
@@ -7,6 +7,7 @@
 const path = require("path");
 const asyncLib = require("neo-async");
 const {
+	Tapable,
 	AsyncSeriesWaterfallHook,
 	SyncWaterfallHook,
 	SyncBailHook,
@@ -57,8 +58,9 @@ const identToLoaderRequest = resultString => {
 
 const dependencyCache = new WeakMap();
 
-class NormalModuleFactory {
+class NormalModuleFactory extends Tapable {
 	constructor(context, resolverFactory, options) {
+		super();
 		this.hooks = {
 			resolver: new SyncWaterfallHook(["resolver"]),
 			factory: new SyncWaterfallHook(["factory"]),
@@ -75,6 +77,40 @@ class NormalModuleFactory {
 				() => new SyncHook(["generator", "generatorOptions"])
 			)
 		};
+		this._pluginCompat.tap("NormalModuleFactory", options => {
+			switch (options.name) {
+				case "before-resolve":
+				case "after-resolve":
+					options.async = true;
+					break;
+				case "parser":
+					this.hooks.parser
+						.for("javascript/auto")
+						.tap(options.fn.name || "unnamed compat plugin", options.fn);
+					return true;
+			}
+			let match;
+			match = /^parser (.+)$/.exec(options.name);
+			if (match) {
+				this.hooks.parser
+					.for(match[1])
+					.tap(
+						options.fn.name || "unnamed compat plugin",
+						options.fn.bind(this)
+					);
+				return true;
+			}
+			match = /^create-parser (.+)$/.exec(options.name);
+			if (match) {
+				this.hooks.createParser
+					.for(match[1])
+					.tap(
+						options.fn.name || "unnamed compat plugin",
+						options.fn.bind(this)
+					);
+				return true;
+			}
+		});
 		this.resolverFactory = resolverFactory;
 		this.ruleSet = new RuleSet(options.defaultRules.concat(options.rules));
 		this.cachePredicate =
diff --git a/lib/Parser.js b/lib/Parser.js
index 2b563bb4b..515df3877 100644
--- a/lib/Parser.js
+++ b/lib/Parser.js
@@ -7,7 +7,8 @@
 // Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
 
 const acorn = require("acorn-dynamic-import").default;
-const { SyncBailHook, HookMap } = require("tapable");
+const { Tapable, SyncBailHook, HookMap } = require("tapable");
+const util = require("util");
 const vm = require("vm");
 const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
 const StackedSetMap = require("./util/StackedSetMap");
@@ -40,8 +41,9 @@ const EMPTY_COMMENT_OPTIONS = {
 	errors: null
 };
 
-class Parser {
+class Parser extends Tapable {
 	constructor(options, sourceType = "auto") {
+		super();
 		this.hooks = {
 			evaluateTypeof: new HookMap(() => new SyncBailHook(["expression"])),
 			evaluate: new HookMap(() => new SyncBailHook(["expression"])),
@@ -97,6 +99,50 @@ class Parser {
 			expressionConditionalOperator: new SyncBailHook(["expression"]),
 			program: new SyncBailHook(["ast", "comments"])
 		};
+		const HOOK_MAP_COMPAT_CONFIG = {
+			evaluateTypeof: /^evaluate typeof (.+)$/,
+			evaluateIdentifier: /^evaluate Identifier (.+)$/,
+			evaluateDefinedIdentifier: /^evaluate defined Identifier (.+)$/,
+			evaluateCallExpressionMember: /^evaluate CallExpression .(.+)$/,
+			evaluate: /^evaluate (.+)$/,
+			label: /^label (.+)$/,
+			varDeclarationLet: /^var-let (.+)$/,
+			varDeclarationConst: /^var-const (.+)$/,
+			varDeclarationVar: /^var-var (.+)$/,
+			varDeclaration: /^var (.+)$/,
+			canRename: /^can-rename (.+)$/,
+			rename: /^rename (.+)$/,
+			typeof: /^typeof (.+)$/,
+			assigned: /^assigned (.+)$/,
+			assign: /^assign (.+)$/,
+			callAnyMember: /^call (.+)\.\*$/,
+			call: /^call (.+)$/,
+			new: /^new (.+)$/,
+			expressionConditionalOperator: /^expression \?:$/,
+			expressionAnyMember: /^expression (.+)\.\*$/,
+			expression: /^expression (.+)$/
+		};
+		this._pluginCompat.tap("Parser", options => {
+			for (const name of Object.keys(HOOK_MAP_COMPAT_CONFIG)) {
+				const regexp = HOOK_MAP_COMPAT_CONFIG[name];
+				const match = regexp.exec(options.name);
+				if (match) {
+					if (match[1]) {
+						this.hooks[name].tap(
+							match[1],
+							options.fn.name || "unnamed compat plugin",
+							options.fn.bind(this)
+						);
+					} else {
+						this.hooks[name].tap(
+							options.fn.name || "unnamed compat plugin",
+							options.fn.bind(this)
+						);
+					}
+					return true;
+				}
+			}
+		});
 		this.options = options;
 		this.sourceType = sourceType;
 		this.scope = undefined;
@@ -2136,4 +2182,12 @@ class Parser {
 	}
 }
 
+// TODO remove in webpack 5
+Object.defineProperty(Parser.prototype, "getCommentOptions", {
+	configurable: false,
+	value: util.deprecate(function(range) {
+		return this.parseCommentOptions(range).options;
+	}, "Parser.getCommentOptions: Use Parser.parseCommentOptions(range) instead")
+});
+
 module.exports = Parser;
diff --git a/lib/RemovedPluginError.js b/lib/RemovedPluginError.js
new file mode 100644
index 000000000..626c3b4fb
--- /dev/null
+++ b/lib/RemovedPluginError.js
@@ -0,0 +1,11 @@
+"use strict";
+
+const WebpackError = require("./WebpackError");
+
+module.exports = class RemovedPluginError extends WebpackError {
+	constructor(message) {
+		super(message);
+
+		Error.captureStackTrace(this, this.constructor);
+	}
+};
diff --git a/lib/RequireJsStuffPlugin.js b/lib/RequireJsStuffPlugin.js
index 84793c8a9..c9888056a 100644
--- a/lib/RequireJsStuffPlugin.js
+++ b/lib/RequireJsStuffPlugin.js
@@ -20,10 +20,11 @@ module.exports = class RequireJsStuffPlugin {
 				);
 				const handler = (parser, parserOptions) => {
 					if (
-						typeof parserOptions.requireJs !== "undefined" &&
+						typeof parserOptions.requireJs === "undefined" ||
 						!parserOptions.requireJs
-					)
+					) {
 						return;
+					}
 
 					parser.hooks.call
 						.for("require.config")
diff --git a/lib/ResolverFactory.js b/lib/ResolverFactory.js
index b9a039612..47028ee7f 100644
--- a/lib/ResolverFactory.js
+++ b/lib/ResolverFactory.js
@@ -4,17 +4,39 @@
  */
 "use strict";
 
-const { HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
+const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
 const Factory = require("enhanced-resolve").ResolverFactory;
 
-module.exports = class ResolverFactory {
+module.exports = class ResolverFactory extends Tapable {
 	constructor() {
+		super();
 		this.hooks = {
 			resolveOptions: new HookMap(
 				() => new SyncWaterfallHook(["resolveOptions"])
 			),
 			resolver: new HookMap(() => new SyncHook(["resolver", "resolveOptions"]))
 		};
+		this._pluginCompat.tap("ResolverFactory", options => {
+			let match;
+			match = /^resolve-options (.+)$/.exec(options.name);
+			if (match) {
+				this.hooks.resolveOptions.tap(
+					match[1],
+					options.fn.name || "unnamed compat plugin",
+					options.fn
+				);
+				return true;
+			}
+			match = /^resolver (.+)$/.exec(options.name);
+			if (match) {
+				this.hooks.resolver.tap(
+					match[1],
+					options.fn.name || "unnamed compat plugin",
+					options.fn
+				);
+				return true;
+			}
+		});
 		this.cache1 = new WeakMap();
 		this.cache2 = new Map();
 	}
diff --git a/lib/WebpackOptionsDefaulter.js b/lib/WebpackOptionsDefaulter.js
index 0e2d8fbe6..e15d58d79 100644
--- a/lib/WebpackOptionsDefaulter.js
+++ b/lib/WebpackOptionsDefaulter.js
@@ -221,11 +221,11 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
 		});
 		this.set("optimization.splitChunks.minChunks", 1);
 		this.set("optimization.splitChunks.maxAsyncRequests", "make", options => {
-			return isProductionLikeMode(options) ? 6 : Infinity;
+			return isProductionLikeMode(options) ? 5 : Infinity;
 		});
 		this.set("optimization.splitChunks.automaticNameDelimiter", "~");
 		this.set("optimization.splitChunks.maxInitialRequests", "make", options => {
-			return isProductionLikeMode(options) ? 4 : Infinity;
+			return isProductionLikeMode(options) ? 3 : Infinity;
 		});
 		this.set("optimization.splitChunks.name", true);
 		this.set("optimization.splitChunks.cacheGroups", {});
diff --git a/lib/dependencies/ContextDependency.js b/lib/dependencies/ContextDependency.js
index dac077572..10c2fea99 100644
--- a/lib/dependencies/ContextDependency.js
+++ b/lib/dependencies/ContextDependency.js
@@ -50,4 +50,19 @@ class ContextDependency extends Dependency {
 	}
 }
 
+// TODO remove in webpack 5
+Object.defineProperty(ContextDependency.prototype, "async", {
+	configurable: false,
+	get() {
+		throw new Error(
+			"ContextDependency.async was removed. Use ContextDependency.options.mode instead."
+		);
+	},
+	set() {
+		throw new Error(
+			"ContextDependency.async was removed. Pass options.mode to constructor instead"
+		);
+	}
+});
+
 module.exports = ContextDependency;
diff --git a/lib/dependencies/SystemPlugin.js b/lib/dependencies/SystemPlugin.js
index 69f3e8dc0..5caf3d965 100644
--- a/lib/dependencies/SystemPlugin.js
+++ b/lib/dependencies/SystemPlugin.js
@@ -18,12 +18,11 @@ class SystemPlugin {
 			(compilation, { normalModuleFactory }) => {
 				const handler = (parser, parserOptions) => {
 					if (
-						typeof parserOptions.system !== "undefined" &&
+						typeof parserOptions.system === "undefined" ||
 						!parserOptions.system
-					)
+					) {
 						return;
-
-					const shouldWarn = typeof parserOptions.system === "undefined";
+					}
 
 					const setNotSupported = name => {
 						parser.hooks.evaluateTypeof
@@ -82,14 +81,9 @@ class SystemPlugin {
 					});
 
 					parser.hooks.call.for("System.import").tap("SystemPlugin", expr => {
-						if (shouldWarn) {
-							parser.state.module.warnings.push(
-								new SystemImportDeprecationWarning(
-									parser.state.module,
-									expr.loc
-								)
-							);
-						}
+						parser.state.module.warnings.push(
+							new SystemImportDeprecationWarning(parser.state.module, expr.loc)
+						);
 
 						return parser.hooks.importCall.call(expr);
 					});
diff --git a/lib/util/StackedSetMap.js b/lib/util/StackedSetMap.js
index f602146a9..589b68d52 100644
--- a/lib/util/StackedSetMap.js
+++ b/lib/util/StackedSetMap.js
@@ -4,6 +4,8 @@
 */
 "use strict";
 
+const util = require("util");
+
 const TOMBSTONE = {};
 const UNDEFINED_MARKER = {};
 
@@ -115,6 +117,19 @@ class StackedSetMap {
 	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");
+	}
 }
 
+// TODO remove in webpack 5
+StackedSetMap.prototype.push = util.deprecate(function(item) {
+	this.add(item);
+}, "This is no longer an Array: Use add instead.");
+
 module.exports = StackedSetMap;
diff --git a/lib/wasm/WebAssemblyParser.js b/lib/wasm/WebAssemblyParser.js
index 8a57941fd..6c022af85 100644
--- a/lib/wasm/WebAssemblyParser.js
+++ b/lib/wasm/WebAssemblyParser.js
@@ -10,6 +10,7 @@ const {
 	moduleContextFromModuleAST
 } = require("@webassemblyjs/helper-module-context");
 
+const { Tapable } = require("tapable");
 const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
 const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
 
@@ -58,8 +59,9 @@ const decoderOpts = {
 	ignoreCustomNameSection: true
 };
 
-class WebAssemblyParser {
+class WebAssemblyParser extends Tapable {
 	constructor(options) {
+		super();
 		this.hooks = {};
 		this.options = options;
 	}
diff --git a/lib/webpack.js b/lib/webpack.js
index fdafae4e7..facfebf6c 100644
--- a/lib/webpack.js
+++ b/lib/webpack.js
@@ -12,6 +12,7 @@ const WebpackOptionsDefaulter = require("./WebpackOptionsDefaulter");
 const validateSchema = require("./validateSchema");
 const WebpackOptionsValidationError = require("./WebpackOptionsValidationError");
 const webpackOptionsSchema = require("../schemas/WebpackOptions.json");
+const RemovedPluginError = require("./RemovedPluginError");
 const version = require("../package.json").version;
 
 const webpack = (options, callback) => {
@@ -156,3 +157,27 @@ exportPlugins((exports.debug = {}), {
 exportPlugins((exports.util = {}), {
 	createHash: () => require("./util/createHash")
 });
+
+const defineMissingPluginError = (namespace, pluginName, errorMessage) => {
+	Object.defineProperty(namespace, pluginName, {
+		configurable: false,
+		enumerable: true,
+		get() {
+			throw new RemovedPluginError(errorMessage);
+		}
+	});
+};
+
+// TODO remove in webpack 5
+defineMissingPluginError(
+	exports.optimize,
+	"UglifyJsPlugin",
+	"webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead."
+);
+
+// TODO remove in webpack 5
+defineMissingPluginError(
+	exports.optimize,
+	"CommonsChunkPlugin",
+	"webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead."
+);
diff --git a/package.json b/package.json
index dac3277ad..5b6f6dcfa 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
     "file-loader": "^1.1.6",
     "glob": "^7.1.2",
     "husky": "^1.0.0-rc.6",
+    "i18n-webpack-plugin": "^1.0.0",
     "istanbul": "^0.4.5",
     "jade": "^1.11.0",
     "jade-loader": "~0.8.0",
@@ -77,11 +78,11 @@
     "vm-browserify": "~0.0.0",
     "wast-loader": "^1.5.5",
     "webpack-dev-middleware": "^1.9.0",
-    "worker-loader": "^2.0.0",
+    "worker-loader": "^1.1.1",
     "xxhashjs": "^0.2.1"
   },
   "engines": {
-    "node": ">=8.9.0"
+    "node": ">=6.11.5"
   },
   "repository": {
     "type": "git",
diff --git a/yarn.lock b/yarn.lock
index bee3f7cbb..4f12dc3fd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2698,6 +2698,10 @@ husky@^1.0.0-rc.6:
     run-node "^1.0.0"
     slash "^2.0.0"
 
+i18n-webpack-plugin@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/i18n-webpack-plugin/-/i18n-webpack-plugin-1.0.0.tgz#0ca12296ec937a4f94325cd0264d08f4e0549831"
+
 iconv-lite@0.4.19:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
@@ -6814,9 +6818,9 @@ worker-farm@^1.5.2:
   dependencies:
     errno "~0.1.7"
 
-worker-loader@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-2.0.0.tgz#45fda3ef76aca815771a89107399ee4119b430ac"
+worker-loader@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/worker-loader/-/worker-loader-1.1.1.tgz#920d74ddac6816fc635392653ed8b4af1929fd92"
   dependencies:
     loader-utils "^1.0.0"
     schema-utils "^0.4.0"


Test patch:
diff --git a/test/Chunk.unittest.js b/test/Chunk.unittest.js
index af34250c6..dab229ee6 100644
--- a/test/Chunk.unittest.js
+++ b/test/Chunk.unittest.js
@@ -22,6 +22,34 @@ describe("Chunk", () => {
 		expect(ChunkInstance.canBeInitial()).toBe(false);
 	});
 
+	describe("entry", () => {
+		it("returns an error if get entry", () => {
+			expect(() => {
+				ChunkInstance.entry;
+			}).toThrow("Chunk.entry was removed. Use hasRuntime()");
+		});
+
+		it("returns an error if set an entry", () => {
+			expect(() => {
+				ChunkInstance.entry = 10;
+			}).toThrow("Chunk.entry was removed. Use hasRuntime()");
+		});
+	});
+
+	describe("initial", () => {
+		it("returns an error if get initial", () => {
+			expect(() => {
+				ChunkInstance.initial;
+			}).toThrow("Chunk.initial was removed. Use canBeInitial/isOnlyInitial()");
+		});
+
+		it("returns an error if set an initial", () => {
+			expect(() => {
+				ChunkInstance.initial = 10;
+			}).toThrow("Chunk.initial was removed. Use canBeInitial/isOnlyInitial()");
+		});
+	});
+
 	describe("hasRuntime", () => {
 		it("returns false", () => {
 			expect(ChunkInstance.hasRuntime()).toBe(false);
diff --git a/test/RemovedPlugins.unittest.js b/test/RemovedPlugins.unittest.js
new file mode 100644
index 000000000..915ee0e11
--- /dev/null
+++ b/test/RemovedPlugins.unittest.js
@@ -0,0 +1,18 @@
+const webpack = require("../lib/webpack");
+const RemovedPluginError = require("../lib/RemovedPluginError");
+
+describe("removed plugin errors", () => {
+	it("should error when accessing removed plugins", () => {
+		expect(() => webpack.optimize.UglifyJsPlugin).toThrow(RemovedPluginError);
+		expect(
+			() => webpack.optimize.UglifyJsPlugin
+		).toThrowErrorMatchingSnapshot();
+
+		expect(() => webpack.optimize.CommonsChunkPlugin).toThrow(
+			RemovedPluginError
+		);
+		expect(
+			() => webpack.optimize.CommonsChunkPlugin
+		).toThrowErrorMatchingSnapshot();
+	});
+});
diff --git a/test/__snapshots__/RemovedPlugins.unittest.js.snap b/test/__snapshots__/RemovedPlugins.unittest.js.snap
new file mode 100644
index 000000000..ecac2f61a
--- /dev/null
+++ b/test/__snapshots__/RemovedPlugins.unittest.js.snap
@@ -0,0 +1,5 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`removed plugin errors should error when accessing removed plugins 1`] = `"webpack.optimize.UglifyJsPlugin has been removed, please use config.optimization.minimize instead."`;
+
+exports[`removed plugin errors should error when accessing removed plugins 2`] = `"webpack.optimize.CommonsChunkPlugin has been removed, please use config.optimization.splitChunks instead."`;
diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap
index 2c5d8d3dd..1fe3e7358 100644
--- a/test/__snapshots__/StatsTestCases.test.js.snap
+++ b/test/__snapshots__/StatsTestCases.test.js.snap
@@ -330,9 +330,9 @@ Child vendors:
      [6] ./c.js 72 bytes {3} {8} [built]
 Child multiple-vendors:
     Entrypoint main = multiple-vendors/main.js
-    Entrypoint a = multiple-vendors/libs-x.js multiple-vendors/vendors~a~async-a~async-b~b.js multiple-vendors/a~async-a~async-b~async-c~b~c.js multiple-vendors/a.js
-    Entrypoint b = multiple-vendors/libs-x.js multiple-vendors/vendors~a~async-a~async-b~b.js multiple-vendors/a~async-a~async-b~async-c~b~c.js multiple-vendors/b.js
-    Entrypoint c = multiple-vendors/libs-x.js multiple-vendors/vendors~async-c~c.js multiple-vendors/a~async-a~async-b~async-c~b~c.js multiple-vendors/c.js
+    Entrypoint a = multiple-vendors/libs-x.js multiple-vendors/vendors~a~async-a~async-b~b.js multiple-vendors/a.js
+    Entrypoint b = multiple-vendors/libs-x.js multiple-vendors/vendors~a~async-a~async-b~b.js multiple-vendors/b.js
+    Entrypoint c = multiple-vendors/libs-x.js multiple-vendors/vendors~async-c~c.js multiple-vendors/c.js
     chunk    {0} multiple-vendors/libs-x.js (libs-x) 20 bytes <{9}> ={1}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: libs) (name: libs-x)
         > ./a a
         > ./b b
@@ -341,20 +341,17 @@ Child multiple-vendors:
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
      [2] ./node_modules/x.js 20 bytes {0} [built]
-    chunk    {1} multiple-vendors/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c)
-        > ./a a
-        > ./b b
-        > ./c c
+    chunk    {1} multiple-vendors/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c)
         > ./a [8] ./index.js 1:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [1] ./d.js 20 bytes {1} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
     chunk    {2} multiple-vendors/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g)
         > ./g [] 6:0-47
         > ./g [] 6:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
     chunk    {3} multiple-vendors/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b)
         > ./a a
         > ./b b
@@ -383,24 +380,27 @@ Child multiple-vendors:
     chunk    {9} multiple-vendors/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered]
         > ./ main
      [8] ./index.js 147 bytes {9} [built]
-    chunk   {10} multiple-vendors/a.js (a) 156 bytes ={0}= ={1}= ={3}= >{2}< >{5}< [entry] [rendered]
+    chunk   {10} multiple-vendors/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered]
         > ./a a
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
      [6] ./a.js + 1 modules 156 bytes {6} {10} [built]
          | ./a.js 121 bytes [built]
          | ./e.js 20 bytes [built]
-    chunk   {11} multiple-vendors/b.js (b) 92 bytes ={0}= ={1}= ={3}= [entry] [rendered]
+    chunk   {11} multiple-vendors/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered]
         > ./b b
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [4] ./b.js 72 bytes {7} {11} [built]
-    chunk   {12} multiple-vendors/c.js (c) 92 bytes ={0}= ={1}= ={4}= [entry] [rendered]
+    chunk   {12} multiple-vendors/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered]
         > ./c c
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [5] ./c.js 72 bytes {8} {12} [built]
 Child all:
     Entrypoint main = all/main.js
-    Entrypoint a = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~a~async-a~async-b~b.js all/a~async-a~async-b~async-c~b~c.js all/a.js
-    Entrypoint b = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~a~async-a~async-b~b.js all/a~async-a~async-b~async-c~b~c.js all/b.js
-    Entrypoint c = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~async-c~c.js all/a~async-a~async-b~async-c~b~c.js all/c.js
+    Entrypoint a = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~a~async-a~async-b~b.js all/a.js
+    Entrypoint b = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~a~async-a~async-b~b.js all/b.js
+    Entrypoint c = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~async-c~c.js all/c.js
     chunk    {0} all/vendors~a~async-a~async-b~async-c~b~c.js (vendors~a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={1}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~async-c~b~c)
         > ./a a
         > ./b b
@@ -409,20 +409,17 @@ Child all:
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
      [2] ./node_modules/x.js 20 bytes {0} [built]
-    chunk    {1} all/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c)
-        > ./a a
-        > ./b b
-        > ./c c
+    chunk    {1} all/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c)
         > ./a [8] ./index.js 1:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [1] ./d.js 20 bytes {1} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
     chunk    {2} all/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g)
         > ./g [] 6:0-47
         > ./g [] 6:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
     chunk    {3} all/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b)
         > ./a a
         > ./b b
@@ -451,18 +448,21 @@ Child all:
     chunk    {9} all/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered]
         > ./ main
      [8] ./index.js 147 bytes {9} [built]
-    chunk   {10} all/a.js (a) 156 bytes ={0}= ={1}= ={3}= >{2}< >{5}< [entry] [rendered]
+    chunk   {10} all/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered]
         > ./a a
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
      [6] ./a.js + 1 modules 156 bytes {6} {10} [built]
          | ./a.js 121 bytes [built]
          | ./e.js 20 bytes [built]
-    chunk   {11} all/b.js (b) 92 bytes ={0}= ={1}= ={3}= [entry] [rendered]
+    chunk   {11} all/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered]
         > ./b b
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [4] ./b.js 72 bytes {7} {11} [built]
-    chunk   {12} all/c.js (c) 92 bytes ={0}= ={1}= ={4}= [entry] [rendered]
+    chunk   {12} all/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered]
         > ./c c
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [5] ./c.js 72 bytes {8} {12} [built]"
 `;
 
@@ -2245,9 +2245,9 @@ exports[`StatsTestCases should print correct stats for split-chunks 1`] = `
      [6] ./c.js 72 bytes {7} {12} [built]
 Child all-chunks:
     Entrypoint main = default/main.js
-    Entrypoint a = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~a~async-a~async-b~b.js default/a~async-a~async-b~async-c~b~c.js default/a.js
-    Entrypoint b = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~a~async-a~async-b~b.js default/a~async-a~async-b~async-c~b~c.js default/b.js
-    Entrypoint c = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~async-c~c.js default/a~async-a~async-b~async-c~b~c.js default/c.js
+    Entrypoint a = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~a~async-a~async-b~b.js default/a.js
+    Entrypoint b = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~a~async-a~async-b~b.js default/b.js
+    Entrypoint c = default/vendors~a~async-a~async-b~async-c~b~c.js default/vendors~async-c~c.js default/c.js
     chunk    {0} default/vendors~a~async-a~async-b~async-c~b~c.js (vendors~a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={1}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~async-c~b~c)
         > ./a a
         > ./b b
@@ -2256,20 +2256,17 @@ Child all-chunks:
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
      [2] ./node_modules/x.js 20 bytes {0} [built]
-    chunk    {1} default/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c)
-        > ./a a
-        > ./b b
-        > ./c c
+    chunk    {1} default/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c)
         > ./a [8] ./index.js 1:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [1] ./d.js 20 bytes {1} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
     chunk    {2} default/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g)
         > ./g [] 6:0-47
         > ./g [] 6:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
     chunk    {3} default/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b)
         > ./a a
         > ./b b
@@ -2298,18 +2295,21 @@ Child all-chunks:
     chunk    {9} default/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered]
         > ./ main
      [8] ./index.js 147 bytes {9} [built]
-    chunk   {10} default/a.js (a) 156 bytes ={0}= ={1}= ={3}= >{2}< >{5}< [entry] [rendered]
+    chunk   {10} default/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered]
         > ./a a
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
      [6] ./a.js + 1 modules 156 bytes {6} {10} [built]
          | ./a.js 121 bytes [built]
          | ./e.js 20 bytes [built]
-    chunk   {11} default/b.js (b) 92 bytes ={0}= ={1}= ={3}= [entry] [rendered]
+    chunk   {11} default/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered]
         > ./b b
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [4] ./b.js 72 bytes {7} {11} [built]
-    chunk   {12} default/c.js (c) 92 bytes ={0}= ={1}= ={4}= [entry] [rendered]
+    chunk   {12} default/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered]
         > ./c c
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [5] ./c.js 72 bytes {8} {12} [built]
 Child manual:
     Entrypoint main = default/main.js
@@ -2436,8 +2436,8 @@ Child name-too-long:
 Child custom-chunks-filter:
     Entrypoint main = default/main.js
     Entrypoint a = default/a.js
-    Entrypoint b = default/vendors~async-a~async-b~async-c~b~c.js default/vendors~async-a~async-b~b.js default/async-a~async-b~async-c~b~c.js default/b.js
-    Entrypoint c = default/vendors~async-a~async-b~async-c~b~c.js default/vendors~async-c~c.js default/async-a~async-b~async-c~b~c.js default/c.js
+    Entrypoint b = default/vendors~async-a~async-b~async-c~b~c.js default/vendors~async-a~async-b~b.js default/b.js
+    Entrypoint c = default/vendors~async-a~async-b~async-c~b~c.js default/vendors~async-c~c.js default/c.js
     chunk    {0} default/vendors~async-a~async-b~async-c~b~c.js (vendors~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={1}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-a~async-b~async-c~b~c)
         > ./b b
         > ./c c
@@ -2445,19 +2445,17 @@ Child custom-chunks-filter:
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
      [2] ./node_modules/x.js 20 bytes {0} {10} [built]
-    chunk    {1} default/async-a~async-b~async-c~b~c.js (async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c~b~c)
-        > ./b b
-        > ./c c
+    chunk    {1} default/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c)
         > ./a [8] ./index.js 1:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [1] ./d.js 20 bytes {1} {10} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
     chunk    {2} default/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g)
         > ./g [] 6:0-47
         > ./g [] 6:0-47
         > ./b [8] ./index.js 2:0-47
         > ./c [8] ./index.js 3:0-47
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
     chunk    {3} default/vendors~async-a~async-b~b.js (vendors~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-a~async-b~b)
         > ./b b
         > ./a [8] ./index.js 1:0-47
@@ -2487,19 +2485,21 @@ Child custom-chunks-filter:
      [8] ./index.js 147 bytes {9} [built]
     chunk   {10} default/a.js (a) 216 bytes >{2}< >{5}< [entry] [rendered]
         > ./a a
-     [1] ./d.js 20 bytes {1} {10} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
      [2] ./node_modules/x.js 20 bytes {0} {10} [built]
      [3] ./node_modules/y.js 20 bytes {3} {10} [built]
      [6] ./a.js + 1 modules 156 bytes {6} {10} [built]
          | ./a.js 121 bytes [built]
          | ./e.js 20 bytes [built]
-    chunk   {11} default/b.js (b) 92 bytes ={0}= ={1}= ={3}= [entry] [rendered]
+    chunk   {11} default/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered]
         > ./b b
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [4] ./b.js 72 bytes {7} {11} [built]
-    chunk   {12} default/c.js (c) 92 bytes ={0}= ={1}= ={4}= [entry] [rendered]
+    chunk   {12} default/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered]
         > ./c c
-     [0] ./f.js 20 bytes {2} {11} {12} [built]
+     [0] ./d.js 20 bytes {1} {10} {11} {12} [built]
+     [1] ./f.js 20 bytes {2} {11} {12} [built]
      [5] ./c.js 72 bytes {8} {12} [built]
 Child custom-chunks-filter-in-cache-groups:
     Entrypoint main = default/main.js
diff --git a/test/cases/parsing/typeof/index.js b/test/cases/parsing/typeof/index.js
index 93e366595..b978c96d9 100644
--- a/test/cases/parsing/typeof/index.js
+++ b/test/cases/parsing/typeof/index.js
@@ -42,7 +42,6 @@ it("should not parse filtered stuff", function() {
 	if(typeof module === "undefined") module = require("fail");
 	if(typeof module != "object") module = require("fail");
 	if(typeof exports == "undefined") exports = require("fail");
-	if(typeof System !== "object") exports = require("fail");
 	if(typeof require.include !== "function") require.include("fail");
 	if(typeof require.ensure !== "function") require.ensure(["fail"], function(){});
 });
diff --git a/test/cases/parsing/issue-2942/index.js b/test/configCases/parsing/issue-2942/index.js
similarity index 100%
rename from test/cases/parsing/issue-2942/index.js
rename to test/configCases/parsing/issue-2942/index.js
diff --git a/test/cases/parsing/issue-2942/warnings.js b/test/configCases/parsing/issue-2942/warnings.js
similarity index 100%
rename from test/cases/parsing/issue-2942/warnings.js
rename to test/configCases/parsing/issue-2942/warnings.js
diff --git a/test/configCases/parsing/issue-2942/webpack.config.js b/test/configCases/parsing/issue-2942/webpack.config.js
new file mode 100644
index 000000000..0570b6f2f
--- /dev/null
+++ b/test/configCases/parsing/issue-2942/webpack.config.js
@@ -0,0 +1,12 @@
+module.exports = {
+	module: {
+		rules: [
+			{
+				test: /\.js$/,
+				parser: {
+					system: true
+				}
+			}
+		]
+	}
+};
diff --git a/test/cases/parsing/requirejs/file.js b/test/configCases/parsing/requirejs/file.js
similarity index 100%
rename from test/cases/parsing/requirejs/file.js
rename to test/configCases/parsing/requirejs/file.js
diff --git a/test/cases/parsing/requirejs/index.js b/test/configCases/parsing/requirejs/index.js
similarity index 100%
rename from test/cases/parsing/requirejs/index.js
rename to test/configCases/parsing/requirejs/index.js
diff --git a/test/configCases/parsing/requirejs/webpack.config.js b/test/configCases/parsing/requirejs/webpack.config.js
new file mode 100644
index 000000000..44b2c6f35
--- /dev/null
+++ b/test/configCases/parsing/requirejs/webpack.config.js
@@ -0,0 +1,12 @@
+module.exports = {
+	module: {
+		rules: [
+			{
+				test: /\.js$/,
+				parser: {
+					requireJs: true
+				}
+			}
+		]
+	}
+};
diff --git a/test/configCases/parsing/system.import/index.js b/test/configCases/parsing/system.import/index.js
index 754a325d9..651706eff 100644
--- a/test/configCases/parsing/system.import/index.js
+++ b/test/configCases/parsing/system.import/index.js
@@ -1,36 +1,39 @@
 it("should answer typeof System correctly", () => {
-	if(__SYSTEM__ === false) {
-		expect((typeof System)).toBe("undefined");
-	} else {
+	if(__SYSTEM__) {
 		expect((typeof System)).toBe("object");
+	} else {
+		expect((typeof System)).toBe("undefined");
 	}
 });
 
 it("should answer typeof System.import correctly", () => {
-	if(__SYSTEM__ === false) {
+	if(__SYSTEM__) {
+		expect((typeof System.import)).toBe("function");
+	} else {
 		expect(() => {
 			typeof System.import;
 		}).toThrowError();
-	} else {
-		expect((typeof System.import)).toBe("function");
 	}
 });
 
 it("should be able to use System.import()", done => {
 	try {
 		System.import("./module").then(mod => {
-			if(__SYSTEM__ === false) {
-				done(new Error("System.import should not be parsed"));
-			} else {
-				expect(mod).toEqual({ default: "ok", [Symbol.toStringTag]: "Module" });
+			if(__SYSTEM__) {
+				expect(mod).toEqual({
+					default: "ok",
+					[Symbol.toStringTag]: "Module"
+				});
 				done();
+			} else {
+				done(new Error("System.import should not be parsed"));
 			}
 		});
 	} catch(e) {
-		if(__SYSTEM__ === false) {
-			done();
-		} else {
+		if(__SYSTEM__) {
 			done(e);
+		} else {
+			done();
 		}
 	}
 });
diff --git a/test/configCases/parsing/system.import/warnings.js b/test/configCases/parsing/system.import/warnings.js
index 3d4e4ec27..a96ced4c7 100644
--- a/test/configCases/parsing/system.import/warnings.js
+++ b/test/configCases/parsing/system.import/warnings.js
@@ -1,3 +1,3 @@
 module.exports = [
-	[/system_undef/, /System\.import\(\) is deprecated/]
+	[/system_true/, /System\.import\(\) is deprecated/]
 ];