From 77daf83a430c8490ba1d71cf410bc0569a66c332 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 11 Jun 2017 02:34:05 +0200 Subject: [PATCH 01/23] refactor: pass a unique compiler name to get child compilation (#483) This is recommended after https://github.com/webpack/webpack/pull/4704 --- loader.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/loader.js b/loader.js index 06e649e1..3324156a 100644 --- a/loader.js +++ b/loader.js @@ -47,20 +47,12 @@ module.exports.pitch = function(request) { filename: childFilename, publicPath: publicPath }; - var childCompiler = this._compilation.createChildCompiler("extract-text-webpack-plugin", outputOptions); + var childCompiler = this._compilation.createChildCompiler("extract-text-webpack-plugin " + NS + " " + request, outputOptions); childCompiler.apply(new NodeTemplatePlugin(outputOptions)); childCompiler.apply(new LibraryTemplatePlugin(null, "commonjs2")); childCompiler.apply(new NodeTargetPlugin()); childCompiler.apply(new SingleEntryPlugin(this.context, "!!" + request)); childCompiler.apply(new LimitChunkCountPlugin({ maxChunks: 1 })); - var subCache = "subcache " + NS + " " + request; // eslint-disable-line no-path-concat - childCompiler.plugin("compilation", function(compilation) { - if(compilation.cache) { - if(!compilation.cache[subCache]) - compilation.cache[subCache] = {}; - compilation.cache = compilation.cache[subCache]; - } - }); // We set loaderContext[NS] = false to indicate we already in // a child compiler so we don't spawn another child compilers from there. childCompiler.plugin("this-compilation", function(compilation) { From 92e4349bef1db73692ce279ccc70dfa31e0ebfdf Mon Sep 17 00:00:00 2001 From: Joshua Wiens Date: Sat, 10 Jun 2017 19:44:32 -0500 Subject: [PATCH 02/23] chore(package): Update peerDeps & webpack devDep --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6c0d831e..442d6cf9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "OrderUndefinedError.js" ], "peerDependencies": { - "webpack": "^2.2.0" + "webpack": ">= 3.0.0-rc.0 || ^3.0.0" }, "dependencies": { "schema-utils": "^0.3.0", @@ -35,7 +35,7 @@ "should": "^11.1.2", "standard-version": "^4.1.0", "style-loader": "^0.18.2", - "webpack": "^2.6.1" + "webpack": "^3.0.0-rc.0" }, "homepage": "http://github.com/webpack-contrib/extract-text-webpack-plugin", "repository": { From cdfccb49d9b865408600ec9eec66a9e8ae98208a Mon Sep 17 00:00:00 2001 From: David Heidrich Date: Tue, 20 Jun 2017 22:15:22 +0200 Subject: [PATCH 03/23] Squashed commit of the following: commit e2631af23487a234af08c546c10e26d2a3accf27 Author: David Heidrich Date: Mon Jun 19 19:08:13 2017 +0200 removed dead code, cleanup commit d8943d71732881a5a3f65a496d4652666dcdb3ed Author: David Heidrich Date: Mon Jun 19 15:48:51 2017 +0200 restored console log commit 0d7c94a35c046fb7f24b5ca10757e1f5fe5dccd3 Author: David Heidrich Date: Mon Jun 19 00:30:34 2017 +0200 fixed async loading (keep original style loader in async chunks) commit 950d7ef042f805e5a8781eb074e026e15bb3785c Author: David Heidrich Date: Sun Jun 18 15:52:41 2017 +0200 fixed exporting module informations commit 8b97ee5c6b11430f5db44badd8207c3d3c57042b Author: David Heidrich Date: Sun Jun 18 14:27:51 2017 +0200 fixed line length commit 6898aa4ee0d51493097991414354e38520a2b09f Author: David Heidrich Date: Sun Jun 18 14:26:34 2017 +0200 respect all chunks commit bbc93ab566cf444982fd61c8eaf32f28795474e9 Author: David Heidrich Date: Sun Jun 18 12:33:09 2017 +0200 adjusted test to be env agnostic commit 3eba62730f0b50c2276fb8d28feb5cc238ba7e45 Author: David Heidrich Date: Sun Jun 18 12:25:16 2017 +0200 adjusted tests to use css-loader, added tests for allChunks, remove module from initial chunk commit 09ac8cba2f03e3dc73af6e11351b52a74678b269 Author: David Heidrich Date: Sat Jun 17 22:36:09 2017 +0200 adjusted tests, remove module from base if extracted commit 17917967b68ace3587d75ca045ceeb3fb183ecaa Author: David Heidrich Date: Sat Jun 17 21:23:58 2017 +0200 added testcase for issue #120 --- index.js | 82 +++++++++++++++---- package.json | 1 + test/TestCases.test.js | 62 +++++++------- .../default-styles.css | 3 + .../entries/contact.js | 2 + .../entries/homepage.js | 2 + .../multiple-entries-all-async/expected/0.js | 22 +++++ .../multiple-entries-all-async/expected/1.js | 22 +++++ .../expected/contact.css | 9 ++ .../expected/homepage.css | 9 ++ .../multiple-entries-all-async/router.js | 6 ++ .../routes/contact/index.js | 5 ++ .../routes/contact/styles.css | 3 + .../routes/homepage/index.js | 5 ++ .../routes/homepage/styles.css | 3 + .../webpack.config.js | 23 ++++++ .../multiple-entries-async/default-styles.css | 3 + .../multiple-entries-async/entries/contact.js | 2 + .../entries/homepage.js | 2 + .../multiple-entries-async/expected/0.js | 62 ++++++++++++++ .../multiple-entries-async/expected/1.js | 62 ++++++++++++++ .../expected/contact.css | 6 ++ .../expected/homepage.css | 6 ++ test/cases/multiple-entries-async/router.js | 6 ++ .../routes/contact/index.js | 5 ++ .../routes/contact/styles.css | 3 + .../routes/homepage/index.js | 5 ++ .../routes/homepage/styles.css | 3 + .../multiple-entries-async/webpack.config.js | 23 ++++++ 29 files changed, 402 insertions(+), 45 deletions(-) create mode 100644 test/cases/multiple-entries-all-async/default-styles.css create mode 100644 test/cases/multiple-entries-all-async/entries/contact.js create mode 100644 test/cases/multiple-entries-all-async/entries/homepage.js create mode 100644 test/cases/multiple-entries-all-async/expected/0.js create mode 100644 test/cases/multiple-entries-all-async/expected/1.js create mode 100644 test/cases/multiple-entries-all-async/expected/contact.css create mode 100644 test/cases/multiple-entries-all-async/expected/homepage.css create mode 100644 test/cases/multiple-entries-all-async/router.js create mode 100644 test/cases/multiple-entries-all-async/routes/contact/index.js create mode 100644 test/cases/multiple-entries-all-async/routes/contact/styles.css create mode 100644 test/cases/multiple-entries-all-async/routes/homepage/index.js create mode 100644 test/cases/multiple-entries-all-async/routes/homepage/styles.css create mode 100644 test/cases/multiple-entries-all-async/webpack.config.js create mode 100644 test/cases/multiple-entries-async/default-styles.css create mode 100644 test/cases/multiple-entries-async/entries/contact.js create mode 100644 test/cases/multiple-entries-async/entries/homepage.js create mode 100644 test/cases/multiple-entries-async/expected/0.js create mode 100644 test/cases/multiple-entries-async/expected/1.js create mode 100644 test/cases/multiple-entries-async/expected/contact.css create mode 100644 test/cases/multiple-entries-async/expected/homepage.css create mode 100644 test/cases/multiple-entries-async/router.js create mode 100644 test/cases/multiple-entries-async/routes/contact/index.js create mode 100644 test/cases/multiple-entries-async/routes/contact/styles.css create mode 100644 test/cases/multiple-entries-async/routes/homepage/index.js create mode 100644 test/cases/multiple-entries-async/routes/homepage/styles.css create mode 100644 test/cases/multiple-entries-async/webpack.config.js diff --git a/index.js b/index.js index ed5b11f4..1e096b45 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ var ConcatSource = require("webpack-sources").ConcatSource; var async = require("async"); var ExtractedModule = require("./ExtractedModule"); var Chunk = require("webpack/lib/Chunk"); +var NormalModule = require("webpack/lib/NormalModule"); var OrderUndefinedError = require("./OrderUndefinedError"); var loaderUtils = require("loader-utils"); var validateOptions = require('schema-utils'); @@ -186,15 +187,15 @@ ExtractTextPlugin.prototype.loader = function(options) { ExtractTextPlugin.prototype.extract = function(options) { if(arguments.length > 1) { throw new Error("Breaking change: extract now only takes a single argument. Either an options " + - "object *or* the loader(s).\n" + - "Example: if your old code looked like this:\n" + - " ExtractTextPlugin.extract('style-loader', 'css-loader')\n\n" + - "You would change it to:\n" + - " ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' })\n\n" + - "The available options are:\n" + - " use: string | object | loader[]\n" + - " fallback: string | object | loader[]\n" + - " publicPath: string\n"); + "object *or* the loader(s).\n" + + "Example: if your old code looked like this:\n" + + " ExtractTextPlugin.extract('style-loader', 'css-loader')\n\n" + + "You would change it to:\n" + + " ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' })\n\n" + + "The available options are:\n" + + " use: string | object | loader[]\n" + + " fallback: string | object | loader[]\n" + + " publicPath: string\n"); } if(options.fallbackLoader) { console.warn('fallbackLoader option has been deprecated - replace with "fallback"'); @@ -225,7 +226,7 @@ ExtractTextPlugin.prototype.extract = function(options) { return [this.loader(options)] .concat(before, loader) .map(getLoaderObject); -} +}; ExtractTextPlugin.extract = ExtractTextPlugin.prototype.extract.bind(ExtractTextPlugin); @@ -233,6 +234,7 @@ ExtractTextPlugin.prototype.apply = function(compiler) { var options = this.options; compiler.plugin("this-compilation", function(compilation) { var extractCompilation = new ExtractTextPluginCompilation(); + var toRemoveModules = {}; compilation.plugin("normal-module-loader", function(loaderContext, module) { loaderContext[NS] = function(content, opt) { if(options.disable) @@ -274,21 +276,43 @@ ExtractTextPlugin.prototype.apply = function(compiler) { if(meta && (!meta.options.id || meta.options.id === id)) { var wasExtracted = Array.isArray(meta.content); if(shouldExtract !== wasExtracted) { - module[NS + "/extract"] = shouldExtract; // eslint-disable-line no-path-concat - compilation.rebuildModule(module, function(err) { + var newModule = new NormalModule( + module.request, + module.userRequest, + module.rawRequest, + module.loaders, + module.resource, + module.parser + ); + newModule[NS + "/extract"] = shouldExtract; // eslint-disable-line no-path-concat + // build a new module and save result to extracted compilations + compilation.buildModule(newModule, false, newModule, null, function(err) { if(err) { compilation.errors.push(err); return callback(); } - meta = module[NS]; + meta = newModule[NS]; // Error out if content is not an array and is not null if(!Array.isArray(meta.content) && meta.content != null) { - err = new Error(module.identifier() + " doesn't export content"); + err = new Error(newModule.identifier() + " doesn't export content"); compilation.errors.push(err); return callback(); } - if(meta.content) - extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); + if(meta.content) { + var ident = module.identifier(); + extractCompilation.addResultToChunk(ident, meta.content, module, extractedChunk); + // remove generated result from chunk + if(toRemoveModules[ident]) { + toRemoveModules[ident].chunks.push(chunk) + } else { + toRemoveModules[ident] = { + module: newModule, + moduleToRemove: module, + chunks: [chunk] + }; + } + + } callback(); }); } else { @@ -318,6 +342,32 @@ ExtractTextPlugin.prototype.apply = function(compiler) { callback(); }.bind(this)); }.bind(this)); + compilation.plugin("optimize-module-ids", function(modules){ + modules.forEach(function (module) { + var data = toRemoveModules[module.identifier()]; + if (data) { + var id = module.id; + var newModule = new NormalModule( + module.request, + module.userRequest, + module.rawRequest, + module.loaders, + module.resource, + module.parser + ); + newModule.id = id; + newModule._source = data.module._source; + data.chunks.forEach(function (chunk) { + chunk.removeModule(data.moduleToRemove); + var deps = data.moduleToRemove.dependencies; + deps.forEach(d => { + chunk.removeModule(d.module); + }); + chunk.addModule(newModule); + }); + } + }); + }); compilation.plugin("additional-assets", function(callback) { extractedChunks.forEach(function(extractedChunk) { if(extractedChunk.modules.length) { diff --git a/package.json b/package.json index 6c0d831e..9e875449 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "mocha": "^3.2.0", "mocha-lcov-reporter": "1.3.0", "raw-loader": "^0.5.1", + "rimraf": "^2.6.1", "should": "^11.1.2", "standard-version": "^4.1.0", "style-loader": "^0.18.2", diff --git a/test/TestCases.test.js b/test/TestCases.test.js index bbcb3712..415d1094 100644 --- a/test/TestCases.test.js +++ b/test/TestCases.test.js @@ -3,6 +3,7 @@ var vm = require("vm"); var path = require("path"); var webpack = require("webpack"); var should = require("should"); +var rimraf = require('rimraf'); var ExtractTextPlugin = require("../"); var cases = process.env.CASES ? process.env.CASES.split(",") : fs.readdirSync(path.join(__dirname, "cases")); @@ -12,36 +13,39 @@ describe("TestCases", function() { it(testCase, function(done) { var testDirectory = path.join(__dirname, "cases", testCase); var outputDirectory = path.join(__dirname, "js", testCase); - var options = { entry: { test: "./index.js" } }; - var configFile = path.join(testDirectory, "webpack.config.js"); - if(fs.existsSync(configFile)) - options = require(configFile); - options.context = testDirectory; - if(!options.module) options.module = {}; - if(!options.module.loaders) options.module.loaders = [ - { test: /\.txt$/, loader: ExtractTextPlugin.extract("raw-loader") } - ]; - if(!options.output) options.output = { filename: "[name].js" }; - if(!options.output.path) options.output.path = outputDirectory; - if(process.env.CASES) { - console.log("\nwebpack." + testCase + ".config.js " + JSON.stringify(options, null, 2)); - } - webpack(options, function(err, stats) { - if(err) return done(err); - if(stats.hasErrors()) return done(new Error(stats.toString())); - var testFile = path.join(outputDirectory, "test.js"); - if(fs.existsSync(testFile)) - require(testFile)(suite); - var expectedDirectory = path.join(testDirectory, "expected"); - fs.readdirSync(expectedDirectory).forEach(function(file) { - var filePath = path.join(expectedDirectory, file); - var actualPath = path.join(outputDirectory, file); - readFileOrEmpty(actualPath).should.be.eql( - readFileOrEmpty(filePath), - file + " should be correct"); + function test() { + var options = { entry: { test: "./index.js" } }; + var configFile = path.join(testDirectory, "webpack.config.js"); + if (fs.existsSync(configFile)) + options = require(configFile); + options.context = testDirectory; + if (!options.module) options.module = {}; + if (!options.module.loaders) options.module.loaders = [ + { test: /\.txt$/, loader: ExtractTextPlugin.extract("raw-loader") } + ]; + if (!options.output) options.output = { filename: "[name].js" }; + if (!options.output.path) options.output.path = outputDirectory; + if (process.env.CASES) { + console.log("\nwebpack." + testCase + ".config.js " + JSON.stringify(options, null, 2)); + } + webpack(options, function (err, stats) { + if (err) return done(err); + if (stats.hasErrors()) return done(new Error(stats.toString())); + var testFile = path.join(outputDirectory, "test.js"); + if (fs.existsSync(testFile)) + require(testFile)(suite); + var expectedDirectory = path.join(testDirectory, "expected"); + fs.readdirSync(expectedDirectory).forEach(function (file) { + var filePath = path.join(expectedDirectory, file); + var actualPath = path.join(outputDirectory, file); + readFileOrEmpty(actualPath).should.be.eql( + readFileOrEmpty(filePath), + file + " should be correct"); + }); + done(); }); - done(); - }); + } + rimraf(outputDirectory, test); }); }); }); diff --git a/test/cases/multiple-entries-all-async/default-styles.css b/test/cases/multiple-entries-all-async/default-styles.css new file mode 100644 index 00000000..f0d5b13b --- /dev/null +++ b/test/cases/multiple-entries-all-async/default-styles.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/cases/multiple-entries-all-async/entries/contact.js b/test/cases/multiple-entries-all-async/entries/contact.js new file mode 100644 index 00000000..66b458c2 --- /dev/null +++ b/test/cases/multiple-entries-all-async/entries/contact.js @@ -0,0 +1,2 @@ +require('../router'); +require('../routes/contact'); diff --git a/test/cases/multiple-entries-all-async/entries/homepage.js b/test/cases/multiple-entries-all-async/entries/homepage.js new file mode 100644 index 00000000..7a8e5aea --- /dev/null +++ b/test/cases/multiple-entries-all-async/entries/homepage.js @@ -0,0 +1,2 @@ +require('../router'); +require('../routes/homepage'); diff --git a/test/cases/multiple-entries-all-async/expected/0.js b/test/cases/multiple-entries-all-async/expected/0.js new file mode 100644 index 00000000..88d37a36 --- /dev/null +++ b/test/cases/multiple-entries-all-async/expected/0.js @@ -0,0 +1,22 @@ +webpackJsonp([0],{ + +/***/ 2: +/***/ (function(module, exports, __webpack_require__) { + +__webpack_require__(7); + +modules.export = function() { + return 'Route Homepage'; +}; + + +/***/ }), + +/***/ 7: +/***/ (function(module, exports) { + +// removed by extract-text-webpack-plugin + +/***/ }) + +}); \ No newline at end of file diff --git a/test/cases/multiple-entries-all-async/expected/1.js b/test/cases/multiple-entries-all-async/expected/1.js new file mode 100644 index 00000000..72666204 --- /dev/null +++ b/test/cases/multiple-entries-all-async/expected/1.js @@ -0,0 +1,22 @@ +webpackJsonp([1],{ + +/***/ 1: +/***/ (function(module, exports, __webpack_require__) { + +__webpack_require__(6); + +modules.export = function() { + return 'Route Contact'; +}; + + +/***/ }), + +/***/ 6: +/***/ (function(module, exports) { + +// removed by extract-text-webpack-plugin + +/***/ }) + +}); \ No newline at end of file diff --git a/test/cases/multiple-entries-all-async/expected/contact.css b/test/cases/multiple-entries-all-async/expected/contact.css new file mode 100644 index 00000000..c952ccfd --- /dev/null +++ b/test/cases/multiple-entries-all-async/expected/contact.css @@ -0,0 +1,9 @@ +body { + background: red; +} +.contact { + color: black; +} +.homepage { + color: black; +} diff --git a/test/cases/multiple-entries-all-async/expected/homepage.css b/test/cases/multiple-entries-all-async/expected/homepage.css new file mode 100644 index 00000000..c952ccfd --- /dev/null +++ b/test/cases/multiple-entries-all-async/expected/homepage.css @@ -0,0 +1,9 @@ +body { + background: red; +} +.contact { + color: black; +} +.homepage { + color: black; +} diff --git a/test/cases/multiple-entries-all-async/router.js b/test/cases/multiple-entries-all-async/router.js new file mode 100644 index 00000000..d9855317 --- /dev/null +++ b/test/cases/multiple-entries-all-async/router.js @@ -0,0 +1,6 @@ +require('./default-styles.css'); +module.export = function (route) { + return import(/* webpackChunkName: "route-[request]" */ './routes/' + route + 'index.js').then(function (route) { + return route; + }); +}; diff --git a/test/cases/multiple-entries-all-async/routes/contact/index.js b/test/cases/multiple-entries-all-async/routes/contact/index.js new file mode 100644 index 00000000..c16daacb --- /dev/null +++ b/test/cases/multiple-entries-all-async/routes/contact/index.js @@ -0,0 +1,5 @@ +require('./styles.css'); + +modules.export = function() { + return 'Route Contact'; +}; diff --git a/test/cases/multiple-entries-all-async/routes/contact/styles.css b/test/cases/multiple-entries-all-async/routes/contact/styles.css new file mode 100644 index 00000000..fd8a667d --- /dev/null +++ b/test/cases/multiple-entries-all-async/routes/contact/styles.css @@ -0,0 +1,3 @@ +.contact { + color: black; +} diff --git a/test/cases/multiple-entries-all-async/routes/homepage/index.js b/test/cases/multiple-entries-all-async/routes/homepage/index.js new file mode 100644 index 00000000..bb86d26e --- /dev/null +++ b/test/cases/multiple-entries-all-async/routes/homepage/index.js @@ -0,0 +1,5 @@ +require('./styles.css'); + +modules.export = function() { + return 'Route Homepage'; +}; diff --git a/test/cases/multiple-entries-all-async/routes/homepage/styles.css b/test/cases/multiple-entries-all-async/routes/homepage/styles.css new file mode 100644 index 00000000..1fc1fc5a --- /dev/null +++ b/test/cases/multiple-entries-all-async/routes/homepage/styles.css @@ -0,0 +1,3 @@ +.homepage { + color: black; +} diff --git a/test/cases/multiple-entries-all-async/webpack.config.js b/test/cases/multiple-entries-all-async/webpack.config.js new file mode 100644 index 00000000..d94f0093 --- /dev/null +++ b/test/cases/multiple-entries-all-async/webpack.config.js @@ -0,0 +1,23 @@ +var ExtractTextPlugin = require("../../../"); +module.exports = { + entry: { + 'homepage': "./entries/homepage.js", + 'contact': "./entries/contact.js" + }, + module: { + loaders: [ + { test: /\.css$/, use: ExtractTextPlugin.extract({ + fallback: "style-loader", + use: { loader: "css-loader", options: { + sourceMap: false + } } + }) } + ] + }, + plugins: [ + new ExtractTextPlugin({ + filename: '[name].css', + allChunks: true + }) + ] +}; diff --git a/test/cases/multiple-entries-async/default-styles.css b/test/cases/multiple-entries-async/default-styles.css new file mode 100644 index 00000000..f0d5b13b --- /dev/null +++ b/test/cases/multiple-entries-async/default-styles.css @@ -0,0 +1,3 @@ +body { + background: red; +} diff --git a/test/cases/multiple-entries-async/entries/contact.js b/test/cases/multiple-entries-async/entries/contact.js new file mode 100644 index 00000000..66b458c2 --- /dev/null +++ b/test/cases/multiple-entries-async/entries/contact.js @@ -0,0 +1,2 @@ +require('../router'); +require('../routes/contact'); diff --git a/test/cases/multiple-entries-async/entries/homepage.js b/test/cases/multiple-entries-async/entries/homepage.js new file mode 100644 index 00000000..7a8e5aea --- /dev/null +++ b/test/cases/multiple-entries-async/entries/homepage.js @@ -0,0 +1,2 @@ +require('../router'); +require('../routes/homepage'); diff --git a/test/cases/multiple-entries-async/expected/0.js b/test/cases/multiple-entries-async/expected/0.js new file mode 100644 index 00000000..e768406d --- /dev/null +++ b/test/cases/multiple-entries-async/expected/0.js @@ -0,0 +1,62 @@ +webpackJsonp([0],{ + +/***/ 11: +/***/ (function(module, exports, __webpack_require__) { + +// style-loader: Adds some css to the DOM by adding a