JavascriptGenerator.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency");
  11. /** @typedef {import("webpack-sources").Source} Source */
  12. /** @typedef {import("../DependenciesBlock")} DependenciesBlock */
  13. /** @typedef {import("../Dependency")} Dependency */
  14. /** @typedef {import("../DependencyTemplates")} DependencyTemplates */
  15. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  16. /** @typedef {import("../Module")} Module */
  17. /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
  18. /** @typedef {import("../NormalModule")} NormalModule */
  19. /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
  20. // TODO: clean up this file
  21. // replace with newer constructs
  22. const deprecatedGetInitFragments = util.deprecate(
  23. (template, dependency, templateContext) =>
  24. template.getInitFragments(dependency, templateContext),
  25. "DependencyTemplate.getInitFragment is deprecated (use apply(dep, source, { initFragments }) instead)",
  26. "DEP_WEBPACK_JAVASCRIPT_GENERATOR_GET_INIT_FRAGMENTS"
  27. );
  28. const TYPES = new Set(["javascript"]);
  29. class JavascriptGenerator extends Generator {
  30. /**
  31. * @param {NormalModule} module fresh module
  32. * @returns {Set<string>} available types (do not mutate)
  33. */
  34. getTypes(module) {
  35. return TYPES;
  36. }
  37. /**
  38. * @param {NormalModule} module the module
  39. * @param {string=} type source type
  40. * @returns {number} estimate size of the module
  41. */
  42. getSize(module, type) {
  43. const originalSource = module.originalSource();
  44. if (!originalSource) {
  45. return 39;
  46. }
  47. return originalSource.size();
  48. }
  49. /**
  50. * @param {NormalModule} module module for which the bailout reason should be determined
  51. * @param {ConcatenationBailoutReasonContext} context context
  52. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  53. */
  54. getConcatenationBailoutReason(module, context) {
  55. // Only harmony modules are valid for optimization
  56. if (
  57. !module.buildMeta ||
  58. module.buildMeta.exportsType !== "namespace" ||
  59. module.presentationalDependencies === undefined ||
  60. !module.presentationalDependencies.some(
  61. d => d instanceof HarmonyCompatibilityDependency
  62. )
  63. ) {
  64. return "Module is not an ECMAScript module";
  65. }
  66. // Some expressions are not compatible with module concatenation
  67. // because they may produce unexpected results. The plugin bails out
  68. // if some were detected upfront.
  69. if (module.buildInfo && module.buildInfo.moduleConcatenationBailout) {
  70. return `Module uses ${module.buildInfo.moduleConcatenationBailout}`;
  71. }
  72. }
  73. /**
  74. * @param {NormalModule} module module for which the code should be generated
  75. * @param {GenerateContext} generateContext context for generate
  76. * @returns {Source} generated code
  77. */
  78. generate(module, generateContext) {
  79. const originalSource = module.originalSource();
  80. if (!originalSource) {
  81. return new RawSource("throw new Error('No source available');");
  82. }
  83. const source = new ReplaceSource(originalSource);
  84. const initFragments = [];
  85. this.sourceModule(module, initFragments, source, generateContext);
  86. return InitFragment.addToSource(source, initFragments, generateContext);
  87. }
  88. /**
  89. * @param {Module} module the module to generate
  90. * @param {InitFragment[]} initFragments mutable list of init fragments
  91. * @param {ReplaceSource} source the current replace source which can be modified
  92. * @param {GenerateContext} generateContext the generateContext
  93. * @returns {void}
  94. */
  95. sourceModule(module, initFragments, source, generateContext) {
  96. for (const dependency of module.dependencies) {
  97. this.sourceDependency(
  98. module,
  99. dependency,
  100. initFragments,
  101. source,
  102. generateContext
  103. );
  104. }
  105. if (module.presentationalDependencies !== undefined) {
  106. for (const dependency of module.presentationalDependencies) {
  107. this.sourceDependency(
  108. module,
  109. dependency,
  110. initFragments,
  111. source,
  112. generateContext
  113. );
  114. }
  115. }
  116. for (const childBlock of module.blocks) {
  117. this.sourceBlock(
  118. module,
  119. childBlock,
  120. initFragments,
  121. source,
  122. generateContext
  123. );
  124. }
  125. }
  126. /**
  127. * @param {Module} module the module to generate
  128. * @param {DependenciesBlock} block the dependencies block which will be processed
  129. * @param {InitFragment[]} initFragments mutable list of init fragments
  130. * @param {ReplaceSource} source the current replace source which can be modified
  131. * @param {GenerateContext} generateContext the generateContext
  132. * @returns {void}
  133. */
  134. sourceBlock(module, block, initFragments, source, generateContext) {
  135. for (const dependency of block.dependencies) {
  136. this.sourceDependency(
  137. module,
  138. dependency,
  139. initFragments,
  140. source,
  141. generateContext
  142. );
  143. }
  144. for (const childBlock of block.blocks) {
  145. this.sourceBlock(
  146. module,
  147. childBlock,
  148. initFragments,
  149. source,
  150. generateContext
  151. );
  152. }
  153. }
  154. /**
  155. * @param {Module} module the current module
  156. * @param {Dependency} dependency the dependency to generate
  157. * @param {InitFragment[]} initFragments mutable list of init fragments
  158. * @param {ReplaceSource} source the current replace source which can be modified
  159. * @param {GenerateContext} generateContext the render context
  160. * @returns {void}
  161. */
  162. sourceDependency(module, dependency, initFragments, source, generateContext) {
  163. const constructor = /** @type {new (...args: any[]) => Dependency} */ (
  164. dependency.constructor
  165. );
  166. const template = generateContext.dependencyTemplates.get(constructor);
  167. if (!template) {
  168. throw new Error(
  169. "No template for dependency: " + dependency.constructor.name
  170. );
  171. }
  172. let chunkInitFragments;
  173. const templateContext = {
  174. runtimeTemplate: generateContext.runtimeTemplate,
  175. dependencyTemplates: generateContext.dependencyTemplates,
  176. moduleGraph: generateContext.moduleGraph,
  177. chunkGraph: generateContext.chunkGraph,
  178. module,
  179. runtime: generateContext.runtime,
  180. runtimeRequirements: generateContext.runtimeRequirements,
  181. concatenationScope: generateContext.concatenationScope,
  182. codeGenerationResults: generateContext.codeGenerationResults,
  183. initFragments,
  184. get chunkInitFragments() {
  185. if (!chunkInitFragments) {
  186. const data = generateContext.getData();
  187. chunkInitFragments = data.get("chunkInitFragments");
  188. if (!chunkInitFragments) {
  189. chunkInitFragments = [];
  190. data.set("chunkInitFragments", chunkInitFragments);
  191. }
  192. }
  193. return chunkInitFragments;
  194. }
  195. };
  196. template.apply(dependency, source, templateContext);
  197. // TODO remove in webpack 6
  198. if ("getInitFragments" in template) {
  199. const fragments = deprecatedGetInitFragments(
  200. template,
  201. dependency,
  202. templateContext
  203. );
  204. if (fragments) {
  205. for (const fragment of fragments) {
  206. initFragments.push(fragment);
  207. }
  208. }
  209. }
  210. }
  211. }
  212. module.exports = JavascriptGenerator;