diff options
Diffstat (limited to 'node_modules/webpack/lib/FlagDependencyExportsPlugin.js')
| -rw-r--r-- | node_modules/webpack/lib/FlagDependencyExportsPlugin.js | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/FlagDependencyExportsPlugin.js b/node_modules/webpack/lib/FlagDependencyExportsPlugin.js new file mode 100644 index 00000000..aacbb3d2 --- /dev/null +++ b/node_modules/webpack/lib/FlagDependencyExportsPlugin.js @@ -0,0 +1,420 @@ +/* + 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 */ +/** @typedef {import("./Module").BuildInfo} BuildInfo */ + +const PLUGIN_NAME = "FlagDependencyExportsPlugin"; +const PLUGIN_LOGGER_NAME = `webpack.${PLUGIN_NAME}`; + +class FlagDependencyExportsPlugin { + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + compiler.hooks.compilation.tap(PLUGIN_NAME, compilation => { + const moduleGraph = compilation.moduleGraph; + const cache = compilation.getCache(PLUGIN_NAME); + compilation.hooks.finishModules.tapAsync( + PLUGIN_NAME, + (modules, callback) => { + const logger = compilation.getLogger(PLUGIN_LOGGER_NAME); + 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 the module doesn't have an exportsType, it's a module + // without declared exports. + if ( + (!module.buildMeta || !module.buildMeta.exportsType) && + exportsInfo.otherExportsInfo.provided !== null + ) { + // It's a module without declared exports + statNoExports++; + exportsInfo.setHasProvideInfo(); + exportsInfo.setUnknownExportsProvided(); + return callback(); + } + // If the module has no hash, it's uncacheable + if ( + typeof (/** @type {BuildInfo} */ (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(), + /** @type {BuildInfo} */ + (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; + let from = globalFrom; + let fromExport; + 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( + /** @type {ExportsInfo} */ (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; + 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 ( + /** @type {ExportsInfo} */ + (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 = /** @type {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 ( + /** @type {BuildInfo} */ (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(), + /** @type {BuildInfo} */ + (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(PLUGIN_NAME, module => { + providedExportsCache.set( + module, + moduleGraph.getExportsInfo(module).getRestoreProvidedData() + ); + }); + compilation.hooks.finishRebuildingModule.tap(PLUGIN_NAME, module => { + moduleGraph + .getExportsInfo(module) + .restoreProvided(providedExportsCache.get(module)); + }); + }); + } +} + +module.exports = FlagDependencyExportsPlugin; |
