CommonJsChunkFormatPlugin.js 4.9 KB

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