ContainerReferencePlugin.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra and Zackary Jackson @ScriptedAlchemy
  4. */
  5. "use strict";
  6. const ExternalModule = require("../ExternalModule");
  7. const ExternalsPlugin = require("../ExternalsPlugin");
  8. const RuntimeGlobals = require("../RuntimeGlobals");
  9. const FallbackDependency = require("./FallbackDependency");
  10. const FallbackItemDependency = require("./FallbackItemDependency");
  11. const FallbackModuleFactory = require("./FallbackModuleFactory");
  12. const RemoteModule = require("./RemoteModule");
  13. const RemoteRuntimeModule = require("./RemoteRuntimeModule");
  14. const RemoteToExternalDependency = require("./RemoteToExternalDependency");
  15. const { parseOptions } = require("./options");
  16. /** @typedef {import("../../declarations/plugins/container/ContainerReferencePlugin").ContainerReferencePluginOptions} ContainerReferencePluginOptions */
  17. /** @typedef {import("../Compiler")} Compiler */
  18. const slashCode = "/".charCodeAt(0);
  19. const PLUGIN_NAME = "ContainerReferencePlugin";
  20. class ContainerReferencePlugin {
  21. /**
  22. * Creates an instance of ContainerReferencePlugin.
  23. * @param {ContainerReferencePluginOptions} options options
  24. */
  25. constructor(options) {
  26. /** @typedef {ContainerReferencePluginOptions} */
  27. this.options = options;
  28. }
  29. /**
  30. * Applies the plugin by registering its hooks on the compiler.
  31. * @param {Compiler} compiler the compiler instance
  32. * @returns {void}
  33. */
  34. apply(compiler) {
  35. compiler.hooks.validate.tap(PLUGIN_NAME, () => {
  36. compiler.validate(
  37. () =>
  38. require("../../schemas/plugins/container/ContainerReferencePlugin.json"),
  39. this.options,
  40. {
  41. name: "Container Reference Plugin",
  42. baseDataPath: "options"
  43. },
  44. (options) =>
  45. require("../../schemas/plugins/container/ContainerReferencePlugin.check")(
  46. options
  47. )
  48. );
  49. });
  50. const { remoteType } = this.options;
  51. const remotes = parseOptions(
  52. this.options.remotes,
  53. (item) => ({
  54. external: Array.isArray(item) ? item : [item],
  55. shareScope: this.options.shareScope || "default"
  56. }),
  57. (item) => ({
  58. external: Array.isArray(item.external)
  59. ? item.external
  60. : [item.external],
  61. shareScope: item.shareScope || this.options.shareScope || "default"
  62. })
  63. );
  64. /** @type {Record<string, string>} */
  65. const remoteExternals = {};
  66. for (const [key, config] of remotes) {
  67. let i = 0;
  68. for (const external of config.external) {
  69. if (external.startsWith("internal ")) continue;
  70. remoteExternals[
  71. `webpack/container/reference/${key}${i ? `/fallback-${i}` : ""}`
  72. ] = external;
  73. i++;
  74. }
  75. }
  76. new ExternalsPlugin(remoteType, remoteExternals).apply(compiler);
  77. compiler.hooks.compilation.tap(
  78. PLUGIN_NAME,
  79. (compilation, { normalModuleFactory }) => {
  80. compilation.dependencyFactories.set(
  81. RemoteToExternalDependency,
  82. normalModuleFactory
  83. );
  84. compilation.dependencyFactories.set(
  85. FallbackItemDependency,
  86. normalModuleFactory
  87. );
  88. compilation.dependencyFactories.set(
  89. FallbackDependency,
  90. new FallbackModuleFactory()
  91. );
  92. normalModuleFactory.hooks.factorize.tap(PLUGIN_NAME, (data) => {
  93. if (!data.request.includes("!")) {
  94. for (const [key, config] of remotes) {
  95. if (
  96. data.request.startsWith(`${key}`) &&
  97. (data.request.length === key.length ||
  98. data.request.charCodeAt(key.length) === slashCode)
  99. ) {
  100. return new RemoteModule(
  101. data.request,
  102. config.external.map((external, i) =>
  103. external.startsWith("internal ")
  104. ? external.slice(9)
  105. : `webpack/container/reference/${key}${
  106. i ? `/fallback-${i}` : ""
  107. }`
  108. ),
  109. `.${data.request.slice(key.length)}`,
  110. config.shareScope
  111. );
  112. }
  113. }
  114. }
  115. });
  116. compilation.hooks.runtimeRequirementInTree
  117. .for(RuntimeGlobals.ensureChunkHandlers)
  118. .tap(PLUGIN_NAME, (chunk, set) => {
  119. set.add(RuntimeGlobals.module);
  120. set.add(RuntimeGlobals.moduleFactoriesAddOnly);
  121. set.add(RuntimeGlobals.hasOwnProperty);
  122. set.add(RuntimeGlobals.initializeSharing);
  123. set.add(RuntimeGlobals.shareScopeMap);
  124. compilation.addRuntimeModule(chunk, new RemoteRuntimeModule());
  125. });
  126. const { chunkCondition } =
  127. ExternalModule.getCompilationHooks(compilation);
  128. // External modules issued by remote modules should be placed in entry chunks
  129. // to ensure they are loaded and initialize first
  130. chunkCondition.tap(
  131. PLUGIN_NAME,
  132. (chunk, compilation) =>
  133. compilation.chunkGraph.getNumberOfEntryModules(chunk) > 0
  134. );
  135. }
  136. );
  137. }
  138. }
  139. module.exports = ContainerReferencePlugin;