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; | ||
|  | } |