Generator.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { JAVASCRIPT_TYPE } = require("./ModuleSourceTypeConstants");
  7. const memoize = require("./util/memoize");
  8. /** @import { Source } from "webpack-sources" */
  9. /** @import ChunkGraph from "./ChunkGraph" */
  10. /** @import CodeGenerationResults from "./CodeGenerationResults" */
  11. /** @import ConcatenationScope from "./ConcatenationScope" */
  12. /** @import DependencyTemplates from "./DependencyTemplates" */
  13. /**
  14. * @import {
  15. * CodeGenerationResultData,
  16. * ConcatenationBailoutReasonContext,
  17. * RuntimeRequirements,
  18. * SourceType,
  19. * SourceTypes
  20. * } from "./Module"
  21. */
  22. /** @import ModuleGraph from "./ModuleGraph" */
  23. /** @import NormalModule from "./NormalModule" */
  24. /** @import RuntimeTemplate from "./RuntimeTemplate" */
  25. /** @import Hash from "./util/Hash" */
  26. /** @import { RuntimeSpec } from "./util/runtime" */
  27. /**
  28. * Defines the generate context type used by this module.
  29. * @typedef {object} GenerateContext
  30. * @property {DependencyTemplates} dependencyTemplates mapping from dependencies to templates
  31. * @property {RuntimeTemplate} runtimeTemplate the runtime template
  32. * @property {ModuleGraph} moduleGraph the module graph
  33. * @property {ChunkGraph} chunkGraph the chunk graph
  34. * @property {RuntimeRequirements} runtimeRequirements the requirements for runtime
  35. * @property {RuntimeSpec} runtime the runtime
  36. * @property {ConcatenationScope=} concatenationScope when in concatenated module, information about other concatenated modules
  37. * @property {CodeGenerationResults=} codeGenerationResults code generation results of other modules (need to have a codeGenerationDependency to use that)
  38. * @property {SourceType} type which kind of code should be generated
  39. * @property {() => CodeGenerationResultData=} getData get access to the code generation data
  40. */
  41. /**
  42. * Defines the generate error fn callback.
  43. * @callback GenerateErrorFn
  44. * @param {Error} error the error
  45. * @param {NormalModule} module module for which the code should be generated
  46. * @param {GenerateContext} generateContext context for generate
  47. * @returns {Source | null} generated code
  48. */
  49. /**
  50. * Represents the generator runtime component.
  51. * @typedef {object} UpdateHashContext
  52. * @property {NormalModule} module the module
  53. * @property {ChunkGraph} chunkGraph
  54. * @property {RuntimeSpec} runtime
  55. * @property {RuntimeTemplate=} runtimeTemplate
  56. */
  57. const getModuleParseError = memoize(() => require("./errors/ModuleParseError"));
  58. class Generator {
  59. /**
  60. * Returns generator by type.
  61. * @param {{ [key in SourceType]?: Generator }} map map of types
  62. * @returns {ByTypeGenerator} generator by type
  63. */
  64. static byType(map) {
  65. return new ByTypeGenerator(map);
  66. }
  67. /**
  68. * Returns the statement a module that failed to build throws when executed.
  69. * @param {Error} error the build error
  70. * @param {string=} parseErrorConstructor constructor for a rejected source, defaults to `SyntaxError` (WebAssembly rejects with `WebAssembly.CompileError` instead)
  71. * @returns {string} javascript statement
  72. */
  73. static throwBuildErrorCode(error, parseErrorConstructor = "SyntaxError") {
  74. // A source the parser rejected throws what a native load of it would.
  75. const name =
  76. error instanceof getModuleParseError() ? parseErrorConstructor : "Error";
  77. return `throw new ${name}(${JSON.stringify(error.message)});`;
  78. }
  79. /* istanbul ignore next */
  80. /**
  81. * Returns the source types available for this module.
  82. * @abstract
  83. * @param {NormalModule} module fresh module
  84. * @returns {SourceTypes} available types (do not mutate)
  85. */
  86. getTypes(module) {
  87. const AbstractMethodError = require("./errors/AbstractMethodError");
  88. throw new AbstractMethodError();
  89. }
  90. /**
  91. * @returns {boolean} whether getTypes() depends on the module's incoming connections
  92. */
  93. getTypesDependOnIncomingConnections() {
  94. return false;
  95. }
  96. /* istanbul ignore next */
  97. /**
  98. * Returns the estimated size for the requested source type.
  99. * @abstract
  100. * @param {NormalModule} module the module
  101. * @param {SourceType=} type source type
  102. * @returns {number} estimate size of the module
  103. */
  104. getSize(module, type) {
  105. const AbstractMethodError = require("./errors/AbstractMethodError");
  106. throw new AbstractMethodError();
  107. }
  108. /* istanbul ignore next */
  109. /**
  110. * Generates generated code for this runtime module.
  111. * @abstract
  112. * @param {NormalModule} module module for which the code should be generated
  113. * @param {GenerateContext} generateContext context for generate
  114. * @returns {Source | null} generated code
  115. */
  116. generate(
  117. module,
  118. { dependencyTemplates, runtimeTemplate, moduleGraph, type }
  119. ) {
  120. const AbstractMethodError = require("./errors/AbstractMethodError");
  121. throw new AbstractMethodError();
  122. }
  123. /**
  124. * Returns the reason this module cannot be concatenated, when one exists.
  125. * @param {NormalModule} module module for which the bailout reason should be determined
  126. * @param {ConcatenationBailoutReasonContext} context context
  127. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  128. */
  129. getConcatenationBailoutReason(module, context) {
  130. return `Module Concatenation is not implemented for ${this.constructor.name}`;
  131. }
  132. /**
  133. * Updates the hash with the data contributed by this instance.
  134. * @param {Hash} hash hash that will be modified
  135. * @param {UpdateHashContext} updateHashContext context for updating hash
  136. */
  137. updateHash(hash, { module, runtime }) {
  138. // no nothing
  139. }
  140. }
  141. /**
  142. * @this {ByTypeGenerator}
  143. * @type {GenerateErrorFn}
  144. */
  145. function generateError(error, module, generateContext) {
  146. const type = generateContext.type;
  147. const generator =
  148. /** @type {Generator & { generateError?: GenerateErrorFn }} */
  149. (this.map[type]);
  150. if (!generator) {
  151. throw new Error(`Generator.byType: no generator specified for ${type}`);
  152. }
  153. if (typeof generator.generateError === "undefined") {
  154. return null;
  155. }
  156. return generator.generateError(error, module, generateContext);
  157. }
  158. class ByTypeGenerator extends Generator {
  159. /**
  160. * Creates an instance of ByTypeGenerator.
  161. * @param {{ [key in SourceType]?: Generator }} map map of types
  162. */
  163. constructor(map) {
  164. super();
  165. this.map = map;
  166. /** @type {SourceTypes} */
  167. this._types = /** @type {SourceTypes} */ (new Set(Object.keys(map)));
  168. /** @type {GenerateErrorFn | undefined} */
  169. this.generateError = generateError.bind(this);
  170. }
  171. /**
  172. * Returns the source types available for this module.
  173. * @param {NormalModule} module fresh module
  174. * @returns {SourceTypes} available types (do not mutate)
  175. */
  176. getTypes(module) {
  177. return this._types;
  178. }
  179. /**
  180. * Returns the estimated size for the requested source type.
  181. * @param {NormalModule} module the module
  182. * @param {SourceType=} type source type
  183. * @returns {number} estimate size of the module
  184. */
  185. getSize(module, type = JAVASCRIPT_TYPE) {
  186. const t = type;
  187. const generator = this.map[t];
  188. return generator ? generator.getSize(module, t) : 0;
  189. }
  190. /**
  191. * Generates generated code for this runtime module.
  192. * @param {NormalModule} module module for which the code should be generated
  193. * @param {GenerateContext} generateContext context for generate
  194. * @returns {Source | null} generated code
  195. */
  196. generate(module, generateContext) {
  197. const type = generateContext.type;
  198. const generator = this.map[type];
  199. if (!generator) {
  200. throw new Error(`Generator.byType: no generator specified for ${type}`);
  201. }
  202. return generator.generate(module, generateContext);
  203. }
  204. }
  205. module.exports = Generator;