CommonJsChunkFormatPlugin.js 4.5 KB

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