| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const mm = require("micromatch");const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency");const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency");/** @typedef {import("../Module")} Module *//** @typedef {import("../Dependency")} Dependency *//** * @typedef {Object} ExportInModule * @property {Module} module the module * @property {string} exportName the name of the export * @property {boolean} checked if the export is conditional *//** * @typedef {Object} ReexportInfo * @property {Map<string, ExportInModule[]>} static * @property {Map<Module, Set<string>>} dynamic *//** * @param {ReexportInfo} info info object * @param {string} exportName name of export * @returns {ExportInModule | undefined} static export */const getMappingFromInfo = (info, exportName) => {	const staticMappings = info.static.get(exportName);	if (staticMappings !== undefined) {		if (staticMappings.length === 1) return staticMappings[0];		return undefined;	}	const dynamicMappings = Array.from(info.dynamic).filter(		([_, ignored]) => !ignored.has(exportName)	);	if (dynamicMappings.length === 1) {		return {			module: dynamicMappings[0][0],			exportName,			checked: true		};	}	return undefined;};/** * @param {ReexportInfo} info info object * @param {string} exportName name of export of source module * @param {Module} module the target module * @param {string} innerExportName name of export of target module * @param {boolean} checked true, if existence of target module is checked */const addStaticReexport = (	info,	exportName,	module,	innerExportName,	checked) => {	let mappings = info.static.get(exportName);	if (mappings !== undefined) {		for (const mapping of mappings) {			if (mapping.module === module && mapping.exportName === innerExportName) {				mapping.checked = mapping.checked && checked;				return;			}		}	} else {		mappings = [];		info.static.set(exportName, mappings);	}	mappings.push({		module,		exportName: innerExportName,		checked	});};/** * @param {ReexportInfo} info info object * @param {Module} module the reexport module * @param {Set<string>} ignored ignore list * @returns {void} */const addDynamicReexport = (info, module, ignored) => {	const existingList = info.dynamic.get(module);	if (existingList !== undefined) {		for (const key of existingList) {			if (!ignored.has(key)) existingList.delete(key);		}	} else {		info.dynamic.set(module, new Set(ignored));	}};class SideEffectsFlagPlugin {	apply(compiler) {		compiler.hooks.normalModuleFactory.tap("SideEffectsFlagPlugin", nmf => {			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {				const resolveData = data.resourceResolveData;				if (					resolveData &&					resolveData.descriptionFileData &&					resolveData.relativePath				) {					const sideEffects = resolveData.descriptionFileData.sideEffects;					const hasSideEffects = SideEffectsFlagPlugin.moduleHasSideEffects(						resolveData.relativePath,						sideEffects					);					if (!hasSideEffects) {						module.factoryMeta.sideEffectFree = true;					}				}				return module;			});			nmf.hooks.module.tap("SideEffectsFlagPlugin", (module, data) => {				if (data.settings.sideEffects === false) {					module.factoryMeta.sideEffectFree = true;				} else if (data.settings.sideEffects === true) {					module.factoryMeta.sideEffectFree = false;				}			});		});		compiler.hooks.compilation.tap("SideEffectsFlagPlugin", compilation => {			compilation.hooks.optimizeDependencies.tap(				"SideEffectsFlagPlugin",				modules => {					/** @type {Map<Module, ReexportInfo>} */					const reexportMaps = new Map();					// Capture reexports of sideEffectFree modules					for (const module of modules) {						/** @type {Dependency[]} */						const removeDependencies = [];						for (const dep of module.dependencies) {							if (dep instanceof HarmonyImportSideEffectDependency) {								if (dep.module && dep.module.factoryMeta.sideEffectFree) {									removeDependencies.push(dep);								}							} else if (								dep instanceof HarmonyExportImportedSpecifierDependency							) {								if (module.factoryMeta.sideEffectFree) {									const mode = dep.getMode(true);									if (										mode.type === "safe-reexport" ||										mode.type === "checked-reexport" ||										mode.type === "dynamic-reexport" ||										mode.type === "reexport-non-harmony-default" ||										mode.type === "reexport-non-harmony-default-strict" ||										mode.type === "reexport-named-default"									) {										let info = reexportMaps.get(module);										if (!info) {											reexportMaps.set(												module,												(info = {													static: new Map(),													dynamic: new Map()												})											);										}										const targetModule = dep._module;										switch (mode.type) {											case "safe-reexport":												for (const [key, id] of mode.map) {													if (id) {														addStaticReexport(															info,															key,															targetModule,															id,															false														);													}												}												break;											case "checked-reexport":												for (const [key, id] of mode.map) {													if (id) {														addStaticReexport(															info,															key,															targetModule,															id,															true														);													}												}												break;											case "dynamic-reexport":												addDynamicReexport(info, targetModule, mode.ignored);												break;											case "reexport-non-harmony-default":											case "reexport-non-harmony-default-strict":											case "reexport-named-default":												addStaticReexport(													info,													mode.name,													targetModule,													"default",													false												);												break;										}									}								}							}						}					}					// Flatten reexports					for (const info of reexportMaps.values()) {						const dynamicReexports = info.dynamic;						info.dynamic = new Map();						for (const reexport of dynamicReexports) {							let [targetModule, ignored] = reexport;							for (;;) {								const innerInfo = reexportMaps.get(targetModule);								if (!innerInfo) break;								for (const [key, reexports] of innerInfo.static) {									if (ignored.has(key)) continue;									for (const { module, exportName, checked } of reexports) {										addStaticReexport(info, key, module, exportName, checked);									}								}								// Follow dynamic reexport if there is only one								if (innerInfo.dynamic.size !== 1) {									// When there are more then one, we don't know which one									break;								}								ignored = new Set(ignored);								for (const [innerModule, innerIgnored] of innerInfo.dynamic) {									for (const key of innerIgnored) {										if (ignored.has(key)) continue;										// This reexports ends here										addStaticReexport(info, key, targetModule, key, true);										ignored.add(key);									}									targetModule = innerModule;								}							}							// Update reexport as all other cases has been handled							addDynamicReexport(info, targetModule, ignored);						}					}					for (const info of reexportMaps.values()) {						const staticReexports = info.static;						info.static = new Map();						for (const [key, reexports] of staticReexports) {							for (let mapping of reexports) {								for (;;) {									const innerInfo = reexportMaps.get(mapping.module);									if (!innerInfo) break;									const newMapping = getMappingFromInfo(										innerInfo,										mapping.exportName									);									if (!newMapping) break;									mapping = newMapping;								}								addStaticReexport(									info,									key,									mapping.module,									mapping.exportName,									mapping.checked								);							}						}					}					// Update imports along the reexports from sideEffectFree modules					for (const pair of reexportMaps) {						const module = pair[0];						const info = pair[1];						let newReasons = undefined;						for (let i = 0; i < module.reasons.length; i++) {							const reason = module.reasons[i];							const dep = reason.dependency;							if (								(dep instanceof HarmonyExportImportedSpecifierDependency ||									(dep instanceof HarmonyImportSpecifierDependency &&										!dep.namespaceObjectAsContext)) &&								dep._id							) {								const mapping = getMappingFromInfo(info, dep._id);								if (mapping) {									dep.redirectedModule = mapping.module;									dep.redirectedId = mapping.exportName;									mapping.module.addReason(										reason.module,										dep,										reason.explanation											? reason.explanation +													" (skipped side-effect-free modules)"											: "(skipped side-effect-free modules)"									);									// removing the currect reason, by not adding it to the newReasons array									// lazily create the newReasons array									if (newReasons === undefined) {										newReasons = i === 0 ? [] : module.reasons.slice(0, i);									}									continue;								}							}							if (newReasons !== undefined) newReasons.push(reason);						}						if (newReasons !== undefined) {							module.reasons = newReasons;						}					}				}			);		});	}	static moduleHasSideEffects(moduleName, flagValue) {		switch (typeof flagValue) {			case "undefined":				return true;			case "boolean":				return flagValue;			case "string":				if (process.platform === "win32") {					flagValue = flagValue.replace(/\\/g, "/");				}				return mm.isMatch(moduleName, flagValue, {					matchBase: true				});			case "object":				return flagValue.some(glob =>					SideEffectsFlagPlugin.moduleHasSideEffects(moduleName, glob)				);		}	}}module.exports = SideEffectsFlagPlugin;
 |