mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			228 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| var ConcatSource = require("webpack-sources").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-tree", function(chunks, modules) {
 | |
| 			var modulesByHash = {};
 | |
| 			var allDups = [];
 | |
| 			modules.forEach(function(module) {
 | |
| 				if(!module.getSourceHash || !module.getAllModuleDependencies || !module.createTemplate || !module.getTemplateArguments || module.blocks.length > 0) 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;
 | |
| 				}
 | |
| 			});
 | |
| 			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.slice());
 | |
| 							roots.template = template;
 | |
| 							chunk.addModule(template);
 | |
| 							template.addChunk(chunk);
 | |
| 							compilation.modules.push(template);
 | |
| 							hasDeduplicatedModules = true;
 | |
| 						}
 | |
| 					});
 | |
| 				}([], [], [], chunk));
 | |
| 				if(hasDeduplicatedModules)
 | |
| 					chunk.__DedupePluginHasDeduplicatedModules = true;
 | |
| 			});
 | |
| 		});
 | |
| 
 | |
| 		function mergeCommonModules(commonModules, newModules) {
 | |
| 			return commonModules.filter(function(module) {
 | |
| 				return newModules.indexOf(module) >= 0;
 | |
| 			});
 | |
| 		}
 | |
| 
 | |
| 		compilation.moduleTemplate.plugin("package", function(moduleSource, module, chunk) {
 | |
| 			if(!module.rootDuplicatesChunks || !chunk) return moduleSource;
 | |
| 			var chunkIndex = module.rootDuplicatesChunks.indexOf(chunk);
 | |
| 			if(!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) {
 | |
| 					if(a.request === b.request) return 0;
 | |
| 					return a.request < b.request ? -1 : 1;
 | |
| 				});
 | |
| 				var array = [JSON.stringify(rootDuplicates.template.id)].concat(module.getTemplateArguments(rootDuplicates.template.templateModules).map(function(module) {
 | |
| 					if(module.id === null || module.id === undefined)
 | |
| 						return "(function webpackMissingModule() { throw new Error(" + JSON.stringify("Cannot find module") + "); }())";
 | |
| 					return JSON.stringify(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("" + JSON.stringify(rootDuplicates[0].id));
 | |
| 				return source;
 | |
| 			}
 | |
| 		});
 | |
| 		compilation.plugin("chunk-hash", function(chunk, hash) {
 | |
| 			if(chunk.__DedupePluginHasDeduplicatedModules)
 | |
| 				hash.update("DedupePlugin (deduplication code)");
 | |
| 		});
 | |
| 		compilation.mainTemplate.plugin("add-module", function(source, chunk, hash, varModuleId, varModule) {
 | |
| 			// we don't need to test all nested chunks, because `__DedupePluginHasDeduplicatedModules`
 | |
| 			// is not set on entry chunks
 | |
| 			if(!chunk.__DedupePluginHasDeduplicatedModules) {
 | |
| 				return source;
 | |
| 			}
 | |
| 			return this.asString([
 | |
| 				"var _m = " + varModule + ";",
 | |
| 				"",
 | |
| 				"// Check if module is deduplicated",
 | |
| 				"switch(typeof _m) {",
 | |
| 				"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;"
 | |
| 				]),
 | |
| 				"case \"function\":",
 | |
| 				this.indent([
 | |
| 					"// Normal module",
 | |
| 					"modules[" + varModuleId + "] = _m;",
 | |
| 					"break;"
 | |
| 				]),
 | |
| 				"default:",
 | |
| 				this.indent([
 | |
| 					"// Module is a copy of another module",
 | |
| 					"modules[" + varModuleId + "] = modules[_m];",
 | |
| 					"break;"
 | |
| 				]),
 | |
| 				"}"
 | |
| 			]);
 | |
| 		});
 | |
| 		compilation.mainTemplate.plugin("modules", function(orginalSource, chunk) {
 | |
| 			if(!chunk.__DedupePluginHasDeduplicatedModules) {
 | |
| 				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([
 | |
| 					"if(Object.prototype.hasOwnProperty.call(modules, i)) {",
 | |
| 					this.indent([
 | |
| 						"switch(typeof modules[i]) {",
 | |
| 						"case \"function\": 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(this, [a,b,c].concat(args));"
 | |
| 								]),
 | |
| 								"};"
 | |
| 							]),
 | |
| 							"}(modules[i]));",
 | |
| 							"break;"
 | |
| 						]),
 | |
| 						"default:",
 | |
| 						this.indent([
 | |
| 							"// Module is a copy of another module",
 | |
| 							"modules[i] = modules[modules[i]];",
 | |
| 							"break;"
 | |
| 						]),
 | |
| 						"}"
 | |
| 					]),
 | |
| 					"}"
 | |
| 				]),
 | |
| 				"}",
 | |
| 				"return modules;"
 | |
| 			]));
 | |
| 			source.add("\n}(");
 | |
| 			source.add(orginalSource);
 | |
| 			source.add("))");
 | |
| 			return source;
 | |
| 		});
 | |
| 	});
 | |
| };
 |