Code coverage report for lib\optimize\DedupePlugin.js

Statements: 97.37% (111 / 114)      Branches: 89.58% (43 / 48)      Functions: 100% (22 / 22)      Lines: 98.13% (105 / 107)      Ignored: none     

All files » lib\optimize\ » DedupePlugin.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219        1 1   1   1   1 174   174   174 174 174 174 1101 1013 1013 1013 120 60 60   60     893       174 254 174 174 174 270 270 270 1129 192 180 192 192 192 192 110 110 110   110   82 82 72 72 72 72 72 72 72   10 10   10         270 104 96     270 72 72 72 18 18 18 18 18 18       174 38     1 120 26       174 174 1127 192 192 192 192 48         48 46     48 52   52   48 48   144 538   144 90 90     174 174   174 37 20   17                                                                   174 174 136   38 38 38                                                           38 38 38 38        
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var ConcatSource = require("webpack-core/lib/ConcatSource");
var TemplateArgumentDependency = require("../dependencies/TemplateArgumentDependency");
 
function DedupePlugin() {
}
module.exports = DedupePlugin;
 
DedupePlugin.prototype.apply = function(compiler) {
	compiler.plugin("compilation", function(compilation) {
 
		compilation.dependencyTemplates.set(TemplateArgumentDependency, new TemplateArgumentDependency.Template());
 
		compilation.plugin("after-optimize-modules", function(modules) {
			var modulesByHash = {};
			var allDups = [];
			modules.forEach(function(module, idx) {
				if(!module.getSourceHash || !module.getAllModuleDependencies || !module.createTemplate || !module.getTemplateArguments) return;
				var hash = module.getSourceHash();
				var dupModule = modulesByHash[hash];
				if(dupModule) {
					if(dupModule.duplicates) {
						dupModule.duplicates.push(module);
						module.duplicates = dupModule.duplicates;
					} else {
						allDups.push(module.duplicates = dupModule.duplicates = [dupModule, module]);
					}
				} else {
					modulesByHash[hash] = module;
				}
			});
		});
		compilation.plugin("after-optimize-chunks", function(chunks) {
			var entryChunks = chunks.filter(function(c) { return c.entry; });
			entryChunks.forEach(function(chunk) { // for each entry chunk
				var hasDeduplicatedModules = false;
				(function x(dups, roots, visited, chunk) {
					var currentDups = [];
					var currentRoots = [];
					chunk.modules.forEach(function(module) {
						if(module.duplicates) {
							if(!module.rootDuplicatesChunks)
								module.rootDuplicatesChunks = module.chunks.slice();
							var chunkIndex = module.rootDuplicatesChunks.indexOf(chunk);
							if(!module.rootDuplicates) module.rootDuplicates = [];
							var idx = currentDups.indexOf(module.duplicates);
							if(idx >= 0) {
								module.rootDuplicates[chunkIndex] = currentRoots[idx];
								module.rootDuplicates[chunkIndex].push(module);
								module.rootDuplicates[chunkIndex].commonModules =
									mergeCommonModules(module.rootDuplicates[chunkIndex].commonModules, module.getAllModuleDependencies());
								hasDeduplicatedModules = true;
							} else {
								idx = dups.indexOf(module.duplicates);
								if(idx < 0) {
									module.rootDuplicates[chunkIndex] = [module];
									module.rootDuplicates[chunkIndex].commonModules = module.getAllModuleDependencies();
									module.rootDuplicates[chunkIndex].initialCommonModulesLength = module.rootDuplicates[chunkIndex].commonModules.length;
									dups = dups.concat([module.duplicates]);
									roots = roots.concat([module.rootDuplicates[chunkIndex]]);
									currentDups = currentDups.concat([module.duplicates]);
									currentRoots = currentRoots.concat([module.rootDuplicates[chunkIndex]]);
								} else {
									module.rootDuplicates[chunkIndex] = roots[idx];
									module.rootDuplicates[chunkIndex].commonModules =
										mergeCommonModules(module.rootDuplicates[chunkIndex].commonModules, module.getAllModuleDependencies());
									hasDeduplicatedModules = true;
								}
							}
						}
					});
					chunk.chunks.forEach(function(chunk) {
						if(visited.indexOf(chunk) < 0)
							x(dups, roots, visited.concat(chunk), chunk);
					})
 
					currentRoots.forEach(function(roots) {
						var commonModules = roots.commonModules;
						var initialLength = roots.initialCommonModulesLength;
						if(initialLength !== commonModules.length) {
							var template = roots[0].createTemplate(commonModules);
							roots.template = template;
							chunk.addModule(template);
							template.addChunk(chunk);
							compilation.modules.push(template);
							hasDeduplicatedModules = true;
						}
					});
				}([], [], [], chunk));
				if(hasDeduplicatedModules)
					chunk._DedupePlugin_hasDeduplicatedModules = true;
			});
		});
		function mergeCommonModules(commonModules, newModules) {
			return commonModules.filter(function(module) {
				return newModules.indexOf(module) >= 0;
			});
		}
	});
	compiler.plugin("compilation", function(compilation) {
		compilation.moduleTemplate.plugin("package", function(moduleSource, module, chunk) {
			if(!module.rootDuplicatesChunks || !chunk) return moduleSource;
			var chunkIndex = module.rootDuplicatesChunks.indexOf(chunk);
			Iif(!module.rootDuplicates || !module.rootDuplicates[chunkIndex]) return moduleSource;
			var rootDuplicates = module.rootDuplicates[chunkIndex];
			if(rootDuplicates.template) {
				rootDuplicates.template.addReason(module, {
					type: "template",
					request: module.request,
					templateModules: rootDuplicates.template.templateModules
				});
				rootDuplicates.template.reasons.sort(function(a, b) {
					Eif(a.request === b.request) return 0;
					return a.request < b.request ? -1 : 1;
				});
				var array = [rootDuplicates.template.id].concat(module.getTemplateArguments(rootDuplicates.template.templateModules).map(function(module) {
					Iif(typeof module.id !== "number")
						return "(function webpackMissingModule() { throw new Error(" + JSON.stringify("Cannot find module") + "); }())"
					return module.id;
				}));
				var source = new ConcatSource("[" + array.join(", ") + "]");
				return source;
			} else {
				rootDuplicates.sort(function(a, b) {
					return a.id - b.id;
				});
				if(module === rootDuplicates[0]) return moduleSource;
				var source = new ConcatSource("" + rootDuplicates[0].id);
				return source;
			}
		});
		compilation.moduleTemplate.plugin("hash", function(hash) {
			hash.update("DedupModuleTemplate");
		});
		compilation.mainTemplate.plugin("add-module", function(source, chunk, hash, varModuleId, varModule) {
			if(!chunk._DedupePlugin_hasDeduplicatedModules) {
				return source;
			}
			return this.asString([
				"var _m = " + varModule + ";",
				"",
				"// Check if module is deduplicated",
				"switch(typeof _m) {",
				"case \"number\":",
				this.indent([
					"// Module is a copy of another module",
					"modules[" + varModuleId + "] = modules[_m];",
					"break;"
				]),
				"case \"object\":",
				this.indent([
					"// Module can be created from a template",
					"modules[" + varModuleId + "] = (function(_m) {",
					this.indent([
						"var args = _m.slice(1), templateId = _m[0];",
						"return function (a,b,c) {",
						this.indent([
							"modules[templateId].apply(this, [a,b,c].concat(args));"
						]),
						"};"
					]),
					"}(_m));",
					"break;"
				]),
				"default:",
				this.indent([
					"// Normal module",
					"modules[" + varModuleId + "] = _m;"
				]),
				"}"
			]);
		});
		compilation.mainTemplate.plugin("modules", function(orginalSource, chunk, hash, moduleTemplate, dependencyTemplates) {
			if(!chunk._DedupePlugin_hasDeduplicatedModules) {
				return orginalSource;
			}
			var source = new ConcatSource();
			source.add("(function(modules) {\n");
			source.add(this.indent([
				"// Check all modules for deduplicated modules",
				"for(var i in modules) {",
				this.indent([
					"switch(typeof modules[i]) {",
					"case \"number\":",
					this.indent([
						"// Module is a copy of another module",
						"modules[i] = modules[modules[i]];",
						"break;"
					]),
					"case \"object\":",
					this.indent([
						"// Module can be created from a template",
						"modules[i] = (function(_m) {",
						this.indent([
							"var args = _m.slice(1), fn = modules[_m[0]];",
							"return function (a,b,c) {",
							this.indent([
								"fn.apply(null, [a,b,c].concat(args));"
							]),
							"};"
						]),
						"}(modules[i]));"
					]),
					"}"
				]),
				"}",
				"return modules;"
			]));
			source.add("\n}(");
			source.add(orginalSource);
			source.add("))");
			return source;
		});
	});
};