| 
									
										
										
										
											2017-08-08 15:40:17 +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-08-08 15:40:17 +08:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | const glob2regexp = require("glob-to-regexp"); | 
					
						
							| 
									
										
										
										
											2018-07-31 04:30:27 +08:00
										 |  |  | const { STAGE_DEFAULT } = require("../OptimizationStages"); | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency"); | 
					
						
							| 
									
										
										
										
											2019-10-30 04:37:59 +08:00
										 |  |  | const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency"); | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-20 22:24:35 +08:00
										 |  |  | /** @typedef {import("../Compiler")} Compiler */ | 
					
						
							| 
									
										
										
										
											2018-05-27 05:07:02 +08:00
										 |  |  | /** @typedef {import("../Dependency")} Dependency */ | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | /** @typedef {import("../Module")} Module */ | 
					
						
							| 
									
										
										
										
											2018-05-27 05:07:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ExportInModule | 
					
						
							|  |  |  |  * @property {Module} module the module | 
					
						
							|  |  |  |  * @property {string} exportName the name of the export | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | /** @type {WeakMap<any, Map<string, RegExp>>} */ | 
					
						
							|  |  |  | const globToRegexpCache = new WeakMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {string} glob the pattern | 
					
						
							|  |  |  |  * @param {Map<string, RegExp>} cache the glob to RegExp cache | 
					
						
							|  |  |  |  * @returns {RegExp} a regular expression | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const globToRegexp = (glob, cache) => { | 
					
						
							|  |  |  | 	const cacheEntry = cache.get(glob); | 
					
						
							|  |  |  | 	if (cacheEntry !== undefined) return cacheEntry; | 
					
						
							|  |  |  | 	if (!glob.includes("/")) { | 
					
						
							|  |  |  | 		glob = `**/${glob}`; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	const baseRegexp = glob2regexp(glob, { globstar: true, extended: true }); | 
					
						
							|  |  |  | 	const regexpSource = baseRegexp.source; | 
					
						
							|  |  |  | 	const regexp = new RegExp("^(\\./)?" + regexpSource.slice(1)); | 
					
						
							|  |  |  | 	cache.set(glob, regexp); | 
					
						
							|  |  |  | 	return regexp; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-14 19:35:25 +08:00
										 |  |  | class SideEffectsFlagPlugin { | 
					
						
							| 
									
										
										
										
											2018-07-20 22:24:35 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compiler} compiler webpack compiler | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 	apply(compiler) { | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | 		let cache = globToRegexpCache.get(compiler.root); | 
					
						
							|  |  |  | 		if (cache === undefined) { | 
					
						
							|  |  |  | 			cache = new Map(); | 
					
						
							|  |  |  | 			globToRegexpCache.set(compiler.root, cache); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => { | 
					
						
							| 
									
										
										
										
											2017-12-14 04:35:39 +08:00
										 |  |  | 			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => { | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 				const resolveData = data.resourceResolveData; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				if ( | 
					
						
							|  |  |  | 					resolveData && | 
					
						
							|  |  |  | 					resolveData.descriptionFileData && | 
					
						
							|  |  |  | 					resolveData.relativePath | 
					
						
							|  |  |  | 				) { | 
					
						
							| 
									
										
										
										
											2017-10-12 23:32:41 +08:00
										 |  |  | 					const sideEffects = resolveData.descriptionFileData.sideEffects; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects( | 
					
						
							|  |  |  | 						resolveData.relativePath, | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | 						sideEffects, | 
					
						
							|  |  |  | 						cache | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 					if (!hasSideEffects) { | 
					
						
							| 
									
										
										
										
											2019-11-08 19:52:32 +08:00
										 |  |  | 						if (module.factoryMeta === undefined) { | 
					
						
							|  |  |  | 							module.factoryMeta = {}; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2017-12-06 19:09:17 +08:00
										 |  |  | 						module.factoryMeta.sideEffectFree = true; | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return module; | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-12-06 19:09:34 +08:00
										 |  |  | 			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => { | 
					
						
							| 
									
										
										
										
											2018-05-29 20:50:40 +08:00
										 |  |  | 				if (data.settings.sideEffects === false) { | 
					
						
							| 
									
										
										
										
											2019-11-08 19:52:32 +08:00
										 |  |  | 					if (module.factoryMeta === undefined) { | 
					
						
							|  |  |  | 						module.factoryMeta = {}; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-12-06 19:09:34 +08:00
										 |  |  | 					module.factoryMeta.sideEffectFree = true; | 
					
						
							| 
									
										
										
										
											2018-05-29 20:50:40 +08:00
										 |  |  | 				} else if (data.settings.sideEffects === true) { | 
					
						
							| 
									
										
										
										
											2019-11-08 19:52:32 +08:00
										 |  |  | 					if (module.factoryMeta !== undefined) { | 
					
						
							|  |  |  | 						module.factoryMeta.sideEffectFree = false; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-05-29 20:50:40 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 				return module; | 
					
						
							| 
									
										
										
										
											2017-12-06 19:09:34 +08:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => { | 
					
						
							| 
									
										
										
										
											2018-07-24 23:35:36 +08:00
										 |  |  | 			const moduleGraph = compilation.moduleGraph; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			compilation.hooks.optimizeDependencies.tap( | 
					
						
							| 
									
										
										
										
											2018-12-09 19:54:17 +08:00
										 |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2018-07-31 04:30:27 +08:00
										 |  |  | 					name: "SideEffectsFlagPlugin", | 
					
						
							|  |  |  | 					stage: STAGE_DEFAULT | 
					
						
							| 
									
										
										
										
											2018-12-09 19:54:17 +08:00
										 |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				modules => { | 
					
						
							| 
									
										
										
										
											2018-05-27 05:07:02 +08:00
										 |  |  | 					/** @type {Map<Module, Map<string, ExportInModule>>} */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					const reexportMaps = new Map(); | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					// Capture reexports of sideEffectFree modules
 | 
					
						
							|  |  |  | 					for (const module of modules) { | 
					
						
							|  |  |  | 						for (const dep of module.dependencies) { | 
					
						
							| 
									
										
										
										
											2018-07-24 23:35:11 +08:00
										 |  |  | 							if (dep instanceof HarmonyExportImportedSpecifierDependency) { | 
					
						
							| 
									
										
										
										
											2019-11-08 19:52:32 +08:00
										 |  |  | 								if ( | 
					
						
							|  |  |  | 									module.factoryMeta !== undefined && | 
					
						
							|  |  |  | 									module.factoryMeta.sideEffectFree | 
					
						
							|  |  |  | 								) { | 
					
						
							| 
									
										
										
										
											2018-07-24 23:35:36 +08:00
										 |  |  | 									const mode = dep.getMode(moduleGraph, true); | 
					
						
							| 
									
										
										
										
											2019-02-01 18:45:24 +08:00
										 |  |  | 									if (mode.type === "normal-reexport") { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 										let map = reexportMaps.get(module); | 
					
						
							|  |  |  | 										if (!map) { | 
					
						
							|  |  |  | 											reexportMaps.set(module, (map = new Map())); | 
					
						
							|  |  |  | 										} | 
					
						
							| 
									
										
										
										
											2019-03-14 19:06:59 +08:00
										 |  |  | 										for (const [key, ids] of mode.map) { | 
					
						
							| 
									
										
										
										
											2019-10-30 16:25:35 +08:00
										 |  |  | 											// TODO Support reexporting namespace object
 | 
					
						
							| 
									
										
										
										
											2019-03-14 19:06:59 +08:00
										 |  |  | 											if (ids.length > 0 && !mode.checked.has(key)) { | 
					
						
							| 
									
										
										
										
											2019-02-01 18:45:24 +08:00
										 |  |  | 												map.set(key, { | 
					
						
							| 
									
										
										
										
											2019-10-30 04:37:59 +08:00
										 |  |  | 													module: moduleGraph.getModule(dep), | 
					
						
							| 
									
										
										
										
											2019-03-14 19:06:59 +08:00
										 |  |  | 													exportName: ids[0] | 
					
						
							| 
									
										
										
										
											2019-02-01 18:45:24 +08:00
										 |  |  | 												}); | 
					
						
							|  |  |  | 											} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 										} | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 									} | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2019-10-30 04:37:59 +08:00
										 |  |  | 							} else if (dep instanceof HarmonyImportSideEffectDependency) { | 
					
						
							|  |  |  | 								const connection = moduleGraph.getConnection(dep); | 
					
						
							|  |  |  | 								if (connection) { | 
					
						
							| 
									
										
										
										
											2019-10-30 16:34:39 +08:00
										 |  |  | 									const refModule = connection.resolvedModule; | 
					
						
							| 
									
										
										
										
											2019-11-08 19:52:32 +08:00
										 |  |  | 									if ( | 
					
						
							|  |  |  | 										refModule && | 
					
						
							|  |  |  | 										refModule.factoryMeta !== undefined && | 
					
						
							|  |  |  | 										refModule.factoryMeta.sideEffectFree | 
					
						
							|  |  |  | 									) { | 
					
						
							| 
									
										
										
										
											2019-10-30 16:34:39 +08:00
										 |  |  | 										connection.active = false; | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2019-10-30 04:37:59 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					// Flatten reexports
 | 
					
						
							|  |  |  | 					for (const map of reexportMaps.values()) { | 
					
						
							|  |  |  | 						for (const pair of map) { | 
					
						
							|  |  |  | 							let mapping = pair[1]; | 
					
						
							|  |  |  | 							while (mapping) { | 
					
						
							|  |  |  | 								const innerMap = reexportMaps.get(mapping.module); | 
					
						
							|  |  |  | 								if (!innerMap) break; | 
					
						
							|  |  |  | 								const newMapping = innerMap.get(mapping.exportName); | 
					
						
							|  |  |  | 								if (newMapping) { | 
					
						
							|  |  |  | 									map.set(pair[0], newMapping); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								mapping = newMapping; | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					// Update imports along the reexports from sideEffectFree modules
 | 
					
						
							| 
									
										
										
										
											2019-10-30 16:25:35 +08:00
										 |  |  | 					for (const [module, map] of reexportMaps) { | 
					
						
							| 
									
										
										
										
											2018-07-24 21:30:37 +08:00
										 |  |  | 						for (const connection of moduleGraph.getIncomingConnections( | 
					
						
							|  |  |  | 							module | 
					
						
							|  |  |  | 						)) { | 
					
						
							|  |  |  | 							const dep = connection.dependency; | 
					
						
							| 
									
										
										
										
											2018-05-28 03:47:58 +08:00
										 |  |  | 							if ( | 
					
						
							| 
									
										
										
										
											2019-05-20 17:09:45 +08:00
										 |  |  | 								dep instanceof HarmonyExportImportedSpecifierDependency || | 
					
						
							|  |  |  | 								(dep instanceof HarmonyImportSpecifierDependency && | 
					
						
							|  |  |  | 									!dep.namespaceObjectAsContext) | 
					
						
							| 
									
										
										
										
											2018-05-28 03:47:58 +08:00
										 |  |  | 							) { | 
					
						
							| 
									
										
										
										
											2019-03-14 19:06:59 +08:00
										 |  |  | 								// TODO improve for nested imports
 | 
					
						
							|  |  |  | 								const ids = dep.getIds(moduleGraph); | 
					
						
							|  |  |  | 								if (ids.length > 0) { | 
					
						
							|  |  |  | 									const mapping = map.get(ids[0]); | 
					
						
							|  |  |  | 									if (mapping) { | 
					
						
							|  |  |  | 										moduleGraph.updateModule(dep, mapping.module); | 
					
						
							|  |  |  | 										moduleGraph.addExplanation( | 
					
						
							|  |  |  | 											dep, | 
					
						
							|  |  |  | 											"(skipped side-effect-free modules)" | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 										dep.setIds( | 
					
						
							|  |  |  | 											moduleGraph, | 
					
						
							|  |  |  | 											mapping.exportName | 
					
						
							|  |  |  | 												? [mapping.exportName, ...ids.slice(1)] | 
					
						
							|  |  |  | 												: ids.slice(1) | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 										continue; | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			); | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-19 00:14:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | 	static moduleHasSideEffects(moduleName, flagValue, cache) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		switch (typeof flagValue) { | 
					
						
							| 
									
										
										
										
											2018-01-19 00:14:19 +08:00
										 |  |  | 			case "undefined": | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			case "boolean": | 
					
						
							|  |  |  | 				return flagValue; | 
					
						
							|  |  |  | 			case "string": | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | 				return globToRegexp(flagValue, cache).test(moduleName); | 
					
						
							| 
									
										
										
										
											2018-01-19 00:14:19 +08:00
										 |  |  | 			case "object": | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				return flagValue.some(glob => | 
					
						
							| 
									
										
										
										
											2019-05-17 03:12:59 +08:00
										 |  |  | 					SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob, cache) | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				); | 
					
						
							| 
									
										
										
										
											2018-01-19 00:14:19 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-08 15:40:17 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-14 19:35:25 +08:00
										 |  |  | module.exports = SideEffectsFlagPlugin; |