ModuleChunkFormatPlugin.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource } = require("webpack-sources");
  7. const { RuntimeGlobals } = require("..");
  8. const HotUpdateChunk = require("../HotUpdateChunk");
  9. const Template = require("../Template");
  10. const { getAllChunks } = require("../javascript/ChunkHelpers");
  11. const {
  12. chunkHasJs,
  13. getCompilationHooks,
  14. getChunkFilenameTemplate
  15. } = require("../javascript/JavascriptModulesPlugin");
  16. const { updateHashForEntryStartup } = require("../javascript/StartupHelpers");
  17. /** @typedef {import("../Compiler")} Compiler */
  18. class ModuleChunkFormatPlugin {
  19. /**
  20. * Apply the plugin
  21. * @param {Compiler} compiler the compiler instance
  22. * @returns {void}
  23. */
  24. apply(compiler) {
  25. compiler.hooks.thisCompilation.tap(
  26. "ModuleChunkFormatPlugin",
  27. compilation => {
  28. compilation.hooks.additionalChunkRuntimeRequirements.tap(
  29. "ModuleChunkFormatPlugin",
  30. (chunk, set) => {
  31. if (chunk.hasRuntime()) return;
  32. if (compilation.chunkGraph.getNumberOfEntryModules(chunk) > 0) {
  33. set.add(RuntimeGlobals.require);
  34. set.add(RuntimeGlobals.startupEntrypoint);
  35. set.add(RuntimeGlobals.externalInstallChunk);
  36. }
  37. }
  38. );
  39. const hooks = getCompilationHooks(compilation);
  40. hooks.renderChunk.tap(
  41. "ModuleChunkFormatPlugin",
  42. (modules, renderContext) => {
  43. const { chunk, chunkGraph, runtimeTemplate } = renderContext;
  44. const hotUpdateChunk =
  45. chunk instanceof HotUpdateChunk ? chunk : null;
  46. const source = new ConcatSource();
  47. if (hotUpdateChunk) {
  48. throw new Error(
  49. "HMR is not implemented for module chunk format yet"
  50. );
  51. } else {
  52. source.add(`export const id = ${JSON.stringify(chunk.id)};\n`);
  53. source.add(`export const ids = ${JSON.stringify(chunk.ids)};\n`);
  54. source.add(`export const modules = `);
  55. source.add(modules);
  56. source.add(`;\n`);
  57. const runtimeModules =
  58. chunkGraph.getChunkRuntimeModulesInOrder(chunk);
  59. if (runtimeModules.length > 0) {
  60. source.add("export const runtime =\n");
  61. source.add(
  62. Template.renderChunkRuntimeModules(
  63. runtimeModules,
  64. renderContext
  65. )
  66. );
  67. }
  68. const entries = Array.from(
  69. chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
  70. );
  71. if (entries.length > 0) {
  72. const runtimeChunk = entries[0][1].getRuntimeChunk();
  73. const currentOutputName = compilation
  74. .getPath(
  75. getChunkFilenameTemplate(chunk, compilation.outputOptions),
  76. {
  77. chunk,
  78. contentHashType: "javascript"
  79. }
  80. )
  81. .split("/");
  82. // remove filename, we only need the directory
  83. currentOutputName.pop();
  84. const getRelativePath = chunk => {
  85. const baseOutputName = currentOutputName.slice();
  86. const chunkOutputName = compilation
  87. .getPath(
  88. getChunkFilenameTemplate(
  89. chunk,
  90. compilation.outputOptions
  91. ),
  92. {
  93. chunk: chunk,
  94. contentHashType: "javascript"
  95. }
  96. )
  97. .split("/");
  98. // remove common parts
  99. while (
  100. baseOutputName.length > 0 &&
  101. chunkOutputName.length > 0 &&
  102. baseOutputName[0] === chunkOutputName[0]
  103. ) {
  104. baseOutputName.shift();
  105. chunkOutputName.shift();
  106. }
  107. // create final path
  108. return (
  109. (baseOutputName.length > 0
  110. ? "../".repeat(baseOutputName.length)
  111. : "./") + chunkOutputName.join("/")
  112. );
  113. };
  114. const entrySource = new ConcatSource();
  115. entrySource.add(source);
  116. entrySource.add(";\n\n// load runtime\n");
  117. entrySource.add(
  118. `import __webpack_require__ from ${JSON.stringify(
  119. getRelativePath(runtimeChunk)
  120. )};\n`
  121. );
  122. const startupSource = new ConcatSource();
  123. startupSource.add(
  124. `var __webpack_exec__ = ${runtimeTemplate.returningFunction(
  125. `__webpack_require__(${RuntimeGlobals.entryModuleId} = moduleId)`,
  126. "moduleId"
  127. )}\n`
  128. );
  129. const loadedChunks = new Set();
  130. let index = 0;
  131. for (let i = 0; i < entries.length; i++) {
  132. const [module, entrypoint] = entries[i];
  133. const final = i + 1 === entries.length;
  134. const moduleId = chunkGraph.getModuleId(module);
  135. const chunks = getAllChunks(
  136. entrypoint,
  137. runtimeChunk,
  138. undefined
  139. );
  140. for (const chunk of chunks) {
  141. if (
  142. loadedChunks.has(chunk) ||
  143. !chunkHasJs(chunk, chunkGraph)
  144. )
  145. continue;
  146. loadedChunks.add(chunk);
  147. startupSource.add(
  148. `import * as __webpack_chunk_${index}__ from ${JSON.stringify(
  149. getRelativePath(chunk)
  150. )};\n`
  151. );
  152. startupSource.add(
  153. `${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
  154. );
  155. index++;
  156. }
  157. startupSource.add(
  158. `${
  159. final ? "var __webpack_exports__ = " : ""
  160. }__webpack_exec__(${JSON.stringify(moduleId)});\n`
  161. );
  162. }
  163. entrySource.add(
  164. hooks.renderStartup.call(
  165. startupSource,
  166. entries[entries.length - 1][0],
  167. {
  168. ...renderContext,
  169. inlined: false
  170. }
  171. )
  172. );
  173. return entrySource;
  174. }
  175. }
  176. return source;
  177. }
  178. );
  179. hooks.chunkHash.tap(
  180. "ModuleChunkFormatPlugin",
  181. (chunk, hash, { chunkGraph, runtimeTemplate }) => {
  182. if (chunk.hasRuntime()) return;
  183. hash.update("ModuleChunkFormatPlugin");
  184. hash.update("1");
  185. const entries = Array.from(
  186. chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
  187. );
  188. updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
  189. }
  190. );
  191. }
  192. );
  193. }
  194. }
  195. module.exports = ModuleChunkFormatPlugin;