mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			125 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
/*
 | 
						|
	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
						|
	Author Tobias Koppers @sokra
 | 
						|
*/
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
/** @typedef {import("../Chunk")} Chunk */
 | 
						|
/** @typedef {import("../Chunk").ChunkId} ChunkId */
 | 
						|
/** @typedef {import("../Compiler")} Compiler */
 | 
						|
/** @typedef {import("../Module")} Module */
 | 
						|
 | 
						|
class FlagIncludedChunksPlugin {
 | 
						|
	/**
 | 
						|
	 * Apply the plugin
 | 
						|
	 * @param {Compiler} compiler the compiler instance
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	apply(compiler) {
 | 
						|
		compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", compilation => {
 | 
						|
			compilation.hooks.optimizeChunkIds.tap(
 | 
						|
				"FlagIncludedChunksPlugin",
 | 
						|
				chunks => {
 | 
						|
					const chunkGraph = compilation.chunkGraph;
 | 
						|
 | 
						|
					// prepare two bit integers for each module
 | 
						|
					// 2^31 is the max number represented as SMI in v8
 | 
						|
					// we want the bits distributed this way:
 | 
						|
					// the bit 2^31 is pretty rar and only one module should get it
 | 
						|
					// so it has a probability of 1 / modulesCount
 | 
						|
					// the first bit (2^0) is the easiest and every module could get it
 | 
						|
					// if it doesn't get a better bit
 | 
						|
					// from bit 2^n to 2^(n+1) there is a probability of p
 | 
						|
					// so 1 / modulesCount == p^31
 | 
						|
					// <=> p = sqrt31(1 / modulesCount)
 | 
						|
					// so we use a modulo of 1 / sqrt31(1 / modulesCount)
 | 
						|
					/** @type {WeakMap<Module, number>} */
 | 
						|
					const moduleBits = new WeakMap();
 | 
						|
					const modulesCount = compilation.modules.size;
 | 
						|
 | 
						|
					// precalculate the modulo values for each bit
 | 
						|
					const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31);
 | 
						|
					const modulos = Array.from(
 | 
						|
						{ length: 31 },
 | 
						|
						(x, i) => Math.pow(modulo, i) | 0
 | 
						|
					);
 | 
						|
 | 
						|
					// iterate all modules to generate bit values
 | 
						|
					let i = 0;
 | 
						|
					for (const module of compilation.modules) {
 | 
						|
						let bit = 30;
 | 
						|
						while (i % modulos[bit] !== 0) {
 | 
						|
							bit--;
 | 
						|
						}
 | 
						|
						moduleBits.set(module, 1 << bit);
 | 
						|
						i++;
 | 
						|
					}
 | 
						|
 | 
						|
					// iterate all chunks to generate bitmaps
 | 
						|
					/** @type {WeakMap<Chunk, number>} */
 | 
						|
					const chunkModulesHash = new WeakMap();
 | 
						|
					for (const chunk of chunks) {
 | 
						|
						let hash = 0;
 | 
						|
						for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
 | 
						|
							hash |= /** @type {number} */ (moduleBits.get(module));
 | 
						|
						}
 | 
						|
						chunkModulesHash.set(chunk, hash);
 | 
						|
					}
 | 
						|
 | 
						|
					for (const chunkA of chunks) {
 | 
						|
						const chunkAHash =
 | 
						|
							/** @type {number} */
 | 
						|
							(chunkModulesHash.get(chunkA));
 | 
						|
						const chunkAModulesCount =
 | 
						|
							chunkGraph.getNumberOfChunkModules(chunkA);
 | 
						|
						if (chunkAModulesCount === 0) continue;
 | 
						|
						let bestModule = undefined;
 | 
						|
						for (const module of chunkGraph.getChunkModulesIterable(chunkA)) {
 | 
						|
							if (
 | 
						|
								bestModule === undefined ||
 | 
						|
								chunkGraph.getNumberOfModuleChunks(bestModule) >
 | 
						|
									chunkGraph.getNumberOfModuleChunks(module)
 | 
						|
							)
 | 
						|
								bestModule = module;
 | 
						|
						}
 | 
						|
						loopB: for (const chunkB of chunkGraph.getModuleChunksIterable(
 | 
						|
							/** @type {Module} */ (bestModule)
 | 
						|
						)) {
 | 
						|
							// as we iterate the same iterables twice
 | 
						|
							// skip if we find ourselves
 | 
						|
							if (chunkA === chunkB) continue;
 | 
						|
 | 
						|
							const chunkBModulesCount =
 | 
						|
								chunkGraph.getNumberOfChunkModules(chunkB);
 | 
						|
 | 
						|
							// ids for empty chunks are not included
 | 
						|
							if (chunkBModulesCount === 0) continue;
 | 
						|
 | 
						|
							// instead of swapping A and B just bail
 | 
						|
							// as we loop twice the current A will be B and B then A
 | 
						|
							if (chunkAModulesCount > chunkBModulesCount) continue;
 | 
						|
 | 
						|
							// is chunkA in chunkB?
 | 
						|
 | 
						|
							// we do a cheap check for the hash value
 | 
						|
							const chunkBHash =
 | 
						|
								/** @type {number} */
 | 
						|
								(chunkModulesHash.get(chunkB));
 | 
						|
							if ((chunkBHash & chunkAHash) !== chunkAHash) continue;
 | 
						|
 | 
						|
							// compare all modules
 | 
						|
							for (const m of chunkGraph.getChunkModulesIterable(chunkA)) {
 | 
						|
								if (!chunkGraph.isModuleInChunk(m, chunkB)) continue loopB;
 | 
						|
							}
 | 
						|
							/** @type {ChunkId[]} */
 | 
						|
							(chunkB.ids).push(/** @type {ChunkId} */ (chunkA.id));
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			);
 | 
						|
		});
 | 
						|
	}
 | 
						|
}
 | 
						|
module.exports = FlagIncludedChunksPlugin;
 |