mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			229 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			229 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
/*
 | 
						|
	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("after-plugins", function(compiler) {
 | 
						|
		compiler.moduleTemplate = new DedupModuleTemplateDecorator(compiler.moduleTemplate);
 | 
						|
		compiler.mainTemplate = Object.create(compiler.mainTemplate);
 | 
						|
		var oldRenderAddModule = compiler.mainTemplate.renderAddModule;
 | 
						|
		compiler.mainTemplate.renderAddModule = function(hash, chunk, varModuleId, varModule) {
 | 
						|
			if(!chunk._DedupePlugin_hasDeduplicatedModules) {
 | 
						|
				return oldRenderAddModule.call(this, hash, chunk, varModuleId, varModule);
 | 
						|
			}
 | 
						|
			return [
 | 
						|
				"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), fn = modules[_m[0]];",
 | 
						|
						"return function (a,b,c) {",
 | 
						|
						this.indent([
 | 
						|
							"fn.apply(this, [a,b,c].concat(args));"
 | 
						|
						]),
 | 
						|
						"};"
 | 
						|
					]),
 | 
						|
					"}(_m));",
 | 
						|
					"break;"
 | 
						|
				]),
 | 
						|
				"default:",
 | 
						|
				this.indent([
 | 
						|
					"// Normal module",
 | 
						|
					"modules[" + varModuleId + "] = _m;"
 | 
						|
				]),
 | 
						|
				"}"
 | 
						|
			]
 | 
						|
		};
 | 
						|
		var oldRenderModules = compiler.mainTemplate.renderModules;
 | 
						|
		compiler.mainTemplate.renderModules = function renderModules(hash, chunk, moduleTemplate, dependencyTemplates) {
 | 
						|
			if(!chunk._DedupePlugin_hasDeduplicatedModules) {
 | 
						|
				return oldRenderModules.call(this, hash, chunk, moduleTemplate, dependencyTemplates);
 | 
						|
			}
 | 
						|
			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(oldRenderModules.call(this, hash, chunk, moduleTemplate, dependencyTemplates));
 | 
						|
			source.add("))");
 | 
						|
			return source;
 | 
						|
		};
 | 
						|
	});
 | 
						|
};
 | 
						|
 | 
						|
function DedupModuleTemplateDecorator(template) {
 | 
						|
	this.template = template;
 | 
						|
}
 | 
						|
 | 
						|
DedupModuleTemplateDecorator.prototype.render = function(module, dependencyTemplates, chunk) {
 | 
						|
	if(!module.rootDuplicatesChunks || !chunk) return this.template.render(module, dependencyTemplates, chunk);
 | 
						|
	var chunkIndex = module.rootDuplicatesChunks.indexOf(chunk);
 | 
						|
	if(!module.rootDuplicates || !module.rootDuplicates[chunkIndex]) return this.template.render(module, dependencyTemplates, chunk);
 | 
						|
	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 = [rootDuplicates.template.id].concat(module.getTemplateArguments(rootDuplicates.template.templateModules).map(function(module) {
 | 
						|
			if(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 this.template.render(module, dependencyTemplates, chunk);
 | 
						|
		var source = new ConcatSource("" + rootDuplicates[0].id);
 | 
						|
		return source;
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
DedupModuleTemplateDecorator.prototype.updateHash = function(hash) {
 | 
						|
	hash.update("DedupModuleTemplateDecorator");
 | 
						|
	this.template.updateHash(hash);
 | 
						|
}; |