1 |
|
2 |
|
3 |
|
4 |
|
5 | var ConcatSource = require("webpack-sources").ConcatSource;
|
6 | var async = require("async");
|
7 | var ExtractedModule = require("./ExtractedModule");
|
8 | var Chunk = require("webpack/lib/Chunk");
|
9 | var OrderUndefinedError = require("./OrderUndefinedError");
|
10 | var loaderUtils = require("loader-utils");
|
11 |
|
12 | var nextId = 0;
|
13 |
|
14 | function ExtractTextPluginCompilation() {
|
15 | this.modulesByIdentifier = {};
|
16 | }
|
17 |
|
18 | ExtractTextPlugin.prototype.mergeNonInitialChunks = function(chunk, intoChunk, checkedChunks) {
|
19 | if(!intoChunk) {
|
20 | checkedChunks = [];
|
21 | chunk.chunks.forEach(function(c) {
|
22 | if(c.initial) return;
|
23 | this.mergeNonInitialChunks(c, chunk, checkedChunks);
|
24 | }, this);
|
25 | } else if(checkedChunks.indexOf(chunk) < 0) {
|
26 | checkedChunks.push(chunk);
|
27 | chunk.modules.slice().forEach(function(module) {
|
28 | intoChunk.addModule(module);
|
29 | module.addChunk(intoChunk);
|
30 | });
|
31 | chunk.chunks.forEach(function(c) {
|
32 | if(c.initial) return;
|
33 | this.mergeNonInitialChunks(c, intoChunk, checkedChunks);
|
34 | }, this);
|
35 | }
|
36 | };
|
37 |
|
38 | ExtractTextPluginCompilation.prototype.addModule = function(identifier, originalModule, source, additionalInformation, sourceMap, prevModules) {
|
39 | var m;
|
40 | if(!this.modulesByIdentifier[identifier]) {
|
41 | m = this.modulesByIdentifier[identifier] = new ExtractedModule(identifier, originalModule, source, sourceMap, additionalInformation, prevModules);
|
42 | } else {
|
43 | m = this.modulesByIdentifier[identifier];
|
44 | m.addPrevModules(prevModules);
|
45 | if(originalModule.index2 < m.getOriginalModule().index2) {
|
46 | m.setOriginalModule(originalModule);
|
47 | }
|
48 | }
|
49 | return m;
|
50 | };
|
51 |
|
52 | ExtractTextPluginCompilation.prototype.addResultToChunk = function(identifier, result, originalModule, extractedChunk) {
|
53 | if(!Array.isArray(result)) {
|
54 | result = [[identifier, result]];
|
55 | }
|
56 | var counterMap = {};
|
57 | var prevModules = [];
|
58 | result.forEach(function(item) {
|
59 | var c = counterMap[item[0]];
|
60 | var module = this.addModule.call(this, item[0] + (c || ""), originalModule, item[1], item[2], item[3], prevModules.slice());
|
61 | extractedChunk.addModule(module);
|
62 | module.addChunk(extractedChunk);
|
63 | counterMap[item[0]] = (c || 0) + 1;
|
64 | prevModules.push(module);
|
65 | }, this);
|
66 | };
|
67 |
|
68 | ExtractTextPlugin.prototype.renderExtractedChunk = function(chunk) {
|
69 | var source = new ConcatSource();
|
70 | chunk.modules.forEach(function(module) {
|
71 | var moduleSource = module.source();
|
72 | source.add(this.applyAdditionalInformation(moduleSource, module.additionalInformation));
|
73 | }, this);
|
74 | return source;
|
75 | };
|
76 |
|
77 | function isInvalidOrder(a, b) {
|
78 | var bBeforeA = a.getPrevModules().indexOf(b) >= 0;
|
79 | var aBeforeB = b.getPrevModules().indexOf(a) >= 0;
|
80 | return aBeforeB && bBeforeA;
|
81 | }
|
82 |
|
83 | function getOrder(a, b) {
|
84 | var aOrder = a.getOrder();
|
85 | var bOrder = b.getOrder();
|
86 | if(aOrder < bOrder) return -1;
|
87 | if(aOrder > bOrder) return 1;
|
88 | var aIndex = a.getOriginalModule().index2;
|
89 | var bIndex = b.getOriginalModule().index2;
|
90 | if(aIndex < bIndex) return -1;
|
91 | if(aIndex > bIndex) return 1;
|
92 | var bBeforeA = a.getPrevModules().indexOf(b) >= 0;
|
93 | var aBeforeB = b.getPrevModules().indexOf(a) >= 0;
|
94 | if(aBeforeB && !bBeforeA) return -1;
|
95 | if(!aBeforeB && bBeforeA) return 1;
|
96 | var ai = a.identifier();
|
97 | var bi = b.identifier();
|
98 | if(ai < bi) return -1;
|
99 | if(ai > bi) return 1;
|
100 | return 0;
|
101 | }
|
102 |
|
103 | function ExtractTextPlugin(id, filename, options) {
|
104 | if(typeof filename !== "string") {
|
105 | options = filename;
|
106 | filename = id;
|
107 | id = ++nextId;
|
108 | }
|
109 | if(!options) options = {};
|
110 | this.filename = filename;
|
111 | this.options = options;
|
112 | this.id = id;
|
113 | }
|
114 | module.exports = ExtractTextPlugin;
|
115 |
|
116 | function mergeOptions(a, b) {
|
117 | if(!b) return a;
|
118 | Object.keys(b).forEach(function(key) {
|
119 | a[key] = b[key];
|
120 | });
|
121 | return a;
|
122 | }
|
123 |
|
124 | ExtractTextPlugin.loader = function(options) {
|
125 | return require.resolve("./loader") + (options ? "?" + JSON.stringify(options) : "");
|
126 | };
|
127 |
|
128 | ExtractTextPlugin.extract = function(before, loader, options) {
|
129 | if(typeof loader === "string" || Array.isArray(loader)) {
|
130 | if(typeof before === "string") {
|
131 | before = before.split("!");
|
132 | }
|
133 | return [
|
134 | ExtractTextPlugin.loader(mergeOptions({omit: before.length, extract: true, remove: true}, options))
|
135 | ].concat(before, loader).join("!");
|
136 | } else {
|
137 | options = loader;
|
138 | loader = before;
|
139 | return [
|
140 | ExtractTextPlugin.loader(mergeOptions({remove: true}, options))
|
141 | ].concat(loader).join("!");
|
142 | }
|
143 | };
|
144 |
|
145 | ExtractTextPlugin.prototype.applyAdditionalInformation = function(source, info) {
|
146 | if(info) {
|
147 | return new ConcatSource(
|
148 | "@media " + info[0] + " {",
|
149 | source,
|
150 | "}"
|
151 | );
|
152 | }
|
153 | return source;
|
154 | };
|
155 |
|
156 | ExtractTextPlugin.prototype.loader = function(options) {
|
157 | options = JSON.parse(JSON.stringify(options || {}));
|
158 | options.id = this.id;
|
159 | return ExtractTextPlugin.loader(options);
|
160 | };
|
161 |
|
162 | ExtractTextPlugin.prototype.extract = function(before, loader, options) {
|
163 | if(typeof loader === "string" || Array.isArray(loader)) {
|
164 | if(typeof before === "string") {
|
165 | before = before.split("!");
|
166 | }
|
167 | return [
|
168 | this.loader(mergeOptions({omit: before.length, extract: true, remove: true}, options))
|
169 | ].concat(before, loader).join("!");
|
170 | } else {
|
171 | options = loader;
|
172 | loader = before;
|
173 | return [
|
174 | this.loader(mergeOptions({remove: true}, options))
|
175 | ].concat(loader).join("!");
|
176 | }
|
177 | };
|
178 |
|
179 | ExtractTextPlugin.prototype.apply = function(compiler) {
|
180 | var options = this.options;
|
181 | compiler.plugin("this-compilation", function(compilation) {
|
182 | var extractCompilation = new ExtractTextPluginCompilation();
|
183 | compilation.plugin("normal-module-loader", function(loaderContext, module) {
|
184 | loaderContext[__dirname] = function(content, opt) {
|
185 | if(options.disable)
|
186 | return false;
|
187 | if(!Array.isArray(content) && content !== null)
|
188 | throw new Error("Exported value is not a string.");
|
189 | module.meta[__dirname] = {
|
190 | content: content,
|
191 | options: opt || {}
|
192 | };
|
193 | return options.allChunks || module.meta[__dirname + "/extract"];
|
194 | };
|
195 | });
|
196 | var filename = this.filename;
|
197 | var id = this.id;
|
198 | var extractedChunks, entryChunks, initialChunks;
|
199 | compilation.plugin("optimize", function() {
|
200 | entryChunks = compilation.chunks.filter(function(c) {
|
201 | return c.entry;
|
202 | });
|
203 | initialChunks = compilation.chunks.filter(function(c) {
|
204 | return c.initial;
|
205 | });
|
206 | });
|
207 | compilation.plugin("optimize-tree", function(chunks, modules, callback) {
|
208 | extractedChunks = chunks.map(function() {
|
209 | return new Chunk();
|
210 | });
|
211 | chunks.forEach(function(chunk, i) {
|
212 | var extractedChunk = extractedChunks[i];
|
213 | extractedChunk.index = i;
|
214 | extractedChunk.originalChunk = chunk;
|
215 | extractedChunk.name = chunk.name;
|
216 | extractedChunk.entry = chunk.entry;
|
217 | extractedChunk.initial = chunk.initial;
|
218 | chunk.chunks.forEach(function(c) {
|
219 | extractedChunk.addChunk(extractedChunks[chunks.indexOf(c)]);
|
220 | });
|
221 | chunk.parents.forEach(function(c) {
|
222 | extractedChunk.addParent(extractedChunks[chunks.indexOf(c)]);
|
223 | });
|
224 | });
|
225 | entryChunks.forEach(function(chunk) {
|
226 | var idx = chunks.indexOf(chunk);
|
227 | if(idx < 0) return;
|
228 | var extractedChunk = extractedChunks[idx];
|
229 | extractedChunk.entry = true;
|
230 | });
|
231 | initialChunks.forEach(function(chunk) {
|
232 | var idx = chunks.indexOf(chunk);
|
233 | if(idx < 0) return;
|
234 | var extractedChunk = extractedChunks[idx];
|
235 | extractedChunk.initial = true;
|
236 | });
|
237 | async.forEach(chunks, function(chunk, callback) {
|
238 | var extractedChunk = extractedChunks[chunks.indexOf(chunk)];
|
239 | var shouldExtract = !!(options.allChunks || chunk.initial);
|
240 | async.forEach(chunk.modules.slice(), function(module, callback) {
|
241 | var meta = module.meta && module.meta[__dirname];
|
242 | if(meta && (!meta.options.id || meta.options.id === id)) {
|
243 | var wasExtracted = Array.isArray(meta.content);
|
244 | if(shouldExtract !== wasExtracted) {
|
245 | module.meta[__dirname + "/extract"] = shouldExtract;
|
246 | compilation.rebuildModule(module, function(err) {
|
247 | if(err) {
|
248 | compilation.errors.push(err);
|
249 | return callback();
|
250 | }
|
251 | meta = module.meta[__dirname];
|
252 | if(!Array.isArray(meta.content)) {
|
253 | err = new Error(module.identifier() + " doesn't export content");
|
254 | compilation.errors.push(err);
|
255 | return callback();
|
256 | }
|
257 | if(meta.content)
|
258 | extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk);
|
259 | callback();
|
260 | });
|
261 | } else {
|
262 | if(meta.content)
|
263 | extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk);
|
264 | callback();
|
265 | }
|
266 | } else callback();
|
267 | }, function(err) {
|
268 | if(err) return callback(err);
|
269 | callback();
|
270 | });
|
271 | }, function(err) {
|
272 | if(err) return callback(err);
|
273 | extractedChunks.forEach(function(extractedChunk) {
|
274 | if(extractedChunk.initial)
|
275 | this.mergeNonInitialChunks(extractedChunk);
|
276 | }, this);
|
277 | extractedChunks.forEach(function(extractedChunk) {
|
278 | if(!extractedChunk.initial) {
|
279 | extractedChunk.modules.forEach(function(module) {
|
280 | extractedChunk.removeModule(module);
|
281 | });
|
282 | }
|
283 | });
|
284 | compilation.applyPlugins("optimize-extracted-chunks", extractedChunks);
|
285 | callback();
|
286 | }.bind(this));
|
287 | }.bind(this));
|
288 | compilation.plugin("additional-assets", function(callback) {
|
289 | extractedChunks.forEach(function(extractedChunk) {
|
290 | if(extractedChunk.modules.length) {
|
291 | extractedChunk.modules.sort(function(a, b) {
|
292 | if(!options.ignoreOrder && isInvalidOrder(a, b)) {
|
293 | compilation.errors.push(new OrderUndefinedError(a.getOriginalModule()));
|
294 | compilation.errors.push(new OrderUndefinedError(b.getOriginalModule()));
|
295 | }
|
296 | return getOrder(a, b);
|
297 | });
|
298 | var chunk = extractedChunk.originalChunk;
|
299 | var source = this.renderExtractedChunk(extractedChunk);
|
300 | var file = compilation.getPath(filename, {
|
301 | chunk: chunk
|
302 | }).replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function() {
|
303 | return loaderUtils.getHashDigest(source.source(), arguments[1], arguments[2], parseInt(arguments[3], 10));
|
304 | });
|
305 | compilation.assets[file] = source;
|
306 | chunk.files.push(file);
|
307 | }
|
308 | }, this);
|
309 | callback();
|
310 | }.bind(this));
|
311 | }.bind(this));
|
312 | };
|