JavascriptGenerator.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const util = require("util");
  7. const { RawSource, ReplaceSource } = require("webpack-sources");
  8. const Generator = require("../Generator");
  9. const InitFragment = require("../InitFragment");
  10. const { JAVASCRIPT_TYPES } = require("../ModuleSourceTypeConstants");
  11. const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
  12. /** @typedef {import("webpack-sources").Source} Source */
  13. /** @typedef {import("../Compilation").DependencyConstructor} DependencyConstructor */
  14. /** @typedef {import("../DependenciesBlock")} DependenciesBlock */
  15. /** @typedef {import("../Dependency")} Dependency */
  16. /** @typedef {import("../DependencyTemplate")} DependencyTemplate */
  17. /** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
  18. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  19. /** @typedef {import("../Module")} Module */
  20. /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
  21. /** @typedef {import("../Module").SourceType} SourceType */
  22. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  23. /** @typedef {import("../NormalModule")} NormalModule */
  24. const DEFAULT_SOURCE = {
  25. source() {
  26. return new RawSource("throw new Error('No source available');");
  27. },
  28. /**
  29. * Returns the estimated size for the requested source type.
  30. * @returns {number} size of the DEFAULT_SOURCE.source()
  31. */
  32. size() {
  33. return 39;
  34. }
  35. };
  36. // TODO: clean up this file
  37. // replace with newer constructs
  38. const deprecatedGetInitFragments = util.deprecate(
  39. /**
  40. * Handles the callback logic for this hook.
  41. * @param {DependencyTemplate} template template
  42. * @param {Dependency} dependency dependency
  43. * @param {DependencyTemplateContext} templateContext template context
  44. * @returns {InitFragment<GenerateContext>[]} init fragments
  45. */
  46. (template, dependency, templateContext) =>
  47. /** @type {DependencyTemplate & { getInitFragments: (dependency: Dependency, dependencyTemplateContext: DependencyTemplateContext) => InitFragment<GenerateContext>[] }} */
  48. (template).getInitFragments(dependency, templateContext),
  49. "DependencyTemplate.getInitFragment is deprecated (use apply(dep, source, { initFragments }) instead)",
  50. "DEP_WEBPACK_JAVASCRIPT_GENERATOR_GET_INIT_FRAGMENTS"
  51. );
  52. class JavascriptGenerator extends Generator {
  53. /**
  54. * Returns the source types available for this module.
  55. * @param {NormalModule} module fresh module
  56. * @returns {SourceTypes} available types (do not mutate)
  57. */
  58. getTypes(module) {
  59. return JAVASCRIPT_TYPES;
  60. }
  61. /**
  62. * Returns the estimated size for the requested source type.
  63. * @param {NormalModule} module the module
  64. * @param {SourceType=} type source type
  65. * @returns {number} estimate size of the module
  66. */
  67. getSize(module, type) {
  68. const originalSource = module.originalSource();
  69. if (!originalSource) {
  70. return DEFAULT_SOURCE.size();
  71. }
  72. return originalSource.size();
  73. }
  74. /**
  75. * Returns the reason this module cannot be concatenated, when one exists.
  76. * @param {NormalModule} module module for which the bailout reason should be determined
  77. * @param {ConcatenationBailoutReasonContext} context context
  78. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  79. */
  80. getConcatenationBailoutReason(module, context) {
  81. // Only harmony modules are valid for optimization
  82. if (
  83. !module.buildMeta ||
  84. module.buildMeta.exportsType !== "namespace" ||
  85. module.presentationalDependencies === undefined ||
  86. !module.presentationalDependencies.some(
  87. (d) => d instanceof HarmonyCompatibilityDependency
  88. )
  89. ) {
  90. return "Module is not an ECMAScript module";
  91. }
  92. // Some expressions are not compatible with module concatenation
  93. // because they may produce unexpected results. The plugin bails out
  94. // if some were detected upfront.
  95. if (module.buildInfo && module.buildInfo.moduleConcatenationBailout) {
  96. return `Module uses ${module.buildInfo.moduleConcatenationBailout}`;
  97. }
  98. }
  99. /**
  100. * Processes the provided module.
  101. * @param {Module} module the current module
  102. * @param {Dependency} dependency the dependency to generate
  103. * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
  104. * @param {ReplaceSource} source the current replace source which can be modified
  105. * @param {GenerateContext} generateContext the render context
  106. * @returns {void}
  107. */
  108. sourceDependency(module, dependency, initFragments, source, generateContext) {
  109. const constructor =
  110. /** @type {DependencyConstructor} */
  111. (dependency.constructor);
  112. const template = generateContext.dependencyTemplates.get(constructor);
  113. if (!template) {
  114. throw new Error(
  115. `No template for dependency: ${dependency.constructor.name}`
  116. );
  117. }
  118. /** @type {InitFragment<GenerateContext>[] | undefined} */
  119. let chunkInitFragments;
  120. /** @type {DependencyTemplateContext} */
  121. const templateContext = {
  122. runtimeTemplate: generateContext.runtimeTemplate,
  123. dependencyTemplates: generateContext.dependencyTemplates,
  124. moduleGraph: generateContext.moduleGraph,
  125. chunkGraph: generateContext.chunkGraph,
  126. module,
  127. runtime: generateContext.runtime,
  128. runtimeRequirements: generateContext.runtimeRequirements,
  129. concatenationScope: generateContext.concatenationScope,
  130. codeGenerationResults:
  131. /** @type {NonNullable<GenerateContext["codeGenerationResults"]>} */
  132. (generateContext.codeGenerationResults),
  133. initFragments,
  134. get chunkInitFragments() {
  135. if (!chunkInitFragments) {
  136. const data =
  137. /** @type {NonNullable<GenerateContext["getData"]>} */
  138. (generateContext.getData)();
  139. chunkInitFragments = data.get("chunkInitFragments");
  140. if (!chunkInitFragments) {
  141. chunkInitFragments = [];
  142. data.set("chunkInitFragments", chunkInitFragments);
  143. }
  144. }
  145. return chunkInitFragments;
  146. }
  147. };
  148. template.apply(dependency, source, templateContext);
  149. // TODO remove in webpack 6
  150. if ("getInitFragments" in template) {
  151. const fragments = deprecatedGetInitFragments(
  152. template,
  153. dependency,
  154. templateContext
  155. );
  156. if (fragments) {
  157. for (const fragment of fragments) {
  158. initFragments.push(fragment);
  159. }
  160. }
  161. }
  162. }
  163. /**
  164. * Processes the provided module.
  165. * @param {Module} module the module to generate
  166. * @param {DependenciesBlock} block the dependencies block which will be processed
  167. * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
  168. * @param {ReplaceSource} source the current replace source which can be modified
  169. * @param {GenerateContext} generateContext the generateContext
  170. * @returns {void}
  171. */
  172. sourceBlock(module, block, initFragments, source, generateContext) {
  173. for (const dependency of block.dependencies) {
  174. this.sourceDependency(
  175. module,
  176. dependency,
  177. initFragments,
  178. source,
  179. generateContext
  180. );
  181. }
  182. for (const childBlock of block.blocks) {
  183. this.sourceBlock(
  184. module,
  185. childBlock,
  186. initFragments,
  187. source,
  188. generateContext
  189. );
  190. }
  191. }
  192. /**
  193. * Processes the provided module.
  194. * @param {Module} module the module to generate
  195. * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
  196. * @param {ReplaceSource} source the current replace source which can be modified
  197. * @param {GenerateContext} generateContext the generateContext
  198. * @returns {void}
  199. */
  200. sourceModule(module, initFragments, source, generateContext) {
  201. for (const dependency of module.dependencies) {
  202. this.sourceDependency(
  203. module,
  204. dependency,
  205. initFragments,
  206. source,
  207. generateContext
  208. );
  209. }
  210. if (module.presentationalDependencies !== undefined) {
  211. for (const dependency of module.presentationalDependencies) {
  212. this.sourceDependency(
  213. module,
  214. dependency,
  215. initFragments,
  216. source,
  217. generateContext
  218. );
  219. }
  220. }
  221. for (const childBlock of module.blocks) {
  222. this.sourceBlock(
  223. module,
  224. childBlock,
  225. initFragments,
  226. source,
  227. generateContext
  228. );
  229. }
  230. }
  231. /**
  232. * Generates generated code for this runtime module.
  233. * @param {NormalModule} module module for which the code should be generated
  234. * @param {GenerateContext} generateContext context for generate
  235. * @returns {Source | null} generated code
  236. */
  237. generate(module, generateContext) {
  238. const originalSource = module.originalSource();
  239. if (!originalSource) {
  240. return DEFAULT_SOURCE.source();
  241. }
  242. const source = new ReplaceSource(originalSource);
  243. /** @type {InitFragment<GenerateContext>[]} */
  244. const initFragments = [];
  245. this.sourceModule(module, initFragments, source, generateContext);
  246. return InitFragment.addToSource(source, initFragments, generateContext);
  247. }
  248. /**
  249. * Generates fallback output for the provided error condition.
  250. * @param {Error} error the error
  251. * @param {NormalModule} module module for which the code should be generated
  252. * @param {GenerateContext} generateContext context for generate
  253. * @returns {Source | null} generated code
  254. */
  255. generateError(error, module, generateContext) {
  256. return new RawSource(`throw new Error(${JSON.stringify(error.message)});`);
  257. }
  258. }
  259. module.exports = JavascriptGenerator;