source: frontend/node_modules/webpack/lib/ExternalModuleFactoryPlugin.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: 12.9 KB
RevLine 
[9af201e]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9const ExternalModule = require("./ExternalModule");
10const { ASSET_URL_TYPE } = require("./ModuleSourceTypeConstants");
11const ContextElementDependency = require("./dependencies/ContextElementDependency");
12const CssImportDependency = require("./dependencies/CssImportDependency");
13const CssUrlDependency = require("./dependencies/CssUrlDependency");
14const HarmonyImportDependency = require("./dependencies/HarmonyImportDependency");
15const ImportDependency = require("./dependencies/ImportDependency");
16const { cachedSetProperty, resolveByProperty } = require("./util/cleverMerge");
17
18/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
19/** @typedef {import("../declarations/WebpackOptions").ResolveOptions} ResolveOptions */
20/** @typedef {import("../declarations/WebpackOptions").ExternalsType} ExternalsType */
21/** @typedef {import("../declarations/WebpackOptions").ExternalItem} ExternalItem */
22/** @typedef {import("../declarations/WebpackOptions").ExternalItemValue} ExternalItemValue */
23/** @typedef {import("../declarations/WebpackOptions").ExternalItemObjectKnown} ExternalItemObjectKnown */
24/** @typedef {import("../declarations/WebpackOptions").ExternalItemObjectUnknown} ExternalItemObjectUnknown */
25/** @typedef {import("../declarations/WebpackOptions").Externals} Externals */
26/** @typedef {import("./Dependency")} Dependency */
27/** @typedef {import("./ExternalModule").DependencyMeta} DependencyMeta */
28/** @typedef {import("./ModuleFactory").IssuerLayer} IssuerLayer */
29/** @typedef {import("./ModuleFactory").ModuleFactoryCreateDataContextInfo} ModuleFactoryCreateDataContextInfo */
30/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
31
32/** @typedef {((context: string, request: string, callback: (err?: Error | null, result?: string | false, resolveRequest?: import("enhanced-resolve").ResolveRequest) => void) => void)} ExternalItemFunctionDataGetResolveCallbackResult */
33/** @typedef {((context: string, request: string) => Promise<string>)} ExternalItemFunctionDataGetResolveResult */
34/** @typedef {(options?: ResolveOptions) => ExternalItemFunctionDataGetResolveCallbackResult | ExternalItemFunctionDataGetResolveResult} ExternalItemFunctionDataGetResolve */
35
36/**
37 * Defines the external item function data type used by this module.
38 * @typedef {object} ExternalItemFunctionData
39 * @property {string} context the directory in which the request is placed
40 * @property {ModuleFactoryCreateDataContextInfo} contextInfo contextual information
41 * @property {string} dependencyType the category of the referencing dependency
42 * @property {ExternalItemFunctionDataGetResolve} getResolve get a resolve function with the current resolver options
43 * @property {string} request the request as written by the user in the require/import expression/statement
44 */
45
46/** @typedef {((data: ExternalItemFunctionData, callback: (err?: (Error | null), result?: ExternalItemValue) => void) => void)} ExternalItemFunctionCallback */
47/** @typedef {((data: import("../lib/ExternalModuleFactoryPlugin").ExternalItemFunctionData) => Promise<ExternalItemValue>)} ExternalItemFunctionPromise */
48
49const UNSPECIFIED_EXTERNAL_TYPE_REGEXP = /^[a-z0-9-]+ /;
50const EMPTY_RESOLVE_OPTIONS = {};
51
52// TODO webpack 6 remove this
53const callDeprecatedExternals = util.deprecate(
54 /**
55 * Handles the callback logic for this hook.
56 * @param {EXPECTED_FUNCTION} externalsFunction externals function
57 * @param {string} context context
58 * @param {string} request request
59 * @param {(err: Error | null | undefined, value: ExternalValue | undefined, ty: ExternalsType | undefined) => void} cb cb
60 */
61 (externalsFunction, context, request, cb) => {
62 // eslint-disable-next-line no-useless-call
63 externalsFunction.call(null, context, request, cb);
64 },
65 "The externals-function should be defined like ({context, request}, cb) => { ... }",
66 "DEP_WEBPACK_EXTERNALS_FUNCTION_PARAMETERS"
67);
68
69/** @typedef {(layer: string | null) => ExternalItem} ExternalItemByLayerFn */
70/** @typedef {ExternalItemObjectKnown & ExternalItemObjectUnknown} ExternalItemObject */
71
72/**
73 * Defines the external weak cache type used by this module.
74 * @template {ExternalItemObject} T
75 * @typedef {WeakMap<T, Map<IssuerLayer, Omit<T, "byLayer">>>} ExternalWeakCache
76 */
77
78/** @type {ExternalWeakCache<ExternalItemObject>} */
79const cache = new WeakMap();
80
81/**
82 * Returns result.
83 * @param {ExternalItemObject} obj obj
84 * @param {IssuerLayer} layer layer
85 * @returns {Omit<ExternalItemObject, "byLayer">} result
86 */
87const resolveLayer = (obj, layer) => {
88 let map = cache.get(obj);
89 if (map === undefined) {
90 map = new Map();
91 cache.set(obj, map);
92 } else {
93 const cacheEntry = map.get(layer);
94 if (cacheEntry !== undefined) return cacheEntry;
95 }
96 const result = resolveByProperty(obj, "byLayer", layer);
97 map.set(layer, result);
98 return result;
99};
100
101/** @typedef {string | string[] | boolean | Record<string, string | string[]>} ExternalValue */
102
103const PLUGIN_NAME = "ExternalModuleFactoryPlugin";
104
105class ExternalModuleFactoryPlugin {
106 /**
107 * Creates an instance of ExternalModuleFactoryPlugin.
108 * @param {ExternalsType | ((dependency: Dependency) => ExternalsType)} type default external type
109 * @param {Externals} externals externals config
110 */
111 constructor(type, externals) {
112 this.type = type;
113 this.externals = externals;
114 }
115
116 /**
117 * Applies the plugin by registering its hooks on the compiler.
118 * @param {NormalModuleFactory} normalModuleFactory the normal module factory
119 * @returns {void}
120 */
121 apply(normalModuleFactory) {
122 const globalType = this.type;
123 normalModuleFactory.hooks.factorize.tapAsync(
124 PLUGIN_NAME,
125 (data, callback) => {
126 const context = data.context;
127 const contextInfo = data.contextInfo;
128 const dependency = data.dependencies[0];
129 const dependencyType = data.dependencyType;
130
131 /** @typedef {(err?: Error | null, externalModule?: ExternalModule) => void} HandleExternalCallback */
132
133 /**
134 * Processes the provided value.
135 * @param {ExternalValue} value the external config
136 * @param {ExternalsType | undefined} type type of external
137 * @param {HandleExternalCallback} callback callback
138 * @returns {void}
139 */
140 const handleExternal = (value, type, callback) => {
141 if (value === false) {
142 // Not externals, fallback to original factory
143 return callback();
144 }
145 /** @type {ExternalValue} */
146 let externalConfig = value === true ? dependency.request : value;
147 // When no explicit type is specified, extract it from the externalConfig
148 if (type === undefined) {
149 if (
150 typeof externalConfig === "string" &&
151 UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig)
152 ) {
153 const idx = externalConfig.indexOf(" ");
154 type =
155 /** @type {ExternalsType} */
156 (externalConfig.slice(0, idx));
157 externalConfig = externalConfig.slice(idx + 1);
158 } else if (
159 Array.isArray(externalConfig) &&
160 externalConfig.length > 0 &&
161 UNSPECIFIED_EXTERNAL_TYPE_REGEXP.test(externalConfig[0])
162 ) {
163 const firstItem = externalConfig[0];
164 const idx = firstItem.indexOf(" ");
165 type = /** @type {ExternalsType} */ (firstItem.slice(0, idx));
166 externalConfig = [
167 firstItem.slice(idx + 1),
168 ...externalConfig.slice(1)
169 ];
170 }
171 }
172
173 const defaultType =
174 typeof globalType === "function"
175 ? globalType(dependency)
176 : globalType;
177 const resolvedType = type || defaultType;
178
179 // TODO make it pluggable/add hooks to `ExternalModule` to allow output modules own externals?
180 /** @type {DependencyMeta | undefined} */
181 let dependencyMeta;
182
183 if (
184 dependency instanceof HarmonyImportDependency ||
185 dependency instanceof ImportDependency ||
186 dependency instanceof ContextElementDependency
187 ) {
188 const externalType =
189 dependency instanceof HarmonyImportDependency
190 ? "module"
191 : dependency instanceof ImportDependency
192 ? "import"
193 : undefined;
194
195 dependencyMeta = {
196 attributes: dependency.attributes,
197 phase:
198 dependency instanceof HarmonyImportDependency ||
199 dependency instanceof ImportDependency
200 ? dependency.phase
201 : undefined,
202 externalType
203 };
204 } else if (dependency instanceof CssImportDependency) {
205 dependencyMeta = {
206 layer: dependency.layer,
207 supports: dependency.supports,
208 media: dependency.media
209 };
210 }
211
212 if (
213 resolvedType === "asset" &&
214 dependency instanceof CssUrlDependency
215 ) {
216 dependencyMeta = { sourceType: ASSET_URL_TYPE };
217 }
218
219 callback(
220 null,
221 new ExternalModule(
222 externalConfig,
223 resolvedType,
224 dependency.request,
225 dependencyMeta
226 )
227 );
228 };
229
230 /**
231 * Processes the provided external.
232 * @param {Externals} externals externals config
233 * @param {HandleExternalCallback} callback callback
234 * @returns {void}
235 */
236 const handleExternals = (externals, callback) => {
237 if (typeof externals === "string") {
238 if (externals === dependency.request) {
239 return handleExternal(dependency.request, undefined, callback);
240 }
241 } else if (Array.isArray(externals)) {
242 let i = 0;
243 const next = () => {
244 /** @type {boolean | undefined} */
245 let asyncFlag;
246 /**
247 * Handle externals and callback.
248 * @param {(Error | null)=} err err
249 * @param {ExternalModule=} module module
250 * @returns {void}
251 */
252 const handleExternalsAndCallback = (err, module) => {
253 if (err) return callback(err);
254 if (!module) {
255 if (asyncFlag) {
256 asyncFlag = false;
257 return;
258 }
259 return next();
260 }
261 callback(null, module);
262 };
263
264 do {
265 asyncFlag = true;
266 if (i >= externals.length) return callback();
267 handleExternals(externals[i++], handleExternalsAndCallback);
268 } while (!asyncFlag);
269 asyncFlag = false;
270 };
271
272 next();
273 return;
274 } else if (externals instanceof RegExp) {
275 if (externals.test(dependency.request)) {
276 return handleExternal(dependency.request, undefined, callback);
277 }
278 } else if (typeof externals === "function") {
279 /**
280 * Processes the provided err.
281 * @param {Error | null | undefined} err err
282 * @param {ExternalValue=} value value
283 * @param {ExternalsType=} type type
284 * @returns {void}
285 */
286 const cb = (err, value, type) => {
287 if (err) return callback(err);
288 if (value !== undefined) {
289 handleExternal(value, type, callback);
290 } else {
291 callback();
292 }
293 };
294 if (externals.length === 3) {
295 // TODO webpack 6 remove this
296 callDeprecatedExternals(
297 externals,
298 context,
299 dependency.request,
300 cb
301 );
302 } else {
303 const promise = externals(
304 {
305 context,
306 request: dependency.request,
307 dependencyType,
308 contextInfo,
309 getResolve: (options) => (context, request, callback) => {
310 /** @type {ResolveContext} */
311 const resolveContext = {
312 fileDependencies: data.fileDependencies,
313 missingDependencies: data.missingDependencies,
314 contextDependencies: data.contextDependencies
315 };
316 let resolver = normalModuleFactory.getResolver(
317 "normal",
318 dependencyType
319 ? cachedSetProperty(
320 data.resolveOptions || EMPTY_RESOLVE_OPTIONS,
321 "dependencyType",
322 dependencyType
323 )
324 : data.resolveOptions
325 );
326 if (options) resolver = resolver.withOptions(options);
327 if (callback) {
328 resolver.resolve(
329 {},
330 context,
331 request,
332 resolveContext,
333 callback
334 );
335 } else {
336 return new Promise((resolve, reject) => {
337 resolver.resolve(
338 {},
339 context,
340 request,
341 resolveContext,
342 (err, result) => {
343 if (err) reject(err);
344 else resolve(result);
345 }
346 );
347 });
348 }
349 }
350 },
351 cb
352 );
353 if (promise && promise.then) {
354 promise.then((r) => cb(null, r), cb);
355 }
356 }
357 return;
358 } else if (typeof externals === "object") {
359 const resolvedExternals = resolveLayer(
360 externals,
361 /** @type {IssuerLayer} */
362 (contextInfo.issuerLayer)
363 );
364 if (
365 Object.prototype.hasOwnProperty.call(
366 resolvedExternals,
367 dependency.request
368 )
369 ) {
370 return handleExternal(
371 resolvedExternals[dependency.request],
372 undefined,
373 callback
374 );
375 }
376 }
377 callback();
378 };
379
380 handleExternals(this.externals, callback);
381 }
382 );
383 }
384}
385
386module.exports = ExternalModuleFactoryPlugin;
Note: See TracBrowser for help on using the repository browser.