source: frontend/node_modules/webpack/lib/container/HoistContainerReferencesPlugin.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Zackary Jackson @ScriptedAlchemy
4*/
5
6"use strict";
7
8const AsyncDependenciesBlock = require("../AsyncDependenciesBlock");
9const ExternalModule = require("../ExternalModule");
10const { STAGE_ADVANCED } = require("../OptimizationStages");
11const memoize = require("../util/memoize");
12const { forEachRuntime } = require("../util/runtime");
13
14/** @typedef {import("../Compilation")} Compilation */
15/** @typedef {import("../Compiler")} Compiler */
16/** @typedef {import("../Dependency")} Dependency */
17/** @typedef {import("../Module")} Module */
18
19const getModuleFederationPlugin = memoize(() =>
20 require("./ModuleFederationPlugin")
21);
22
23const PLUGIN_NAME = "HoistContainerReferences";
24
25/**
26 * This class is used to hoist container references in the code.
27 */
28class HoistContainerReferences {
29 /**
30 * Apply the plugin to the compiler.
31 * @param {Compiler} compiler The webpack compiler instance.
32 */
33 apply(compiler) {
34 compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
35 const hooks =
36 getModuleFederationPlugin().getCompilationHooks(compilation);
37 /** @type {Set<Dependency>} */
38 const depsToTrace = new Set();
39 /** @type {Set<Dependency>} */
40 const entryExternalsToHoist = new Set();
41 hooks.addContainerEntryDependency.tap(PLUGIN_NAME, (dep) => {
42 depsToTrace.add(dep);
43 });
44 hooks.addFederationRuntimeDependency.tap(PLUGIN_NAME, (dep) => {
45 depsToTrace.add(dep);
46 });
47
48 compilation.hooks.addEntry.tap(PLUGIN_NAME, (entryDep) => {
49 if (entryDep.type === "entry") {
50 entryExternalsToHoist.add(entryDep);
51 }
52 });
53
54 // Hook into the optimizeChunks phase
55 compilation.hooks.optimizeChunks.tap(
56 {
57 name: PLUGIN_NAME,
58 // advanced stage is where SplitChunksPlugin runs.
59 stage: STAGE_ADVANCED + 1
60 },
61 (_chunks) => {
62 this.hoistModulesInChunks(
63 compilation,
64 depsToTrace,
65 entryExternalsToHoist
66 );
67 }
68 );
69 });
70 }
71
72 /**
73 * Hoist modules in chunks.
74 * @param {Compilation} compilation The webpack compilation instance.
75 * @param {Set<Dependency>} depsToTrace Set of container entry dependencies.
76 * @param {Set<Dependency>} entryExternalsToHoist Set of container entry dependencies to hoist.
77 */
78 hoistModulesInChunks(compilation, depsToTrace, entryExternalsToHoist) {
79 const { chunkGraph, moduleGraph } = compilation;
80
81 // loop over entry points
82 for (const dep of entryExternalsToHoist) {
83 const entryModule = moduleGraph.getModule(dep);
84 if (!entryModule) continue;
85 // get all the external module types and hoist them to the runtime chunk, this will get RemoteModule externals
86 const allReferencedModules = getAllReferencedModules(
87 compilation,
88 entryModule,
89 "external",
90 false
91 );
92
93 const containerRuntimes = chunkGraph.getModuleRuntimes(entryModule);
94 /** @type {Set<string>} */
95 const runtimes = new Set();
96
97 for (const runtimeSpec of containerRuntimes) {
98 forEachRuntime(runtimeSpec, (runtimeKey) => {
99 if (runtimeKey) {
100 runtimes.add(runtimeKey);
101 }
102 });
103 }
104
105 for (const runtime of runtimes) {
106 const runtimeChunk = compilation.namedChunks.get(runtime);
107 if (!runtimeChunk) continue;
108
109 for (const module of allReferencedModules) {
110 if (!chunkGraph.isModuleInChunk(module, runtimeChunk)) {
111 chunkGraph.connectChunkAndModule(runtimeChunk, module);
112 }
113 }
114 }
115 this.cleanUpChunks(compilation, allReferencedModules);
116 }
117
118 // handle container entry specifically
119 for (const dep of depsToTrace) {
120 const containerEntryModule = moduleGraph.getModule(dep);
121 if (!containerEntryModule) continue;
122 const allReferencedModules = getAllReferencedModules(
123 compilation,
124 containerEntryModule,
125 "initial",
126 false
127 );
128
129 const allRemoteReferences = getAllReferencedModules(
130 compilation,
131 containerEntryModule,
132 "external",
133 false
134 );
135
136 for (const remote of allRemoteReferences) {
137 allReferencedModules.add(remote);
138 }
139
140 const containerRuntimes =
141 chunkGraph.getModuleRuntimes(containerEntryModule);
142 /** @type {Set<string>} */
143 const runtimes = new Set();
144
145 for (const runtimeSpec of containerRuntimes) {
146 forEachRuntime(runtimeSpec, (runtimeKey) => {
147 if (runtimeKey) {
148 runtimes.add(runtimeKey);
149 }
150 });
151 }
152
153 for (const runtime of runtimes) {
154 const runtimeChunk = compilation.namedChunks.get(runtime);
155 if (!runtimeChunk) continue;
156
157 for (const module of allReferencedModules) {
158 if (!chunkGraph.isModuleInChunk(module, runtimeChunk)) {
159 chunkGraph.connectChunkAndModule(runtimeChunk, module);
160 }
161 }
162 }
163 this.cleanUpChunks(compilation, allReferencedModules);
164 }
165 }
166
167 /**
168 * Clean up chunks by disconnecting unused modules.
169 * @param {Compilation} compilation The webpack compilation instance.
170 * @param {Set<Module>} modules Set of modules to clean up.
171 */
172 cleanUpChunks(compilation, modules) {
173 const { chunkGraph } = compilation;
174 for (const module of modules) {
175 for (const chunk of chunkGraph.getModuleChunks(module)) {
176 if (!chunk.hasRuntime()) {
177 chunkGraph.disconnectChunkAndModule(chunk, module);
178 if (
179 chunkGraph.getNumberOfChunkModules(chunk) === 0 &&
180 chunkGraph.getNumberOfEntryModules(chunk) === 0
181 ) {
182 chunkGraph.disconnectChunk(chunk);
183 compilation.chunks.delete(chunk);
184 if (chunk.name) {
185 compilation.namedChunks.delete(chunk.name);
186 }
187 }
188 }
189 }
190 }
191 modules.clear();
192 }
193}
194
195/**
196 * Helper method to collect all referenced modules recursively.
197 * @param {Compilation} compilation The webpack compilation instance.
198 * @param {Module} module The module to start collecting from.
199 * @param {string} type The type of modules to collect ("initial", "external", or "all").
200 * @param {boolean} includeInitial Should include the referenced module passed
201 * @returns {Set<Module>} Set of collected modules.
202 */
203function getAllReferencedModules(compilation, module, type, includeInitial) {
204 const collectedModules = new Set(includeInitial ? [module] : []);
205 /** @type {WeakSet<Module>} */
206 const visitedModules = new WeakSet([module]);
207 /** @type {Module[]} */
208 const stack = [module];
209
210 while (stack.length > 0) {
211 const currentModule = stack.pop();
212 if (!currentModule) continue;
213
214 const outgoingConnections =
215 compilation.moduleGraph.getOutgoingConnections(currentModule);
216 if (outgoingConnections) {
217 for (const connection of outgoingConnections) {
218 const connectedModule = connection.module;
219
220 // Skip if module has already been visited
221 if (!connectedModule || visitedModules.has(connectedModule)) {
222 continue;
223 }
224
225 // Handle 'initial' type (skipping async blocks)
226 if (type === "initial") {
227 const parentBlock = compilation.moduleGraph.getParentBlock(
228 /** @type {Dependency} */
229 (connection.dependency)
230 );
231 if (parentBlock instanceof AsyncDependenciesBlock) {
232 continue;
233 }
234 }
235
236 // Handle 'external' type (collecting only external modules)
237 if (type === "external") {
238 if (connection.module instanceof ExternalModule) {
239 collectedModules.add(connectedModule);
240 }
241 } else {
242 // Handle 'all' or unspecified types
243 collectedModules.add(connectedModule);
244 }
245
246 // Add connected module to the stack and mark it as visited
247 visitedModules.add(connectedModule);
248 stack.push(connectedModule);
249 }
250 }
251 }
252
253 return collectedModules;
254}
255
256module.exports = HoistContainerReferences;
Note: See TracBrowser for help on using the repository browser.