| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const asyncLib = require("neo-async");const Queue = require("./util/Queue");/** @typedef {import("./Compiler")} Compiler *//** @typedef {import("./DependenciesBlock")} DependenciesBlock *//** @typedef {import("./Dependency")} Dependency *//** @typedef {import("./Dependency").ExportSpec} ExportSpec *//** @typedef {import("./Dependency").ExportsSpec} ExportsSpec *//** @typedef {import("./ExportsInfo")} ExportsInfo *//** @typedef {import("./Module")} Module */class FlagDependencyExportsPlugin {	/**	 * Apply the plugin	 * @param {Compiler} compiler the compiler instance	 * @returns {void}	 */	apply(compiler) {		compiler.hooks.compilation.tap(			"FlagDependencyExportsPlugin",			compilation => {				const moduleGraph = compilation.moduleGraph;				const cache = compilation.getCache("FlagDependencyExportsPlugin");				compilation.hooks.finishModules.tapAsync(					"FlagDependencyExportsPlugin",					(modules, callback) => {						const logger = compilation.getLogger(							"webpack.FlagDependencyExportsPlugin"						);						let statRestoredFromMemCache = 0;						let statRestoredFromCache = 0;						let statNoExports = 0;						let statFlaggedUncached = 0;						let statNotCached = 0;						let statQueueItemsProcessed = 0;						const { moduleMemCaches } = compilation;						/** @type {Queue<Module>} */						const queue = new Queue();						// Step 1: Try to restore cached provided export info from cache						logger.time("restore cached provided exports");						asyncLib.each(							modules,							(module, callback) => {								const exportsInfo = moduleGraph.getExportsInfo(module);								if (!module.buildMeta || !module.buildMeta.exportsType) {									if (exportsInfo.otherExportsInfo.provided !== null) {										// It's a module without declared exports										statNoExports++;										exportsInfo.setHasProvideInfo();										exportsInfo.setUnknownExportsProvided();										return callback();									}								}								if (typeof module.buildInfo.hash !== "string") {									statFlaggedUncached++;									// Enqueue uncacheable module for determining the exports									queue.enqueue(module);									exportsInfo.setHasProvideInfo();									return callback();								}								const memCache = moduleMemCaches && moduleMemCaches.get(module);								const memCacheValue = memCache && memCache.get(this);								if (memCacheValue !== undefined) {									statRestoredFromMemCache++;									exportsInfo.restoreProvided(memCacheValue);									return callback();								}								cache.get(									module.identifier(),									module.buildInfo.hash,									(err, result) => {										if (err) return callback(err);										if (result !== undefined) {											statRestoredFromCache++;											exportsInfo.restoreProvided(result);										} else {											statNotCached++;											// Without cached info enqueue module for determining the exports											queue.enqueue(module);											exportsInfo.setHasProvideInfo();										}										callback();									}								);							},							err => {								logger.timeEnd("restore cached provided exports");								if (err) return callback(err);								/** @type {Set<Module>} */								const modulesToStore = new Set();								/** @type {Map<Module, Set<Module>>} */								const dependencies = new Map();								/** @type {Module} */								let module;								/** @type {ExportsInfo} */								let exportsInfo;								/** @type {Map<Dependency, ExportsSpec>} */								const exportsSpecsFromDependencies = new Map();								let cacheable = true;								let changed = false;								/**								 * @param {DependenciesBlock} depBlock the dependencies block								 * @returns {void}								 */								const processDependenciesBlock = depBlock => {									for (const dep of depBlock.dependencies) {										processDependency(dep);									}									for (const block of depBlock.blocks) {										processDependenciesBlock(block);									}								};								/**								 * @param {Dependency} dep the dependency								 * @returns {void}								 */								const processDependency = dep => {									const exportDesc = dep.getExports(moduleGraph);									if (!exportDesc) return;									exportsSpecsFromDependencies.set(dep, exportDesc);								};								/**								 * @param {Dependency} dep dependency								 * @param {ExportsSpec} exportDesc info								 * @returns {void}								 */								const processExportsSpec = (dep, exportDesc) => {									const exports = exportDesc.exports;									const globalCanMangle = exportDesc.canMangle;									const globalFrom = exportDesc.from;									const globalPriority = exportDesc.priority;									const globalTerminalBinding =										exportDesc.terminalBinding || false;									const exportDeps = exportDesc.dependencies;									if (exportDesc.hideExports) {										for (const name of exportDesc.hideExports) {											const exportInfo = exportsInfo.getExportInfo(name);											exportInfo.unsetTarget(dep);										}									}									if (exports === true) {										// unknown exports										if (											exportsInfo.setUnknownExportsProvided(												globalCanMangle,												exportDesc.excludeExports,												globalFrom && dep,												globalFrom,												globalPriority											)										) {											changed = true;										}									} else if (Array.isArray(exports)) {										/**										 * merge in new exports										 * @param {ExportsInfo} exportsInfo own exports info										 * @param {(ExportSpec | string)[]} exports list of exports										 */										const mergeExports = (exportsInfo, exports) => {											for (const exportNameOrSpec of exports) {												let name;												let canMangle = globalCanMangle;												let terminalBinding = globalTerminalBinding;												let exports = undefined;												let from = globalFrom;												let fromExport = undefined;												let priority = globalPriority;												let hidden = false;												if (typeof exportNameOrSpec === "string") {													name = exportNameOrSpec;												} else {													name = exportNameOrSpec.name;													if (exportNameOrSpec.canMangle !== undefined)														canMangle = exportNameOrSpec.canMangle;													if (exportNameOrSpec.export !== undefined)														fromExport = exportNameOrSpec.export;													if (exportNameOrSpec.exports !== undefined)														exports = exportNameOrSpec.exports;													if (exportNameOrSpec.from !== undefined)														from = exportNameOrSpec.from;													if (exportNameOrSpec.priority !== undefined)														priority = exportNameOrSpec.priority;													if (exportNameOrSpec.terminalBinding !== undefined)														terminalBinding = exportNameOrSpec.terminalBinding;													if (exportNameOrSpec.hidden !== undefined)														hidden = exportNameOrSpec.hidden;												}												const exportInfo = exportsInfo.getExportInfo(name);												if (													exportInfo.provided === false ||													exportInfo.provided === null												) {													exportInfo.provided = true;													changed = true;												}												if (													exportInfo.canMangleProvide !== false &&													canMangle === false												) {													exportInfo.canMangleProvide = false;													changed = true;												}												if (terminalBinding && !exportInfo.terminalBinding) {													exportInfo.terminalBinding = true;													changed = true;												}												if (exports) {													const nestedExportsInfo =														exportInfo.createNestedExportsInfo();													mergeExports(nestedExportsInfo, exports);												}												if (													from &&													(hidden														? exportInfo.unsetTarget(dep)														: exportInfo.setTarget(																dep,																from,																fromExport === undefined ? [name] : fromExport,																priority														  ))												) {													changed = true;												}												// Recalculate target exportsInfo												const target = exportInfo.getTarget(moduleGraph);												let targetExportsInfo = undefined;												if (target) {													const targetModuleExportsInfo =														moduleGraph.getExportsInfo(target.module);													targetExportsInfo =														targetModuleExportsInfo.getNestedExportsInfo(															target.export														);													// add dependency for this module													const set = dependencies.get(target.module);													if (set === undefined) {														dependencies.set(target.module, new Set([module]));													} else {														set.add(module);													}												}												if (exportInfo.exportsInfoOwned) {													if (														exportInfo.exportsInfo.setRedirectNamedTo(															targetExportsInfo														)													) {														changed = true;													}												} else if (													exportInfo.exportsInfo !== targetExportsInfo												) {													exportInfo.exportsInfo = targetExportsInfo;													changed = true;												}											}										};										mergeExports(exportsInfo, exports);									}									// store dependencies									if (exportDeps) {										cacheable = false;										for (const exportDependency of exportDeps) {											// add dependency for this module											const set = dependencies.get(exportDependency);											if (set === undefined) {												dependencies.set(exportDependency, new Set([module]));											} else {												set.add(module);											}										}									}								};								const notifyDependencies = () => {									const deps = dependencies.get(module);									if (deps !== undefined) {										for (const dep of deps) {											queue.enqueue(dep);										}									}								};								logger.time("figure out provided exports");								while (queue.length > 0) {									module = queue.dequeue();									statQueueItemsProcessed++;									exportsInfo = moduleGraph.getExportsInfo(module);									cacheable = true;									changed = false;									exportsSpecsFromDependencies.clear();									moduleGraph.freeze();									processDependenciesBlock(module);									moduleGraph.unfreeze();									for (const [										dep,										exportsSpec									] of exportsSpecsFromDependencies) {										processExportsSpec(dep, exportsSpec);									}									if (cacheable) {										modulesToStore.add(module);									}									if (changed) {										notifyDependencies();									}								}								logger.timeEnd("figure out provided exports");								logger.log(									`${Math.round(										(100 * (statFlaggedUncached + statNotCached)) /											(statRestoredFromMemCache +												statRestoredFromCache +												statNotCached +												statFlaggedUncached +												statNoExports)									)}% of exports of modules have been determined (${statNoExports} no declared exports, ${statNotCached} not cached, ${statFlaggedUncached} flagged uncacheable, ${statRestoredFromCache} from cache, ${statRestoredFromMemCache} from mem cache, ${										statQueueItemsProcessed -										statNotCached -										statFlaggedUncached									} additional calculations due to dependencies)`								);								logger.time("store provided exports into cache");								asyncLib.each(									modulesToStore,									(module, callback) => {										if (typeof module.buildInfo.hash !== "string") {											// not cacheable											return callback();										}										const cachedData = moduleGraph											.getExportsInfo(module)											.getRestoreProvidedData();										const memCache =											moduleMemCaches && moduleMemCaches.get(module);										if (memCache) {											memCache.set(this, cachedData);										}										cache.store(											module.identifier(),											module.buildInfo.hash,											cachedData,											callback										);									},									err => {										logger.timeEnd("store provided exports into cache");										callback(err);									}								);							}						);					}				);				/** @type {WeakMap<Module, any>} */				const providedExportsCache = new WeakMap();				compilation.hooks.rebuildModule.tap(					"FlagDependencyExportsPlugin",					module => {						providedExportsCache.set(							module,							moduleGraph.getExportsInfo(module).getRestoreProvidedData()						);					}				);				compilation.hooks.finishRebuildingModule.tap(					"FlagDependencyExportsPlugin",					module => {						moduleGraph							.getExportsInfo(module)							.restoreProvided(providedExportsCache.get(module));					}				);			}		);	}}module.exports = FlagDependencyExportsPlugin;
 |