CodeGenerationResults.js 5.8 KB

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