| 
									
										
										
										
											2013-01-31 01:49:25 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-05 13:42:36 +08:00
										 |  |  | "use strict"; | 
					
						
							| 
									
										
										
										
											2013-01-31 01:49:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 00:15:33 +08:00
										 |  |  | const { STAGE_BASIC } = require("../OptimizationStages"); | 
					
						
							| 
									
										
										
										
											2024-11-13 00:42:30 +08:00
										 |  |  | const createSchemaValidation = require("../util/create-schema-validation"); | 
					
						
							| 
									
										
										
										
											2020-07-28 00:09:48 +08:00
										 |  |  | const { runtimeEqual } = require("../util/runtime"); | 
					
						
							| 
									
										
										
										
											2018-07-31 04:30:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 01:09:58 +08:00
										 |  |  | /** @typedef {import("../../declarations/plugins/optimize/MergeDuplicateChunksPlugin").MergeDuplicateChunksPluginOptions} MergeDuplicateChunksPluginOptions */ | 
					
						
							| 
									
										
										
										
											2018-08-14 17:18:22 +08:00
										 |  |  | /** @typedef {import("../Compiler")} Compiler */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-13 00:42:30 +08:00
										 |  |  | const validate = createSchemaValidation( | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 	require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.check"), | 
					
						
							| 
									
										
										
										
											2024-11-13 00:42:30 +08:00
										 |  |  | 	() => | 
					
						
							|  |  |  | 		require("../../schemas/plugins/optimize/MergeDuplicateChunksPlugin.json"), | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		name: "Merge Duplicate Chunks Plugin", | 
					
						
							|  |  |  | 		baseDataPath: "options" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | const PLUGIN_NAME = "MergeDuplicateChunksPlugin"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-05 13:42:36 +08:00
										 |  |  | class MergeDuplicateChunksPlugin { | 
					
						
							| 
									
										
										
										
											2024-11-13 01:09:58 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {MergeDuplicateChunksPluginOptions} options options object | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2024-11-13 00:42:30 +08:00
										 |  |  | 	constructor(options = { stage: STAGE_BASIC }) { | 
					
						
							|  |  |  | 		validate(options); | 
					
						
							|  |  |  | 		this.options = options; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-14 17:18:22 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compiler} compiler the compiler | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-01-05 13:42:36 +08:00
										 |  |  | 	apply(compiler) { | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 		compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => { | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 			compilation.hooks.optimizeChunks.tap( | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					name: PLUGIN_NAME, | 
					
						
							|  |  |  | 					stage: this.options.stage | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 				(chunks) => { | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 					const { chunkGraph, moduleGraph } = compilation; | 
					
						
							| 
									
										
										
										
											2018-08-14 17:18:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 					// remember already tested chunks for performance
 | 
					
						
							|  |  |  | 					const notDuplicates = new Set(); | 
					
						
							| 
									
										
										
										
											2017-11-24 15:55:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 					// for each chunk
 | 
					
						
							|  |  |  | 					for (const chunk of chunks) { | 
					
						
							|  |  |  | 						// track a Set of all chunk that could be duplicates
 | 
					
						
							|  |  |  | 						let possibleDuplicates; | 
					
						
							|  |  |  | 						for (const module of chunkGraph.getChunkModulesIterable(chunk)) { | 
					
						
							|  |  |  | 							if (possibleDuplicates === undefined) { | 
					
						
							|  |  |  | 								// when possibleDuplicates is not yet set,
 | 
					
						
							|  |  |  | 								// create a new Set from chunks of the current module
 | 
					
						
							|  |  |  | 								// including only chunks with the same number of modules
 | 
					
						
							|  |  |  | 								for (const dup of chunkGraph.getModuleChunksIterable(module)) { | 
					
						
							|  |  |  | 									if ( | 
					
						
							|  |  |  | 										dup !== chunk && | 
					
						
							|  |  |  | 										chunkGraph.getNumberOfChunkModules(chunk) === | 
					
						
							|  |  |  | 											chunkGraph.getNumberOfChunkModules(dup) && | 
					
						
							|  |  |  | 										!notDuplicates.has(dup) | 
					
						
							|  |  |  | 									) { | 
					
						
							|  |  |  | 										// delay allocating the new Set until here, reduce memory pressure
 | 
					
						
							|  |  |  | 										if (possibleDuplicates === undefined) { | 
					
						
							|  |  |  | 											possibleDuplicates = new Set(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 										} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 										possibleDuplicates.add(dup); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 									} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 								} | 
					
						
							|  |  |  | 								// when no chunk is possible we can break here
 | 
					
						
							|  |  |  | 								if (possibleDuplicates === undefined) break; | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								// validate existing possible duplicates
 | 
					
						
							|  |  |  | 								for (const dup of possibleDuplicates) { | 
					
						
							|  |  |  | 									// remove possible duplicate when module is not contained
 | 
					
						
							|  |  |  | 									if (!chunkGraph.isModuleInChunk(module, dup)) { | 
					
						
							|  |  |  | 										possibleDuplicates.delete(dup); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 									} | 
					
						
							| 
									
										
										
										
											2017-11-24 15:55:22 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 								// when all chunks has been removed we can break here
 | 
					
						
							|  |  |  | 								if (possibleDuplicates.size === 0) break; | 
					
						
							| 
									
										
										
										
											2017-11-24 15:55:22 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 						// when we found duplicates
 | 
					
						
							|  |  |  | 						if ( | 
					
						
							|  |  |  | 							possibleDuplicates !== undefined && | 
					
						
							|  |  |  | 							possibleDuplicates.size > 0 | 
					
						
							|  |  |  | 						) { | 
					
						
							|  |  |  | 							outer: for (const otherChunk of possibleDuplicates) { | 
					
						
							|  |  |  | 								if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue; | 
					
						
							|  |  |  | 								if (chunkGraph.getNumberOfEntryModules(chunk) > 0) continue; | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 								if (chunkGraph.getNumberOfEntryModules(otherChunk) > 0) { | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 									continue; | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 								if (!runtimeEqual(chunk.runtime, otherChunk.runtime)) { | 
					
						
							|  |  |  | 									for (const module of chunkGraph.getChunkModulesIterable( | 
					
						
							|  |  |  | 										chunk | 
					
						
							|  |  |  | 									)) { | 
					
						
							|  |  |  | 										const exportsInfo = moduleGraph.getExportsInfo(module); | 
					
						
							|  |  |  | 										if ( | 
					
						
							|  |  |  | 											!exportsInfo.isEquallyUsed( | 
					
						
							|  |  |  | 												chunk.runtime, | 
					
						
							|  |  |  | 												otherChunk.runtime | 
					
						
							|  |  |  | 											) | 
					
						
							|  |  |  | 										) { | 
					
						
							|  |  |  | 											continue outer; | 
					
						
							| 
									
										
										
										
											2020-07-28 00:09:48 +08:00
										 |  |  | 										} | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 								} | 
					
						
							|  |  |  | 								// merge them
 | 
					
						
							|  |  |  | 								if (chunkGraph.canChunksBeIntegrated(chunk, otherChunk)) { | 
					
						
							|  |  |  | 									chunkGraph.integrateChunks(chunk, otherChunk); | 
					
						
							|  |  |  | 									compilation.chunks.delete(otherChunk); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2017-11-24 15:55:22 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 						// don't check already processed chunks twice
 | 
					
						
							|  |  |  | 						notDuplicates.add(chunk); | 
					
						
							| 
									
										
										
										
											2017-11-24 15:55:22 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2025-06-04 02:20:37 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2017-01-05 13:42:36 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-05 13:42:36 +08:00
										 |  |  | module.exports = MergeDuplicateChunksPlugin; |