123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /*
- 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) {
- if (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 = 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(
- /** @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 = 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 (
- /** @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;
|