FlagDependencyExportsPlugin.js 3.9 KB

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