InitFragment.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Florent Cailhol @ooflorent
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const makeSerializable = require("./util/makeSerializable");
  8. /** @typedef {import("webpack-sources").Source} Source */
  9. /** @typedef {import("./Generator").GenerateContext} GenerateContext */
  10. /**
  11. * @param {InitFragment} fragment the init fragment
  12. * @param {number} index index
  13. * @returns {[InitFragment, number]} tuple with both
  14. */
  15. const extractFragmentIndex = (fragment, index) => [fragment, index];
  16. /**
  17. * @param {[InitFragment, number]} a first pair
  18. * @param {[InitFragment, number]} b second pair
  19. * @returns {number} sort value
  20. */
  21. const sortFragmentWithIndex = ([a, i], [b, j]) => {
  22. const stageCmp = a.stage - b.stage;
  23. if (stageCmp !== 0) return stageCmp;
  24. const positionCmp = a.position - b.position;
  25. if (positionCmp !== 0) return positionCmp;
  26. return i - j;
  27. };
  28. /**
  29. * @template Context
  30. */
  31. class InitFragment {
  32. /**
  33. * @param {string|Source} content the source code that will be included as initialization code
  34. * @param {number} stage category of initialization code (contribute to order)
  35. * @param {number} position position in the category (contribute to order)
  36. * @param {string=} key unique key to avoid emitting the same initialization code twice
  37. * @param {string|Source=} endContent the source code that will be included at the end of the module
  38. */
  39. constructor(content, stage, position, key, endContent) {
  40. this.content = content;
  41. this.stage = stage;
  42. this.position = position;
  43. this.key = key;
  44. this.endContent = endContent;
  45. }
  46. /**
  47. * @param {Context} context context
  48. * @returns {string|Source} the source code that will be included as initialization code
  49. */
  50. getContent(context) {
  51. return this.content;
  52. }
  53. /**
  54. * @param {Context} context context
  55. * @returns {string|Source=} the source code that will be included at the end of the module
  56. */
  57. getEndContent(context) {
  58. return this.endContent;
  59. }
  60. static addToSource(source, initFragments, context) {
  61. if (initFragments.length > 0) {
  62. // Sort fragments by position. If 2 fragments have the same position,
  63. // use their index.
  64. const sortedFragments = initFragments
  65. .map(extractFragmentIndex)
  66. .sort(sortFragmentWithIndex);
  67. // Deduplicate fragments. If a fragment has no key, it is always included.
  68. const keyedFragments = new Map();
  69. for (const [fragment] of sortedFragments) {
  70. if (typeof fragment.mergeAll === "function") {
  71. if (!fragment.key) {
  72. throw new Error(
  73. `InitFragment with mergeAll function must have a valid key: ${fragment.constructor.name}`
  74. );
  75. }
  76. const oldValue = keyedFragments.get(fragment.key);
  77. if (oldValue === undefined) {
  78. keyedFragments.set(fragment.key, fragment);
  79. } else if (Array.isArray(oldValue)) {
  80. oldValue.push(fragment);
  81. } else {
  82. keyedFragments.set(fragment.key, [oldValue, fragment]);
  83. }
  84. continue;
  85. } else if (typeof fragment.merge === "function") {
  86. const oldValue = keyedFragments.get(fragment.key);
  87. if (oldValue !== undefined) {
  88. keyedFragments.set(fragment.key, fragment.merge(oldValue));
  89. continue;
  90. }
  91. }
  92. keyedFragments.set(fragment.key || Symbol(), fragment);
  93. }
  94. const concatSource = new ConcatSource();
  95. const endContents = [];
  96. for (let fragment of keyedFragments.values()) {
  97. if (Array.isArray(fragment)) {
  98. fragment = fragment[0].mergeAll(fragment);
  99. }
  100. concatSource.add(fragment.getContent(context));
  101. const endContent = fragment.getEndContent(context);
  102. if (endContent) {
  103. endContents.push(endContent);
  104. }
  105. }
  106. concatSource.add(source);
  107. for (const content of endContents.reverse()) {
  108. concatSource.add(content);
  109. }
  110. return concatSource;
  111. } else {
  112. return source;
  113. }
  114. }
  115. serialize(context) {
  116. const { write } = context;
  117. write(this.content);
  118. write(this.stage);
  119. write(this.position);
  120. write(this.key);
  121. write(this.endContent);
  122. }
  123. deserialize(context) {
  124. const { read } = context;
  125. this.content = read();
  126. this.stage = read();
  127. this.position = read();
  128. this.key = read();
  129. this.endContent = read();
  130. }
  131. }
  132. makeSerializable(InitFragment, "webpack/lib/InitFragment");
  133. InitFragment.prototype.merge = undefined;
  134. InitFragment.STAGE_CONSTANTS = 10;
  135. InitFragment.STAGE_ASYNC_BOUNDARY = 20;
  136. InitFragment.STAGE_HARMONY_EXPORTS = 30;
  137. InitFragment.STAGE_HARMONY_IMPORTS = 40;
  138. InitFragment.STAGE_PROVIDES = 50;
  139. InitFragment.STAGE_ASYNC_DEPENDENCIES = 60;
  140. InitFragment.STAGE_ASYNC_HARMONY_IMPORTS = 70;
  141. module.exports = InitFragment;