JavascriptGenerator.js 8.6 KB

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