JavascriptGenerator.js 8.4 KB

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