source: frontend/node_modules/webpack/lib/sharing/ConsumeSharedPlugin.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 12.0 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const RuntimeGlobals = require("../RuntimeGlobals");
9const { parseOptions } = require("../container/options");
10const ModuleNotFoundError = require("../errors/ModuleNotFoundError");
11const WebpackError = require("../errors/WebpackError");
12const LazySet = require("../util/LazySet");
13const { parseRange } = require("../util/semver");
14const ConsumeSharedFallbackDependency = require("./ConsumeSharedFallbackDependency");
15const ConsumeSharedModule = require("./ConsumeSharedModule");
16const ConsumeSharedRuntimeModule = require("./ConsumeSharedRuntimeModule");
17const ProvideForSharedDependency = require("./ProvideForSharedDependency");
18const { resolveMatchedConfigs } = require("./resolveMatchedConfigs");
19const {
20 getDescriptionFile,
21 getRequiredVersionFromDescriptionFile,
22 isRequiredVersion
23} = require("./utils");
24
25/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
26/** @typedef {import("../../declarations/plugins/sharing/ConsumeSharedPlugin").ConsumeSharedPluginOptions} ConsumeSharedPluginOptions */
27/** @typedef {import("../Compiler")} Compiler */
28/** @typedef {import("../Compilation").FileSystemDependencies} FileSystemDependencies */
29/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
30/** @typedef {import("../util/semver").SemVerRange} SemVerRange */
31/** @typedef {import("./ConsumeSharedModule").ConsumeOptions} ConsumeOptions */
32/** @typedef {import("./utils").DescriptionFile} DescriptionFile */
33
34/** @type {ResolveOptionsWithDependencyType} */
35const RESOLVE_OPTIONS = { dependencyType: "esm" };
36const PLUGIN_NAME = "ConsumeSharedPlugin";
37
38class ConsumeSharedPlugin {
39 /**
40 * Creates an instance of ConsumeSharedPlugin.
41 * @param {ConsumeSharedPluginOptions} options options
42 */
43 constructor(options) {
44 this.options = options;
45 }
46
47 /**
48 * Applies the plugin by registering its hooks on the compiler.
49 * @param {Compiler} compiler the compiler instance
50 * @returns {void}
51 */
52 apply(compiler) {
53 // TODO webpack 6 remove string support
54 if (typeof this.options !== "string") {
55 compiler.hooks.validate.tap(PLUGIN_NAME, () => {
56 compiler.validate(
57 () =>
58 require("../../schemas/plugins/sharing/ConsumeSharedPlugin.json"),
59 this.options,
60 {
61 name: "Consume Shared Plugin",
62 baseDataPath: "options"
63 },
64 (options) =>
65 require("../../schemas/plugins/sharing/ConsumeSharedPlugin.check")(
66 options
67 )
68 );
69 });
70 }
71
72 /** @type {[string, ConsumeOptions][]} */
73 const consumes = parseOptions(
74 this.options.consumes,
75 (item, key) => {
76 if (Array.isArray(item)) throw new Error("Unexpected array in options");
77 /** @type {ConsumeOptions} */
78 const result =
79 item === key || !isRequiredVersion(item)
80 ? // item is a request/key
81 {
82 import: key,
83 shareScope: this.options.shareScope || "default",
84 shareKey: key,
85 requiredVersion: undefined,
86 packageName: undefined,
87 strictVersion: false,
88 singleton: false,
89 eager: false
90 }
91 : // key is a request/key
92 // item is a version
93 {
94 import: key,
95 shareScope: this.options.shareScope || "default",
96 shareKey: key,
97 requiredVersion: parseRange(item),
98 strictVersion: true,
99 packageName: undefined,
100 singleton: false,
101 eager: false
102 };
103 return result;
104 },
105 (item, key) => ({
106 import: item.import === false ? undefined : item.import || key,
107 shareScope: item.shareScope || this.options.shareScope || "default",
108 shareKey: item.shareKey || key,
109 requiredVersion:
110 typeof item.requiredVersion === "string"
111 ? parseRange(item.requiredVersion)
112 : item.requiredVersion,
113 strictVersion:
114 typeof item.strictVersion === "boolean"
115 ? item.strictVersion
116 : item.import !== false && !item.singleton,
117 packageName: item.packageName,
118 singleton: Boolean(item.singleton),
119 eager: Boolean(item.eager)
120 })
121 );
122
123 compiler.hooks.thisCompilation.tap(
124 PLUGIN_NAME,
125 (compilation, { normalModuleFactory }) => {
126 compilation.dependencyFactories.set(
127 ConsumeSharedFallbackDependency,
128 normalModuleFactory
129 );
130
131 /** @typedef {Map<string, ConsumeOptions>} Consumes */
132
133 /** @type {Consumes} */
134 let unresolvedConsumes;
135 /** @type {Consumes} */
136 let resolvedConsumes;
137 /** @type {Consumes} */
138 let prefixedConsumes;
139 const promise = resolveMatchedConfigs(compilation, consumes).then(
140 ({ resolved, unresolved, prefixed }) => {
141 resolvedConsumes = resolved;
142 unresolvedConsumes = unresolved;
143 prefixedConsumes = prefixed;
144 }
145 );
146
147 const resolver = compilation.resolverFactory.get(
148 "normal",
149 RESOLVE_OPTIONS
150 );
151
152 /**
153 * Creates a consume shared module.
154 * @param {string} context issuer directory
155 * @param {string} request request
156 * @param {ConsumeOptions} config options
157 * @returns {Promise<ConsumeSharedModule>} create module
158 */
159 const createConsumeSharedModule = (context, request, config) => {
160 /**
161 * Required version warning.
162 * @param {string} details details
163 */
164 const requiredVersionWarning = (details) => {
165 const error = new WebpackError(
166 `No required version specified and unable to automatically determine one. ${details}`
167 );
168 error.file = `shared module ${request}`;
169 compilation.warnings.push(error);
170 };
171 const directFallback =
172 config.import &&
173 /^(?:\.\.?(?:\/|$)|\/|[A-Z]:|\\\\)/i.test(config.import);
174 return Promise.all([
175 new Promise(
176 /**
177 * Handles the callback logic for this hook.
178 * @param {(value?: string) => void} resolve resolve
179 */
180 (resolve) => {
181 if (!config.import) {
182 resolve();
183 return;
184 }
185 /** @type {ResolveContext & { fileDependencies: FileSystemDependencies, contextDependencies: FileSystemDependencies, missingDependencies: FileSystemDependencies }} */
186 const resolveContext = {
187 fileDependencies: new LazySet(),
188 contextDependencies: new LazySet(),
189 missingDependencies: new LazySet()
190 };
191 resolver.resolve(
192 {},
193 directFallback ? compiler.context : context,
194 config.import,
195 resolveContext,
196 (err, result) => {
197 compilation.contextDependencies.addAll(
198 resolveContext.contextDependencies
199 );
200 compilation.fileDependencies.addAll(
201 resolveContext.fileDependencies
202 );
203 compilation.missingDependencies.addAll(
204 resolveContext.missingDependencies
205 );
206 if (err) {
207 compilation.errors.push(
208 new ModuleNotFoundError(null, err, {
209 name: `resolving fallback for shared module ${request}`
210 })
211 );
212 return resolve();
213 }
214 resolve(/** @type {string} */ (result));
215 }
216 );
217 }
218 ),
219 new Promise(
220 /**
221 * Handles the name callback for this hook.
222 * @param {(value?: SemVerRange) => void} resolve resolve
223 */
224 (resolve) => {
225 if (config.requiredVersion !== undefined) {
226 resolve(/** @type {SemVerRange} */ (config.requiredVersion));
227 return;
228 }
229 let packageName = config.packageName;
230 if (packageName === undefined) {
231 if (/^(?:\/|[A-Z]:|\\\\)/i.test(request)) {
232 // For relative or absolute requests we don't automatically use a packageName.
233 // If wished one can specify one with the packageName option.
234 resolve();
235 return;
236 }
237 const match = /^(?:@[^\\/]+[\\/])?[^\\/]+/.exec(request);
238 if (!match) {
239 requiredVersionWarning(
240 "Unable to extract the package name from request."
241 );
242 resolve();
243 return;
244 }
245 packageName = match[0];
246 }
247
248 getDescriptionFile(
249 compilation.inputFileSystem,
250 context,
251 ["package.json"],
252 (err, result, checkedDescriptionFilePaths) => {
253 if (err) {
254 requiredVersionWarning(
255 `Unable to read description file: ${err}`
256 );
257 return resolve();
258 }
259 const { data } =
260 /** @type {DescriptionFile} */
261 (result || {});
262 if (!data) {
263 if (checkedDescriptionFilePaths) {
264 requiredVersionWarning(
265 [
266 `Unable to find required version for "${packageName}" in description file/s`,
267 checkedDescriptionFilePaths.join("\n"),
268 "It need to be in dependencies, devDependencies or peerDependencies."
269 ].join("\n")
270 );
271 } else {
272 requiredVersionWarning(
273 `Unable to find description file in ${context}.`
274 );
275 }
276
277 return resolve();
278 }
279 if (data.name === packageName) {
280 // Package self-referencing
281 return resolve();
282 }
283 const requiredVersion =
284 getRequiredVersionFromDescriptionFile(data, packageName);
285
286 if (requiredVersion) {
287 return resolve(parseRange(requiredVersion));
288 }
289
290 resolve();
291 },
292 (result) => {
293 if (!result) return false;
294 const maybeRequiredVersion =
295 getRequiredVersionFromDescriptionFile(
296 result.data,
297 packageName
298 );
299 return (
300 result.data.name === packageName ||
301 typeof maybeRequiredVersion === "string"
302 );
303 }
304 );
305 }
306 )
307 ]).then(
308 ([importResolved, requiredVersion]) =>
309 new ConsumeSharedModule(
310 directFallback ? compiler.context : context,
311 {
312 ...config,
313 importResolved,
314 import: importResolved ? config.import : undefined,
315 requiredVersion
316 }
317 )
318 );
319 };
320
321 normalModuleFactory.hooks.factorize.tapPromise(
322 PLUGIN_NAME,
323 ({ context, request, dependencies }) =>
324 // wait for resolving to be complete
325 promise.then(() => {
326 if (
327 dependencies[0] instanceof ConsumeSharedFallbackDependency ||
328 dependencies[0] instanceof ProvideForSharedDependency
329 ) {
330 return;
331 }
332 const match = unresolvedConsumes.get(request);
333 if (match !== undefined) {
334 return createConsumeSharedModule(context, request, match);
335 }
336 for (const [prefix, options] of prefixedConsumes) {
337 if (request.startsWith(prefix)) {
338 const remainder = request.slice(prefix.length);
339 return createConsumeSharedModule(context, request, {
340 ...options,
341 import: options.import
342 ? options.import + remainder
343 : undefined,
344 shareKey: options.shareKey + remainder
345 });
346 }
347 }
348 })
349 );
350 normalModuleFactory.hooks.createModule.tapPromise(
351 PLUGIN_NAME,
352 ({ resource }, { context, dependencies }) => {
353 if (
354 dependencies[0] instanceof ConsumeSharedFallbackDependency ||
355 dependencies[0] instanceof ProvideForSharedDependency
356 ) {
357 return Promise.resolve();
358 }
359 const options = resolvedConsumes.get(resource);
360 if (options !== undefined) {
361 return createConsumeSharedModule(context, resource, options);
362 }
363 return Promise.resolve();
364 }
365 );
366 compilation.hooks.additionalTreeRuntimeRequirements.tap(
367 PLUGIN_NAME,
368 (chunk, set) => {
369 set.add(RuntimeGlobals.module);
370 set.add(RuntimeGlobals.moduleCache);
371 set.add(RuntimeGlobals.moduleFactoriesAddOnly);
372 set.add(RuntimeGlobals.shareScopeMap);
373 set.add(RuntimeGlobals.initializeSharing);
374 set.add(RuntimeGlobals.hasOwnProperty);
375 compilation.addRuntimeModule(
376 chunk,
377 new ConsumeSharedRuntimeModule(set)
378 );
379 }
380 );
381 }
382 );
383 }
384}
385
386module.exports = ConsumeSharedPlugin;
Note: See TracBrowser for help on using the repository browser.