CommonJsChunkFormatPlugin.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource, RawSource } = require("webpack-sources");
  7. const RuntimeGlobals = require("../RuntimeGlobals");
  8. const Template = require("../Template");
  9. const {
  10. getChunkFilenameTemplate,
  11. getCompilationHooks
  12. } = require("./JavascriptModulesPlugin");
  13. const {
  14. generateEntryStartup,
  15. updateHashForEntryStartup
  16. } = require("./StartupHelpers");
  17. /** @typedef {import("../Chunk")} Chunk */
  18. /** @typedef {import("../Compiler")} Compiler */
  19. /** @typedef {import("../Entrypoint")} Entrypoint */
  20. class CommonJsChunkFormatPlugin {
  21. /**
  22. * Apply the plugin
  23. * @param {Compiler} compiler the compiler instance
  24. * @returns {void}
  25. */
  26. apply(compiler) {
  27. compiler.hooks.thisCompilation.tap(
  28. "CommonJsChunkFormatPlugin",
  29. compilation => {
  30. compilation.hooks.additionalChunkRuntimeRequirements.tap(
  31. "CommonJsChunkFormatPlugin",
  32. (chunk, set, { chunkGraph }) => {
  33. if (chunk.hasRuntime()) return;
  34. if (chunkGraph.getNumberOfEntryModules(chunk) > 0) {
  35. set.add(RuntimeGlobals.require);
  36. set.add(RuntimeGlobals.startupEntrypoint);
  37. set.add(RuntimeGlobals.externalInstallChunk);
  38. }
  39. }
  40. );
  41. const hooks = getCompilationHooks(compilation);
  42. hooks.renderChunk.tap(
  43. "CommonJsChunkFormatPlugin",
  44. (modules, renderContext) => {
  45. const { chunk, chunkGraph, runtimeTemplate } = renderContext;
  46. const source = new ConcatSource();
  47. source.add(`exports.id = ${JSON.stringify(chunk.id)};\n`);
  48. source.add(`exports.ids = ${JSON.stringify(chunk.ids)};\n`);
  49. source.add(`exports.modules = `);
  50. source.add(modules);
  51. source.add(";\n");
  52. const runtimeModules =
  53. chunkGraph.getChunkRuntimeModulesInOrder(chunk);
  54. if (runtimeModules.length > 0) {
  55. source.add("exports.runtime =\n");
  56. source.add(
  57. Template.renderChunkRuntimeModules(
  58. runtimeModules,
  59. renderContext
  60. )
  61. );
  62. }
  63. const entries = Array.from(
  64. chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
  65. );
  66. if (entries.length > 0) {
  67. const runtimeChunk =
  68. /** @type {Entrypoint} */
  69. (entries[0][1]).getRuntimeChunk();
  70. const currentOutputName = compilation
  71. .getPath(
  72. getChunkFilenameTemplate(chunk, compilation.outputOptions),
  73. {
  74. chunk,
  75. contentHashType: "javascript"
  76. }
  77. )
  78. .split("/");
  79. const runtimeOutputName = compilation
  80. .getPath(
  81. getChunkFilenameTemplate(
  82. runtimeChunk,
  83. compilation.outputOptions
  84. ),
  85. {
  86. chunk: /** @type {Chunk} */ (runtimeChunk),
  87. contentHashType: "javascript"
  88. }
  89. )
  90. .split("/");
  91. // remove filename, we only need the directory
  92. currentOutputName.pop();
  93. // remove common parts
  94. while (
  95. currentOutputName.length > 0 &&
  96. runtimeOutputName.length > 0 &&
  97. currentOutputName[0] === runtimeOutputName[0]
  98. ) {
  99. currentOutputName.shift();
  100. runtimeOutputName.shift();
  101. }
  102. // create final path
  103. const runtimePath =
  104. (currentOutputName.length > 0
  105. ? "../".repeat(currentOutputName.length)
  106. : "./") + runtimeOutputName.join("/");
  107. const entrySource = new ConcatSource();
  108. entrySource.add(
  109. `(${
  110. runtimeTemplate.supportsArrowFunction()
  111. ? "() => "
  112. : "function() "
  113. }{\n`
  114. );
  115. entrySource.add("var exports = {};\n");
  116. entrySource.add(source);
  117. entrySource.add(";\n\n// load runtime\n");
  118. entrySource.add(
  119. `var ${RuntimeGlobals.require} = require(${JSON.stringify(
  120. runtimePath
  121. )});\n`
  122. );
  123. entrySource.add(
  124. `${RuntimeGlobals.externalInstallChunk}(exports);\n`
  125. );
  126. const startupSource = new RawSource(
  127. generateEntryStartup(
  128. chunkGraph,
  129. runtimeTemplate,
  130. entries,
  131. chunk,
  132. false
  133. )
  134. );
  135. entrySource.add(
  136. hooks.renderStartup.call(
  137. startupSource,
  138. entries[entries.length - 1][0],
  139. {
  140. ...renderContext,
  141. inlined: false
  142. }
  143. )
  144. );
  145. entrySource.add("\n})()");
  146. return entrySource;
  147. }
  148. return source;
  149. }
  150. );
  151. hooks.chunkHash.tap(
  152. "CommonJsChunkFormatPlugin",
  153. (chunk, hash, { chunkGraph }) => {
  154. if (chunk.hasRuntime()) return;
  155. hash.update("CommonJsChunkFormatPlugin");
  156. hash.update("1");
  157. const entries = Array.from(
  158. chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk)
  159. );
  160. updateHashForEntryStartup(hash, chunkGraph, entries, chunk);
  161. }
  162. );
  163. }
  164. );
  165. }
  166. }
  167. module.exports = CommonJsChunkFormatPlugin;