You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.2 KiB

4 years ago
  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. const size = a.size;
  9. for (const item of b) {
  10. a.add(item);
  11. }
  12. return a.size !== size;
  13. };
  14. class FlagDependencyExportsPlugin {
  15. apply(compiler) {
  16. compiler.hooks.compilation.tap(
  17. "FlagDependencyExportsPlugin",
  18. compilation => {
  19. compilation.hooks.finishModules.tap(
  20. "FlagDependencyExportsPlugin",
  21. modules => {
  22. const dependencies = new Map();
  23. const queue = new Queue();
  24. let module;
  25. let moduleWithExports;
  26. let moduleProvidedExports;
  27. let providedExportsAreTemporary;
  28. const processDependenciesBlock = depBlock => {
  29. for (const dep of depBlock.dependencies) {
  30. if (processDependency(dep)) return true;
  31. }
  32. for (const variable of depBlock.variables) {
  33. for (const dep of variable.dependencies) {
  34. if (processDependency(dep)) return true;
  35. }
  36. }
  37. for (const block of depBlock.blocks) {
  38. if (processDependenciesBlock(block)) return true;
  39. }
  40. return false;
  41. };
  42. const processDependency = dep => {
  43. const exportDesc = dep.getExports && dep.getExports();
  44. if (!exportDesc) return;
  45. moduleWithExports = true;
  46. const exports = exportDesc.exports;
  47. // break early if it's only in the worst state
  48. if (module.buildMeta.providedExports === true) {
  49. return true;
  50. }
  51. // break if it should move to the worst state
  52. if (exports === true) {
  53. module.buildMeta.providedExports = true;
  54. notifyDependencies();
  55. return true;
  56. }
  57. // merge in new exports
  58. if (Array.isArray(exports)) {
  59. if (addToSet(moduleProvidedExports, exports)) {
  60. notifyDependencies();
  61. }
  62. }
  63. // store dependencies
  64. const exportDeps = exportDesc.dependencies;
  65. if (exportDeps) {
  66. providedExportsAreTemporary = true;
  67. for (const exportDependency of exportDeps) {
  68. // add dependency for this module
  69. const set = dependencies.get(exportDependency);
  70. if (set === undefined) {
  71. dependencies.set(exportDependency, new Set([module]));
  72. } else {
  73. set.add(module);
  74. }
  75. }
  76. }
  77. return false;
  78. };
  79. const notifyDependencies = () => {
  80. const deps = dependencies.get(module);
  81. if (deps !== undefined) {
  82. for (const dep of deps) {
  83. queue.enqueue(dep);
  84. }
  85. }
  86. };
  87. // Start with all modules without provided exports
  88. for (const module of modules) {
  89. if (module.buildInfo.temporaryProvidedExports) {
  90. // Clear exports when they are temporary
  91. // and recreate them
  92. module.buildMeta.providedExports = null;
  93. queue.enqueue(module);
  94. } else if (!module.buildMeta.providedExports) {
  95. queue.enqueue(module);
  96. }
  97. }
  98. while (queue.length > 0) {
  99. module = queue.dequeue();
  100. if (module.buildMeta.providedExports !== true) {
  101. moduleWithExports =
  102. module.buildMeta && module.buildMeta.exportsType;
  103. moduleProvidedExports = new Set();
  104. providedExportsAreTemporary = false;
  105. processDependenciesBlock(module);
  106. module.buildInfo.temporaryProvidedExports = providedExportsAreTemporary;
  107. if (!moduleWithExports) {
  108. module.buildMeta.providedExports = true;
  109. notifyDependencies();
  110. } else if (module.buildMeta.providedExports !== true) {
  111. module.buildMeta.providedExports = Array.from(
  112. moduleProvidedExports
  113. );
  114. }
  115. }
  116. }
  117. }
  118. );
  119. const providedExportsCache = new WeakMap();
  120. compilation.hooks.rebuildModule.tap(
  121. "FlagDependencyExportsPlugin",
  122. module => {
  123. providedExportsCache.set(module, module.buildMeta.providedExports);
  124. }
  125. );
  126. compilation.hooks.finishRebuildingModule.tap(
  127. "FlagDependencyExportsPlugin",
  128. module => {
  129. module.buildMeta.providedExports = providedExportsCache.get(module);
  130. }
  131. );
  132. }
  133. );
  134. }
  135. }
  136. module.exports = FlagDependencyExportsPlugin;