ContainerReferencePlugin.js 4.6 KB

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