From 4ba47032bb609d4be7adb73dfbdf07b7c40df860 Mon Sep 17 00:00:00 2001 From: Steven Hargrove Date: Thu, 6 Jul 2017 15:24:37 -0400 Subject: [PATCH 1/4] corrected order for chunk.modules loop --- index.js | 350 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 index.js diff --git a/index.js b/index.js new file mode 100644 index 00000000..4dd74015 --- /dev/null +++ b/index.js @@ -0,0 +1,350 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +var fs = require('fs'); +var ConcatSource = require("webpack-sources").ConcatSource; +var async = require("async"); +var ExtractedModule = require("./ExtractedModule"); +var Chunk = require("webpack/lib/Chunk"); +var OrderUndefinedError = require("./OrderUndefinedError"); +var loaderUtils = require("loader-utils"); +var validateOptions = require('schema-utils'); +var path = require('path'); + +var NS = fs.realpathSync(__dirname); + +var nextId = 0; + +function ExtractTextPluginCompilation() { + this.modulesByIdentifier = {}; +} + +function isInitialOrHasNoParents(chunk) { + return chunk.isInitial() || chunk.parents.length === 0; +} + +ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) { + if(!intoChunk) { + checkedChunks = []; + chunk.chunks.forEach(function(c) { + if(isInitialOrHasNoParents(c)) return; + this.mergeNonInitialChunks(c, chunk, checkedChunks); + }, this); + } else if(checkedChunks.indexOf(chunk) < 0) { + checkedChunks.push(chunk); + chunk.modules.slice().forEach(function(module) { + intoChunk.addModule(module); + module.addChunk(intoChunk); + }); + chunk.chunks.forEach(function(c) { + if(isInitialOrHasNoParents(c)) return; + this.mergeNonInitialChunks(c, intoChunk, checkedChunks); + }, this); + } +}; + +ExtractTextPluginCompilation.prototype.addModule = function(identifier, originalModule, source, additionalInformation, sourceMap, prevModules) { + var m; + if(!this.modulesByIdentifier[identifier]) { + m = this.modulesByIdentifier[identifier] = new ExtractedModule(identifier, originalModule, source, sourceMap, additionalInformation, prevModules); + } else { + m = this.modulesByIdentifier[identifier]; + m.addPrevModules(prevModules); + if(originalModule.index2 < m.getOriginalModule().index2) { + m.setOriginalModule(originalModule); + } + } + return m; +}; + +ExtractTextPluginCompilation.prototype.addResultToChunk = function(identifier, result, originalModule, extractedChunk) { + if(!Array.isArray(result)) { + result = [[identifier, result]]; + } + var counterMap = {}; + var prevModules = []; + result.forEach(function(item) { + var c = counterMap[item[0]]; + var module = this.addModule.call(this, item[0] + (c || ""), originalModule, item[1], item[2], item[3], prevModules.slice()); + extractedChunk.addModule(module); + module.addChunk(extractedChunk); + counterMap[item[0]] = (c || 0) + 1; + prevModules.push(module); + }, this); +}; + +ExtractTextPlugin.prototype.renderExtractedChunk = function(chunk) { + var source = new ConcatSource(); + chunk.modules.forEach(function(module) { + var moduleSource = module.source(); + source.add(this.applyAdditionalInformation(moduleSource, module.additionalInformation)); + }, this); + return source; +}; + +function isInvalidOrder(a, b) { + var bBeforeA = a.getPrevModules().indexOf(b) >= 0; + var aBeforeB = b.getPrevModules().indexOf(a) >= 0; + return aBeforeB && bBeforeA; +} + +function getOrder(a, b) { + var aOrder = a.getOrder(); + var bOrder = b.getOrder(); + if(aOrder < bOrder) return -1; + if(aOrder > bOrder) return 1; + var aIndex = a.getOriginalModule().index2; + var bIndex = b.getOriginalModule().index2; + if(aIndex < bIndex) return -1; + if(aIndex > bIndex) return 1; + var bBeforeA = a.getPrevModules().indexOf(b) >= 0; + var aBeforeB = b.getPrevModules().indexOf(a) >= 0; + if(aBeforeB && !bBeforeA) return -1; + if(!aBeforeB && bBeforeA) return 1; + var ai = a.identifier(); + var bi = b.identifier(); + if(ai < bi) return -1; + if(ai > bi) return 1; + return 0; +} + +function ExtractTextPlugin(options) { + if(arguments.length > 1) { + throw new Error("Breaking change: ExtractTextPlugin now only takes a single argument. Either an options " + + "object *or* the name of the result file.\n" + + "Example: if your old code looked like this:\n" + + " new ExtractTextPlugin('css/[name].css', { disable: false, allChunks: true })\n\n" + + "You would change it to:\n" + + " new ExtractTextPlugin({ filename: 'css/[name].css', disable: false, allChunks: true })\n\n" + + "The available options are:\n" + + " filename: string\n" + + " allChunks: boolean\n" + + " disable: boolean\n" + + " ignoreOrder: boolean\n"); + } + if(isString(options)) { + options = { filename: options }; + } else { + validateOptions(path.resolve(__dirname, './schema/plugin.json'), options, 'Extract Text Plugin'); + } + this.filename = options.filename; + this.id = options.id != null ? options.id : ++nextId; + this.options = {}; + mergeOptions(this.options, options); + delete this.options.filename; + delete this.options.id; +} +module.exports = ExtractTextPlugin; + +function getLoaderObject(loader) { + if (isString(loader)) { + return {loader: loader}; + } + return loader; +} + +function mergeOptions(a, b) { + if(!b) return a; + Object.keys(b).forEach(function(key) { + a[key] = b[key]; + }); + return a; +} + +function isString(a) { + return typeof a === "string"; +} + +function isFunction(a) { + return isType('Function', a); +} + +function isType(type, obj) { + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; +} + +ExtractTextPlugin.loader = function(options) { + return { loader: require.resolve("./loader"), options: options }; +}; + +ExtractTextPlugin.prototype.applyAdditionalInformation = function(source, info) { + if(info) { + return new ConcatSource( + "@media " + info[0] + " {", + source, + "}" + ); + } + return source; +}; + +ExtractTextPlugin.prototype.loader = function(options) { + return ExtractTextPlugin.loader(mergeOptions({id: this.id}, 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"); + } + if(options.fallbackLoader) { + console.warn('fallbackLoader option has been deprecated - replace with "fallback"'); + } + if(options.loader) { + console.warn('loader option has been deprecated - replace with "use"'); + } + if(Array.isArray(options) || isString(options) || typeof options.options === "object" || typeof options.query === 'object') { + options = { loader: options }; + } else { + validateOptions(path.resolve(__dirname, './schema/loader.json'), options, 'Extract Text Plugin (Loader)'); + } + var loader = options.use ||  options.loader; + var before = options.fallback || options.fallbackLoader || []; + if(isString(loader)) { + loader = loader.split("!"); + } + if(isString(before)) { + before = before.split("!"); + } else if(!Array.isArray(before)) { + before = [before]; + } + options = mergeOptions({omit: before.length, remove: true}, options); + delete options.loader; + delete options.use; + delete options.fallback; + delete options.fallbackLoader; + return [this.loader(options)] + .concat(before, loader) + .map(getLoaderObject); +} + +ExtractTextPlugin.extract = ExtractTextPlugin.prototype.extract.bind(ExtractTextPlugin); + +ExtractTextPlugin.prototype.apply = function(compiler) { + var options = this.options; + compiler.plugin("this-compilation", function(compilation) { + var extractCompilation = new ExtractTextPluginCompilation(); + compilation.plugin("normal-module-loader", function(loaderContext, module) { + loaderContext[NS] = function(content, opt) { + if(options.disable) + return false; + if(!Array.isArray(content) && content != null) + throw new Error("Exported value was not extracted as an array: " + JSON.stringify(content)); + module[NS] = { + content: content, + options: opt || {} + }; + return options.allChunks || module[NS + "/extract"]; // eslint-disable-line no-path-concat + }; + }); + var filename = this.filename; + var id = this.id; + var extractedChunks, entryChunks, initialChunks; + compilation.plugin("optimize-tree", function(chunks, modules, callback) { + extractedChunks = chunks.map(function() { + return new Chunk(); + }); + chunks.forEach(function(chunk, i) { + var extractedChunk = extractedChunks[i]; + extractedChunk.index = i; + extractedChunk.originalChunk = chunk; + extractedChunk.name = chunk.name; + extractedChunk.entrypoints = chunk.entrypoints; + chunk.chunks.forEach(function(c) { + extractedChunk.addChunk(extractedChunks[chunks.indexOf(c)]); + }); + chunk.parents.forEach(function(c) { + extractedChunk.addParent(extractedChunks[chunks.indexOf(c)]); + }); + }); + async.forEach(chunks, function(chunk, callback) { + var extractedChunk = extractedChunks[chunks.indexOf(chunk)]; + var shouldExtract = !!(options.allChunks || isInitialOrHasNoParents(chunk)); + chunk.sortModules(); + async.forEach(chunk.modules.slice(), function(module, callback) { + var meta = module[NS]; + 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) { + if(err) { + compilation.errors.push(err); + return callback(); + } + meta = module[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"); + compilation.errors.push(err); + return callback(); + } + if(meta.content) + extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); + callback(); + }); + } else { + if(meta.content) + extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); + callback(); + } + } else callback(); + }, function(err) { + if(err) return callback(err); + callback(); + }); + }, function(err) { + if(err) return callback(err); + extractedChunks.forEach(function(extractedChunk) { + if(isInitialOrHasNoParents(extractedChunk)) + this.mergeNonInitialChunks(extractedChunk); + }, this); + extractedChunks.forEach(function(extractedChunk) { + if(!isInitialOrHasNoParents(extractedChunk)) { + extractedChunk.modules.slice().forEach(function(module) { + extractedChunk.removeModule(module); + }); + } + }); + compilation.applyPlugins("optimize-extracted-chunks", extractedChunks); + callback(); + }.bind(this)); + }.bind(this)); + compilation.plugin("additional-assets", function(callback) { + extractedChunks.forEach(function(extractedChunk) { + if(extractedChunk.modules.length) { + extractedChunk.modules.sort(function(a, b) { + if(!options.ignoreOrder && isInvalidOrder(a, b)) { + compilation.errors.push(new OrderUndefinedError(a.getOriginalModule())); + compilation.errors.push(new OrderUndefinedError(b.getOriginalModule())); + } + return getOrder(a, b); + }); + var chunk = extractedChunk.originalChunk; + var source = this.renderExtractedChunk(extractedChunk); + + var getPath = (format) => compilation.getPath(format, { + chunk: chunk + }).replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function() { + return loaderUtils.getHashDigest(source.source(), arguments[1], arguments[2], parseInt(arguments[3], 10)); + }); + + var file = (isFunction(filename)) ? filename(getPath) : getPath(filename); + + compilation.assets[file] = source; + chunk.files.push(file); + } + }, this); + callback(); + }.bind(this)); + }.bind(this)); +}; From 9acaa92cee79f7d20f517db38319ad6e8b70c35a Mon Sep 17 00:00:00 2001 From: Steven Hargrove Date: Thu, 6 Jul 2017 17:23:13 -0400 Subject: [PATCH 2/4] added regression test for #548 --- test/cases/chunk-modules-ordered-by-id/a.js | 1 + test/cases/chunk-modules-ordered-by-id/b.txt | 3 +++ test/cases/chunk-modules-ordered-by-id/c.txt | 3 +++ test/cases/chunk-modules-ordered-by-id/expected/file.css | 6 ++++++ test/cases/chunk-modules-ordered-by-id/index.js | 2 ++ test/cases/chunk-modules-ordered-by-id/webpack.config.js | 7 +++++++ 6 files changed, 22 insertions(+) create mode 100644 test/cases/chunk-modules-ordered-by-id/a.js create mode 100644 test/cases/chunk-modules-ordered-by-id/b.txt create mode 100644 test/cases/chunk-modules-ordered-by-id/c.txt create mode 100644 test/cases/chunk-modules-ordered-by-id/expected/file.css create mode 100644 test/cases/chunk-modules-ordered-by-id/index.js create mode 100644 test/cases/chunk-modules-ordered-by-id/webpack.config.js diff --git a/test/cases/chunk-modules-ordered-by-id/a.js b/test/cases/chunk-modules-ordered-by-id/a.js new file mode 100644 index 00000000..e72da909 --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/a.js @@ -0,0 +1 @@ +require('./c.txt'); diff --git a/test/cases/chunk-modules-ordered-by-id/b.txt b/test/cases/chunk-modules-ordered-by-id/b.txt new file mode 100644 index 00000000..6b0f2b45 --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/b.txt @@ -0,0 +1,3 @@ +.block { + color: tomato; +} diff --git a/test/cases/chunk-modules-ordered-by-id/c.txt b/test/cases/chunk-modules-ordered-by-id/c.txt new file mode 100644 index 00000000..b0fb26a3 --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/c.txt @@ -0,0 +1,3 @@ +.App { + color: black; +} diff --git a/test/cases/chunk-modules-ordered-by-id/expected/file.css b/test/cases/chunk-modules-ordered-by-id/expected/file.css new file mode 100644 index 00000000..cf83e47d --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/expected/file.css @@ -0,0 +1,6 @@ +.block { + color: tomato; +} +.App { + color: black; +} diff --git a/test/cases/chunk-modules-ordered-by-id/index.js b/test/cases/chunk-modules-ordered-by-id/index.js new file mode 100644 index 00000000..5a240449 --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/index.js @@ -0,0 +1,2 @@ +require('./a'); +require('./b.txt'); diff --git a/test/cases/chunk-modules-ordered-by-id/webpack.config.js b/test/cases/chunk-modules-ordered-by-id/webpack.config.js new file mode 100644 index 00000000..0fbc2d5c --- /dev/null +++ b/test/cases/chunk-modules-ordered-by-id/webpack.config.js @@ -0,0 +1,7 @@ +var ExtractTextPlugin = require("../../../"); +module.exports = { + entry: "./index", + plugins: [ + new ExtractTextPlugin("file.css") + ] +}; From 7723e3a0757654648438700c4928402ef6c63d36 Mon Sep 17 00:00:00 2001 From: Steven Hargrove Date: Thu, 6 Jul 2017 18:05:55 -0400 Subject: [PATCH 3/4] upgraded to latest webpack version 3.1.0 which includes needed Chunk.sortModules method --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 975a6b8b..c2cf23fd 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "node": ">= 4.3 < 5.0.0 || >= 5.10" }, "peerDependencies": { - "webpack": "^3.0.0" + "webpack": "^3.1.0" }, "homepage": "http://github.com/webpack-contrib/extract-text-webpack-plugin", "repository": { From dde589649a0fc83cb74a707a3cfc29022fbae5f4 Mon Sep 17 00:00:00 2001 From: Steven Hargrove Date: Thu, 6 Jul 2017 21:25:20 -0400 Subject: [PATCH 4/4] applied change to new location of index.js --- index.js | 350 --------------------------------------------------- src/index.js | 1 + 2 files changed, 1 insertion(+), 350 deletions(-) delete mode 100644 index.js diff --git a/index.js b/index.js deleted file mode 100644 index 4dd74015..00000000 --- a/index.js +++ /dev/null @@ -1,350 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -var fs = require('fs'); -var ConcatSource = require("webpack-sources").ConcatSource; -var async = require("async"); -var ExtractedModule = require("./ExtractedModule"); -var Chunk = require("webpack/lib/Chunk"); -var OrderUndefinedError = require("./OrderUndefinedError"); -var loaderUtils = require("loader-utils"); -var validateOptions = require('schema-utils'); -var path = require('path'); - -var NS = fs.realpathSync(__dirname); - -var nextId = 0; - -function ExtractTextPluginCompilation() { - this.modulesByIdentifier = {}; -} - -function isInitialOrHasNoParents(chunk) { - return chunk.isInitial() || chunk.parents.length === 0; -} - -ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) { - if(!intoChunk) { - checkedChunks = []; - chunk.chunks.forEach(function(c) { - if(isInitialOrHasNoParents(c)) return; - this.mergeNonInitialChunks(c, chunk, checkedChunks); - }, this); - } else if(checkedChunks.indexOf(chunk) < 0) { - checkedChunks.push(chunk); - chunk.modules.slice().forEach(function(module) { - intoChunk.addModule(module); - module.addChunk(intoChunk); - }); - chunk.chunks.forEach(function(c) { - if(isInitialOrHasNoParents(c)) return; - this.mergeNonInitialChunks(c, intoChunk, checkedChunks); - }, this); - } -}; - -ExtractTextPluginCompilation.prototype.addModule = function(identifier, originalModule, source, additionalInformation, sourceMap, prevModules) { - var m; - if(!this.modulesByIdentifier[identifier]) { - m = this.modulesByIdentifier[identifier] = new ExtractedModule(identifier, originalModule, source, sourceMap, additionalInformation, prevModules); - } else { - m = this.modulesByIdentifier[identifier]; - m.addPrevModules(prevModules); - if(originalModule.index2 < m.getOriginalModule().index2) { - m.setOriginalModule(originalModule); - } - } - return m; -}; - -ExtractTextPluginCompilation.prototype.addResultToChunk = function(identifier, result, originalModule, extractedChunk) { - if(!Array.isArray(result)) { - result = [[identifier, result]]; - } - var counterMap = {}; - var prevModules = []; - result.forEach(function(item) { - var c = counterMap[item[0]]; - var module = this.addModule.call(this, item[0] + (c || ""), originalModule, item[1], item[2], item[3], prevModules.slice()); - extractedChunk.addModule(module); - module.addChunk(extractedChunk); - counterMap[item[0]] = (c || 0) + 1; - prevModules.push(module); - }, this); -}; - -ExtractTextPlugin.prototype.renderExtractedChunk = function(chunk) { - var source = new ConcatSource(); - chunk.modules.forEach(function(module) { - var moduleSource = module.source(); - source.add(this.applyAdditionalInformation(moduleSource, module.additionalInformation)); - }, this); - return source; -}; - -function isInvalidOrder(a, b) { - var bBeforeA = a.getPrevModules().indexOf(b) >= 0; - var aBeforeB = b.getPrevModules().indexOf(a) >= 0; - return aBeforeB && bBeforeA; -} - -function getOrder(a, b) { - var aOrder = a.getOrder(); - var bOrder = b.getOrder(); - if(aOrder < bOrder) return -1; - if(aOrder > bOrder) return 1; - var aIndex = a.getOriginalModule().index2; - var bIndex = b.getOriginalModule().index2; - if(aIndex < bIndex) return -1; - if(aIndex > bIndex) return 1; - var bBeforeA = a.getPrevModules().indexOf(b) >= 0; - var aBeforeB = b.getPrevModules().indexOf(a) >= 0; - if(aBeforeB && !bBeforeA) return -1; - if(!aBeforeB && bBeforeA) return 1; - var ai = a.identifier(); - var bi = b.identifier(); - if(ai < bi) return -1; - if(ai > bi) return 1; - return 0; -} - -function ExtractTextPlugin(options) { - if(arguments.length > 1) { - throw new Error("Breaking change: ExtractTextPlugin now only takes a single argument. Either an options " + - "object *or* the name of the result file.\n" + - "Example: if your old code looked like this:\n" + - " new ExtractTextPlugin('css/[name].css', { disable: false, allChunks: true })\n\n" + - "You would change it to:\n" + - " new ExtractTextPlugin({ filename: 'css/[name].css', disable: false, allChunks: true })\n\n" + - "The available options are:\n" + - " filename: string\n" + - " allChunks: boolean\n" + - " disable: boolean\n" + - " ignoreOrder: boolean\n"); - } - if(isString(options)) { - options = { filename: options }; - } else { - validateOptions(path.resolve(__dirname, './schema/plugin.json'), options, 'Extract Text Plugin'); - } - this.filename = options.filename; - this.id = options.id != null ? options.id : ++nextId; - this.options = {}; - mergeOptions(this.options, options); - delete this.options.filename; - delete this.options.id; -} -module.exports = ExtractTextPlugin; - -function getLoaderObject(loader) { - if (isString(loader)) { - return {loader: loader}; - } - return loader; -} - -function mergeOptions(a, b) { - if(!b) return a; - Object.keys(b).forEach(function(key) { - a[key] = b[key]; - }); - return a; -} - -function isString(a) { - return typeof a === "string"; -} - -function isFunction(a) { - return isType('Function', a); -} - -function isType(type, obj) { - return Object.prototype.toString.call(obj) === '[object ' + type + ']'; -} - -ExtractTextPlugin.loader = function(options) { - return { loader: require.resolve("./loader"), options: options }; -}; - -ExtractTextPlugin.prototype.applyAdditionalInformation = function(source, info) { - if(info) { - return new ConcatSource( - "@media " + info[0] + " {", - source, - "}" - ); - } - return source; -}; - -ExtractTextPlugin.prototype.loader = function(options) { - return ExtractTextPlugin.loader(mergeOptions({id: this.id}, 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"); - } - if(options.fallbackLoader) { - console.warn('fallbackLoader option has been deprecated - replace with "fallback"'); - } - if(options.loader) { - console.warn('loader option has been deprecated - replace with "use"'); - } - if(Array.isArray(options) || isString(options) || typeof options.options === "object" || typeof options.query === 'object') { - options = { loader: options }; - } else { - validateOptions(path.resolve(__dirname, './schema/loader.json'), options, 'Extract Text Plugin (Loader)'); - } - var loader = options.use ||  options.loader; - var before = options.fallback || options.fallbackLoader || []; - if(isString(loader)) { - loader = loader.split("!"); - } - if(isString(before)) { - before = before.split("!"); - } else if(!Array.isArray(before)) { - before = [before]; - } - options = mergeOptions({omit: before.length, remove: true}, options); - delete options.loader; - delete options.use; - delete options.fallback; - delete options.fallbackLoader; - return [this.loader(options)] - .concat(before, loader) - .map(getLoaderObject); -} - -ExtractTextPlugin.extract = ExtractTextPlugin.prototype.extract.bind(ExtractTextPlugin); - -ExtractTextPlugin.prototype.apply = function(compiler) { - var options = this.options; - compiler.plugin("this-compilation", function(compilation) { - var extractCompilation = new ExtractTextPluginCompilation(); - compilation.plugin("normal-module-loader", function(loaderContext, module) { - loaderContext[NS] = function(content, opt) { - if(options.disable) - return false; - if(!Array.isArray(content) && content != null) - throw new Error("Exported value was not extracted as an array: " + JSON.stringify(content)); - module[NS] = { - content: content, - options: opt || {} - }; - return options.allChunks || module[NS + "/extract"]; // eslint-disable-line no-path-concat - }; - }); - var filename = this.filename; - var id = this.id; - var extractedChunks, entryChunks, initialChunks; - compilation.plugin("optimize-tree", function(chunks, modules, callback) { - extractedChunks = chunks.map(function() { - return new Chunk(); - }); - chunks.forEach(function(chunk, i) { - var extractedChunk = extractedChunks[i]; - extractedChunk.index = i; - extractedChunk.originalChunk = chunk; - extractedChunk.name = chunk.name; - extractedChunk.entrypoints = chunk.entrypoints; - chunk.chunks.forEach(function(c) { - extractedChunk.addChunk(extractedChunks[chunks.indexOf(c)]); - }); - chunk.parents.forEach(function(c) { - extractedChunk.addParent(extractedChunks[chunks.indexOf(c)]); - }); - }); - async.forEach(chunks, function(chunk, callback) { - var extractedChunk = extractedChunks[chunks.indexOf(chunk)]; - var shouldExtract = !!(options.allChunks || isInitialOrHasNoParents(chunk)); - chunk.sortModules(); - async.forEach(chunk.modules.slice(), function(module, callback) { - var meta = module[NS]; - 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) { - if(err) { - compilation.errors.push(err); - return callback(); - } - meta = module[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"); - compilation.errors.push(err); - return callback(); - } - if(meta.content) - extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); - callback(); - }); - } else { - if(meta.content) - extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); - callback(); - } - } else callback(); - }, function(err) { - if(err) return callback(err); - callback(); - }); - }, function(err) { - if(err) return callback(err); - extractedChunks.forEach(function(extractedChunk) { - if(isInitialOrHasNoParents(extractedChunk)) - this.mergeNonInitialChunks(extractedChunk); - }, this); - extractedChunks.forEach(function(extractedChunk) { - if(!isInitialOrHasNoParents(extractedChunk)) { - extractedChunk.modules.slice().forEach(function(module) { - extractedChunk.removeModule(module); - }); - } - }); - compilation.applyPlugins("optimize-extracted-chunks", extractedChunks); - callback(); - }.bind(this)); - }.bind(this)); - compilation.plugin("additional-assets", function(callback) { - extractedChunks.forEach(function(extractedChunk) { - if(extractedChunk.modules.length) { - extractedChunk.modules.sort(function(a, b) { - if(!options.ignoreOrder && isInvalidOrder(a, b)) { - compilation.errors.push(new OrderUndefinedError(a.getOriginalModule())); - compilation.errors.push(new OrderUndefinedError(b.getOriginalModule())); - } - return getOrder(a, b); - }); - var chunk = extractedChunk.originalChunk; - var source = this.renderExtractedChunk(extractedChunk); - - var getPath = (format) => compilation.getPath(format, { - chunk: chunk - }).replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function() { - return loaderUtils.getHashDigest(source.source(), arguments[1], arguments[2], parseInt(arguments[3], 10)); - }); - - var file = (isFunction(filename)) ? filename(getPath) : getPath(filename); - - compilation.assets[file] = source; - chunk.files.push(file); - } - }, this); - callback(); - }.bind(this)); - }.bind(this)); -}; diff --git a/src/index.js b/src/index.js index 6105992e..161f67bd 100644 --- a/src/index.js +++ b/src/index.js @@ -144,6 +144,7 @@ class ExtractTextPlugin { async.forEach(chunks, (chunk, callback) => { // eslint-disable-line no-shadow const extractedChunk = extractedChunks[chunks.indexOf(chunk)]; const shouldExtract = !!(options.allChunks || isInitialOrHasNoParents(chunk)); + chunk.sortModules(); async.forEach(chunk.mapModules((c) => { return c; }), (module, callback) => { // eslint-disable-line no-shadow, arrow-body-style let meta = module[NS]; if (meta && (!meta.options.id || meta.options.id === id)) {