FlagDependencyExportsPlugin.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Queue = require("./util/Queue");
  7. const addToSet = (a, b) => {
  8. for (const item of b) {
  9. a.add(item);
  10. }
  11. };
  12. class FlagDependencyExportsPlugin {
  13. apply(compiler) {
  14. compiler.hooks.compilation.tap(
  15. "FlagDependencyExportsPlugin",
  16. compilation => {
  17. compilation.hooks.finishModules.tap(
  18. "FlagDependencyExportsPlugin",
  19. modules => {
  20. const dependencies = new Map();
  21. const queue = new Queue();
  22. let module;
  23. let moduleWithExports;
  24. let moduleProvidedExports;
  25. let providedExportsAreTemporary;
  26. const processDependenciesBlock = depBlock => {
  27. for (const dep of depBlock.dependencies) {
  28. if (processDependency(dep)) return true;
  29. }
  30. for (const variable of depBlock.variables) {
  31. for (const dep of variable.dependencies) {
  32. if (processDependency(dep)) return true;
  33. }
  34. }
  35. for (const block of depBlock.blocks) {
  36. if (processDependenciesBlock(block)) return true;
  37. }
  38. return false;
  39. };
  40. const processDependency = dep => {
  41. const exportDesc = dep.getExports && dep.getExports();
  42. if (!exportDesc) return;
  43. moduleWithExports = true;
  44. const exports = exportDesc.exports;
  45. // break early if it's only in the worst state
  46. if (module.buildMeta.providedExports === true) {
  47. return true;
  48. }
  49. // break if it should move to the worst state
  50. if (exports === true) {
  51. module.buildMeta.providedExports = true;
  52. return true;
  53. }
  54. // merge in new exports
  55. if (Array.isArray(exports)) {
  56. addToSet(moduleProvidedExports, exports);
  57. }
  58. // store dependencies
  59. const exportDeps = exportDesc.dependencies;
  60. if (exportDeps) {
  61. providedExportsAreTemporary = true;
  62. for (const exportDependency of exportDeps) {
  63. // add dependency for this module
  64. const set = dependencies.get(exportDependency);
  65. if (set === undefined) {
  66. dependencies.set(exportDependency, new Set([module]));
  67. } else {
  68. set.add(module);
  69. }
  70. }
  71. }
  72. return false;
  73. };
  74. const notifyDependencies = () => {
  75. const deps = dependencies.get(module);
  76. if (deps !== undefined) {
  77. for (const dep of deps) {
  78. queue.enqueue(dep);
  79. }
  80. }
  81. };
  82. const notifyDependenciesIfDifferent = (set, array) => {
  83. const deps = dependencies.get(module);
  84. if (deps !== undefined) {
  85. if (set.size === array.length) {
  86. let i = 0;
  87. let different = false;
  88. for (const item of set) {
  89. if (item !== array[i++]) {
  90. different = true;
  91. break;
  92. }
  93. }
  94. if (!different) return;
  95. }
  96. for (const dep of deps) {
  97. queue.enqueue(dep);
  98. }
  99. }
  100. };
  101. // Start with all modules without provided exports
  102. for (const module of modules) {
  103. if (module.buildInfo.temporaryProvidedExports) {
  104. // Clear exports when they are temporary
  105. // and recreate them
  106. module.buildMeta.providedExports = null;
  107. queue.enqueue(module);
  108. } else if (!module.buildMeta.providedExports) {
  109. queue.enqueue(module);
  110. }
  111. }
  112. while (queue.length > 0) {
  113. module = queue.dequeue();
  114. if (module.buildMeta.providedExports !== true) {
  115. moduleWithExports =
  116. module.buildMeta && module.buildMeta.exportsType;
  117. moduleProvidedExports = new Set();
  118. providedExportsAreTemporary = false;
  119. processDependenciesBlock(module);
  120. module.buildInfo.temporaryProvidedExports = providedExportsAreTemporary;
  121. if (!moduleWithExports) {
  122. notifyDependencies();
  123. module.buildMeta.providedExports = true;
  124. } else if (module.buildMeta.providedExports === true) {
  125. notifyDependencies();
  126. } else if (!module.buildMeta.providedExports) {
  127. notifyDependencies();
  128. module.buildMeta.providedExports = Array.from(
  129. moduleProvidedExports
  130. );
  131. } else {
  132. notifyDependenciesIfDifferent(
  133. moduleProvidedExports,
  134. module.buildMeta.providedExports
  135. );
  136. module.buildMeta.providedExports = Array.from(
  137. moduleProvidedExports
  138. );
  139. }
  140. }
  141. }
  142. }
  143. );
  144. const providedExportsCache = new WeakMap();
  145. compilation.hooks.rebuildModule.tap(
  146. "FlagDependencyExportsPlugin",
  147. module => {
  148. providedExportsCache.set(module, module.buildMeta.providedExports);
  149. }
  150. );
  151. compilation.hooks.finishRebuildingModule.tap(
  152. "FlagDependencyExportsPlugin",
  153. module => {
  154. module.buildMeta.providedExports = providedExportsCache.get(module);
  155. }
  156. );
  157. }
  158. );
  159. }
  160. }
  161. module.exports = FlagDependencyExportsPlugin;