mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			201 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| var parse = require("./parse");
 | |
| var resolve = require("./resolve");
 | |
| var fs = require("fs");
 | |
| var path = require("path");
 | |
| 
 | |
| /**
 | |
|  * context: current directory
 | |
|  * mainModule: the entrance module
 | |
|  * options:
 | |
|  * callback: function(err, result)
 | |
|  */
 | |
| module.exports = function buildDeps(context, mainModule, options, callback) {
 | |
| 	if(!callback) {
 | |
| 		callback = options;
 | |
| 		options = {};
 | |
| 	}
 | |
| 	if(!options) options = {};
 | |
| 
 | |
| 	var depTree = {
 | |
| 		modules: {},
 | |
| 		modulesById: {},
 | |
| 		chunks: {},
 | |
| 		nextModuleId: 0,
 | |
| 		nextChunkId: 0,
 | |
| 		chunkModules: {} // used by checkObsolete
 | |
| 	}
 | |
| 	var mainModuleId;
 | |
| 	addModule(depTree, context, mainModule, options, function(err, id) {
 | |
| 		if(err) {
 | |
| 			callback(err);
 | |
| 			return;
 | |
| 		}
 | |
| 		mainModuleId = id;
 | |
| 		buildTree();
 | |
| 	});
 | |
| 	function buildTree() {
 | |
| 		addChunk(depTree, depTree.modulesById[mainModuleId], options);
 | |
| 		for(var chunkId in depTree.chunks) {
 | |
| 			removeParentsModules(depTree, depTree.chunks[chunkId]);
 | |
| 			removeChunkIfEmpty(depTree, depTree.chunks[chunkId]);
 | |
| 			checkObsolete(depTree, depTree.chunks[chunkId]);
 | |
| 		}
 | |
| 		callback(null, depTree);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function addModule(depTree, context, module, options, callback) {
 | |
| 	resolve(context, module, options.resolve, function(err, filename) {
 | |
| 		if(err) {
 | |
| 			callback(err);
 | |
| 			return;
 | |
| 		}
 | |
| 		if(depTree.modules[filename]) {
 | |
| 			callback(null, depTree.modules[filename].id);
 | |
| 		} else {
 | |
| 			var module = depTree.modules[filename] = {
 | |
| 				id: depTree.nextModuleId++,
 | |
| 				filename: filename
 | |
| 			};
 | |
| 			depTree.modulesById[module.id] = module;
 | |
| 			fs.readFile(filename, "utf-8", function(err, source) {
 | |
| 				if(err) {
 | |
| 					callback(err);
 | |
| 					return;
 | |
| 				}
 | |
| 				var deps = parse(source);
 | |
| 				module.requires = deps.requires || [];
 | |
| 				module.asyncs = deps.asyncs || [];
 | |
| 				module.source = source;
 | |
| 
 | |
| 				var requires = {};
 | |
| 				function add(r) {
 | |
| 					requires[r.name] = requires[r.name] || [];
 | |
| 					requires[r.name].push(r);
 | |
| 				}
 | |
| 				if(module.requires)
 | |
| 					module.requires.forEach(add);
 | |
| 				if(module.asyncs)
 | |
| 					module.asyncs.forEach(function addContext(c) {
 | |
| 						if(c.requires)
 | |
| 							c.requires.forEach(add);
 | |
| 						if(c.asyncs)
 | |
| 							c.asyncs.forEach(addContext);
 | |
| 					});
 | |
| 				requiresNames = Object.keys(requires);
 | |
| 				var count = requiresNames.length;
 | |
| 				var errors = [];
 | |
| 				if(requiresNames.length)
 | |
| 					requiresNames.forEach(function(moduleName) {
 | |
| 						addModule(depTree, path.dirname(filename), moduleName, options, function(err, moduleId) {
 | |
| 							if(err) {
 | |
| 								errors.push(err+"\n @ " + filename + " (line " + requires[moduleName][0].line + ", column " + requires[moduleName][0].column + ")");
 | |
| 							} else {
 | |
| 								requires[moduleName].forEach(function(requireItem) {
 | |
| 									requireItem.id = moduleId;
 | |
| 								});
 | |
| 							}
 | |
| 							count--;
 | |
| 							if(count === 0) {
 | |
| 								if(errors.length) {
 | |
| 									callback(errors.join("\n"));
 | |
| 								} else {
 | |
| 									end();
 | |
| 								}
 | |
| 							}
 | |
| 						});
 | |
| 					});
 | |
| 				else end()
 | |
| 				function end() {
 | |
| 					callback(null, module.id);
 | |
| 				}
 | |
| 			});
 | |
| 		}
 | |
| 	});
 | |
| }
 | |
| 
 | |
| function addChunk(depTree, chunkStartpoint, options) {
 | |
| 	var chunk = {
 | |
| 		id: depTree.nextChunkId++,
 | |
| 		modules: {},
 | |
| 		context: chunkStartpoint
 | |
| 	};
 | |
| 	depTree.chunks[chunk.id] = chunk;
 | |
| 	if(chunkStartpoint) {
 | |
| 		chunkStartpoint.chunkId = chunk.id;
 | |
| 		addModuleToChunk(depTree, chunkStartpoint, chunk.id, options);
 | |
| 	}
 | |
| 	return chunk;
 | |
| }
 | |
| 
 | |
| function addModuleToChunk(depTree, context, chunkId, options) {
 | |
| 	context.chunks = context.chunks || [];
 | |
| 	if(context.chunks.indexOf(chunkId) === -1) {
 | |
| 		context.chunks.push(chunkId);
 | |
| 		if(context.id !== undefined)
 | |
| 			depTree.chunks[chunkId].modules[context.id] = "include";
 | |
| 		if(context.requires) {
 | |
| 			context.requires.forEach(function(requireItem) {
 | |
| 				addModuleToChunk(depTree, depTree.modulesById[requireItem.id], chunkId, options);
 | |
| 			});
 | |
| 		}
 | |
| 		if(context.asyncs) {
 | |
| 			context.asyncs.forEach(function(context) {
 | |
| 				var subChunk
 | |
| 				if(context.chunkId) {
 | |
| 					subChunk = depTree.chunks[context.chunkId];
 | |
| 				} else {
 | |
| 					subChunk = addChunk(depTree, context, options);
 | |
| 				}
 | |
| 				subChunk.parents = subChunk.parents || [];
 | |
| 				subChunk.parents.push(chunkId);
 | |
| 			});
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function removeParentsModules(depTree, chunk) {
 | |
| 	if(!chunk.parents) return;
 | |
| 	for(var moduleId in chunk.modules) {
 | |
| 		var inParent = false;
 | |
| 		chunk.parents.forEach(function(parentId) {
 | |
| 			if(depTree.chunks[parentId].modules[moduleId])
 | |
| 				inParent = true;
 | |
| 		});
 | |
| 		if(inParent) {
 | |
| 			chunk.modules[moduleId] = "in-parent";
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function removeChunkIfEmpty(depTree, chunk) {
 | |
| 	var hasModules = false;
 | |
| 	for(var moduleId in chunk.modules) {
 | |
| 		if(chunk.modules[moduleId] === "include") {
 | |
| 			hasModules = true;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	if(!hasModules) {
 | |
| 		chunk.context.chunkId = null;
 | |
| 		chunk.empty = true;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function checkObsolete(depTree, chunk) {
 | |
| 	var modules = [];
 | |
| 	for(var moduleId in chunk.modules) {
 | |
| 		if(chunk.modules[moduleId] === "include") {
 | |
| 			modules.push(moduleId);
 | |
| 		}
 | |
| 	}
 | |
| 	if(modules.length === 0) return;
 | |
| 	modules.sort();
 | |
| 	var moduleString = modules.join(" ");
 | |
| 	if(depTree.chunkModules[moduleString]) {
 | |
| 		chunk.equals = depTree.chunkModules[moduleString];
 | |
| 		if(chunk.context)
 | |
| 			chunk.context.chunkId = chunk.equals;
 | |
| 	} else
 | |
| 		depTree.chunkModules[moduleString] = chunk.id;
 | |
| } |