CodeGenerationResults.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { DEFAULTS } = require("./config/defaults");
  7. const { getOrInsert } = require("./util/MapHelpers");
  8. const { first } = require("./util/SetHelpers");
  9. const createHash = require("./util/createHash");
  10. const { RuntimeSpecMap, runtimeToString } = require("./util/runtime");
  11. /** @typedef {import("webpack-sources").Source} Source */
  12. /** @typedef {import("./Module")} Module */
  13. /** @typedef {import("./Module").SourceType} SourceType */
  14. /** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
  15. /** @typedef {import("./Module").CodeGenerationResultData} CodeGenerationResultData */
  16. /** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
  17. /** @typedef {import("./util/Hash").HashFunction} HashFunction */
  18. /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
  19. /**
  20. * Stores code generation results keyed by module and runtime so later stages
  21. * can retrieve emitted sources, metadata, and derived hashes.
  22. */
  23. class CodeGenerationResults {
  24. /**
  25. * Initializes an empty result store and remembers which hash function should
  26. * be used when a result hash needs to be derived lazily.
  27. * @param {HashFunction} hashFunction the hash function to use
  28. */
  29. constructor(hashFunction = DEFAULTS.HASH_FUNCTION) {
  30. /** @type {Map<Module, RuntimeSpecMap<CodeGenerationResult>>} */
  31. this.map = new Map();
  32. /** @type {HashFunction} */
  33. this._hashFunction = hashFunction;
  34. }
  35. /**
  36. * Returns the code generation result for a module/runtime pair, rejecting
  37. * ambiguous lookups where no unique runtime-independent result exists.
  38. * @param {Module} module the module
  39. * @param {RuntimeSpec} runtime runtime(s)
  40. * @returns {CodeGenerationResult} the CodeGenerationResult
  41. */
  42. get(module, runtime) {
  43. const entry = this.map.get(module);
  44. if (entry === undefined) {
  45. throw new Error(
  46. `No code generation entry for ${module.identifier()} (existing entries: ${Array.from(
  47. this.map.keys(),
  48. (m) => m.identifier()
  49. ).join(", ")})`
  50. );
  51. }
  52. if (runtime === undefined) {
  53. if (entry.size > 1) {
  54. const results = new Set(entry.values());
  55. if (results.size !== 1) {
  56. throw new Error(
  57. `No unique code generation entry for unspecified runtime for ${module.identifier()} (existing runtimes: ${Array.from(
  58. entry.keys(),
  59. (r) => runtimeToString(r)
  60. ).join(", ")}).
  61. Caller might not support runtime-dependent code generation (opt-out via optimization.usedExports: "global").`
  62. );
  63. }
  64. return /** @type {CodeGenerationResult} */ (first(results));
  65. }
  66. return /** @type {CodeGenerationResult} */ (entry.values().next().value);
  67. }
  68. const result = entry.get(runtime);
  69. if (result === undefined) {
  70. throw new Error(
  71. `No code generation entry for runtime ${runtimeToString(
  72. runtime
  73. )} for ${module.identifier()} (existing runtimes: ${Array.from(
  74. entry.keys(),
  75. (r) => runtimeToString(r)
  76. ).join(", ")})`
  77. );
  78. }
  79. return result;
  80. }
  81. /**
  82. * Reports whether a module has a stored result for the requested runtime, or
  83. * a single unambiguous result when no runtime is specified.
  84. * @param {Module} module the module
  85. * @param {RuntimeSpec} runtime runtime(s)
  86. * @returns {boolean} true, when we have data for this
  87. */
  88. has(module, runtime) {
  89. const entry = this.map.get(module);
  90. if (entry === undefined) {
  91. return false;
  92. }
  93. if (runtime !== undefined) {
  94. return entry.has(runtime);
  95. } else if (entry.size > 1) {
  96. const results = new Set(entry.values());
  97. return results.size === 1;
  98. }
  99. return entry.size === 1;
  100. }
  101. /**
  102. * Returns a generated source of the requested source type from a stored code
  103. * generation result.
  104. * @param {Module} module the module
  105. * @param {RuntimeSpec} runtime runtime(s)
  106. * @param {SourceType} sourceType the source type
  107. * @returns {Source} a source
  108. */
  109. getSource(module, runtime, sourceType) {
  110. return /** @type {Source} */ (
  111. this.get(module, runtime).sources.get(sourceType)
  112. );
  113. }
  114. /**
  115. * Returns the runtime requirements captured during code generation for the
  116. * requested module/runtime pair.
  117. * @param {Module} module the module
  118. * @param {RuntimeSpec} runtime runtime(s)
  119. * @returns {ReadOnlyRuntimeRequirements | null} runtime requirements
  120. */
  121. getRuntimeRequirements(module, runtime) {
  122. return this.get(module, runtime).runtimeRequirements;
  123. }
  124. /**
  125. * Returns an arbitrary metadata entry recorded during code generation.
  126. * @param {Module} module the module
  127. * @param {RuntimeSpec} runtime runtime(s)
  128. * @param {string} key data key
  129. * @returns {ReturnType<CodeGenerationResultData["get"]>} data generated by code generation
  130. */
  131. getData(module, runtime, key) {
  132. const data = this.get(module, runtime).data;
  133. return data === undefined ? undefined : data.get(key);
  134. }
  135. /**
  136. * Returns a stable hash for the generated sources and runtime requirements,
  137. * computing and caching it on first access.
  138. * @param {Module} module the module
  139. * @param {RuntimeSpec} runtime runtime(s)
  140. * @returns {string} hash of the code generation
  141. */
  142. getHash(module, runtime) {
  143. const info = this.get(module, runtime);
  144. if (info.hash !== undefined) return info.hash;
  145. const hash = createHash(this._hashFunction);
  146. for (const [type, source] of info.sources) {
  147. hash.update(type);
  148. source.updateHash(hash);
  149. }
  150. if (info.runtimeRequirements) {
  151. for (const rr of info.runtimeRequirements) hash.update(rr);
  152. }
  153. return (info.hash = hash.digest("hex"));
  154. }
  155. /**
  156. * Stores a code generation result for a module/runtime pair, creating the
  157. * per-module runtime map when needed.
  158. * @param {Module} module the module
  159. * @param {RuntimeSpec} runtime runtime(s)
  160. * @param {CodeGenerationResult} result result from module
  161. * @returns {void}
  162. */
  163. add(module, runtime, result) {
  164. const map = getOrInsert(
  165. this.map,
  166. module,
  167. () =>
  168. /** @type {RuntimeSpecMap<CodeGenerationResult>} */
  169. new RuntimeSpecMap()
  170. );
  171. map.set(runtime, result);
  172. }
  173. }
  174. module.exports = CodeGenerationResults;