CommonJsChunkFormatPlugin.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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. runtimeChunk,
  85. compilation.outputOptions
  86. ),
  87. {
  88. chunk: /** @type {Chunk} */ (runtimeChunk),
  89. contentHashType: "javascript"
  90. }
  91. )
  92. .replace(/^\/+/g, "")
  93. .split("/");
  94. // remove common parts
  95. while (
  96. currentOutputName.length > 1 &&
  97. runtimeOutputName.length > 1 &&
  98. currentOutputName[0] === runtimeOutputName[0]
  99. ) {
  100. currentOutputName.shift();
  101. runtimeOutputName.shift();
  102. }
  103. const last = runtimeOutputName.join("/");
  104. // create final path
  105. const runtimePath =
  106. getUndoPath(currentOutputName.join("/"), last, true) + last;
  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;