source: frontend/node_modules/webpack/lib/cache/ResolverCachePlugin.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: 13.6 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 LazySet = require("../util/LazySet");
9const makeSerializable = require("../util/makeSerializable");
10
11/** @typedef {import("enhanced-resolve").ResolveContext} ResolveContext */
12/** @typedef {import("enhanced-resolve").ResolveOptions} ResolveOptions */
13/** @typedef {import("enhanced-resolve").ResolveRequest} ResolveRequest */
14/** @typedef {import("enhanced-resolve").Resolver} Resolver */
15/** @typedef {import("../CacheFacade").ItemCacheFacade} ItemCacheFacade */
16/** @typedef {import("../Compiler")} Compiler */
17/** @typedef {import("../FileSystemInfo")} FileSystemInfo */
18/** @typedef {import("../FileSystemInfo").Snapshot} Snapshot */
19/** @typedef {import("../FileSystemInfo").SnapshotOptions} SnapshotOptions */
20/** @typedef {import("../ResolverFactory").ResolveOptionsWithDependencyType} ResolveOptionsWithDependencyType */
21/** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
22/** @typedef {import("../serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
23
24/**
25 * Defines the sync hook type used by this module.
26 * @template T
27 * @typedef {import("tapable").SyncHook<T>} SyncHook
28 */
29
30/** @typedef {Set<string>} Dependencies */
31
32class CacheEntry {
33 /**
34 * Creates an instance of CacheEntry.
35 * @param {ResolveRequest} result result
36 * @param {Snapshot} snapshot snapshot
37 */
38 constructor(result, snapshot) {
39 this.result = result;
40 this.snapshot = snapshot;
41 }
42
43 /**
44 * Serializes this instance into the provided serializer context.
45 * @param {ObjectSerializerContext} context context
46 */
47 serialize({ write }) {
48 write(this.result);
49 write(this.snapshot);
50 }
51
52 /**
53 * Restores this instance from the provided deserializer context.
54 * @param {ObjectDeserializerContext} context context
55 */
56 deserialize({ read }) {
57 this.result = read();
58 this.snapshot = read();
59 }
60}
61
62makeSerializable(CacheEntry, "webpack/lib/cache/ResolverCachePlugin");
63
64/**
65 * Adds the provided set to the cache entry.
66 * @template T
67 * @param {Set<T> | LazySet<T>} set set to add items to
68 * @param {Set<T> | LazySet<T> | Iterable<T>} otherSet set to add items from
69 * @returns {void}
70 */
71const addAllToSet = (set, otherSet) => {
72 if (set instanceof LazySet) {
73 set.addAll(otherSet);
74 } else {
75 for (const item of otherSet) {
76 set.add(item);
77 }
78 }
79};
80
81/**
82 * Returns stringified version.
83 * @template {object} T
84 * @param {T} object an object
85 * @param {boolean} excludeContext if true, context is not included in string
86 * @returns {string} stringified version
87 */
88const objectToString = (object, excludeContext) => {
89 let str = "";
90 for (const key in object) {
91 if (excludeContext && key === "context") continue;
92 const value = object[key];
93 str +=
94 typeof value === "object" && value !== null
95 ? `|${key}=[${objectToString(value, false)}|]`
96 : `|${key}=|${value}`;
97 }
98 return str;
99};
100
101/** @typedef {NonNullable<ResolveContext["yield"]>} Yield */
102
103const PLUGIN_NAME = "ResolverCachePlugin";
104
105class ResolverCachePlugin {
106 /**
107 * Applies the plugin by registering its hooks on the compiler.
108 * @param {Compiler} compiler the compiler instance
109 * @returns {void}
110 */
111 apply(compiler) {
112 const cache = compiler.getCache(PLUGIN_NAME);
113 /** @type {FileSystemInfo} */
114 let fileSystemInfo;
115 /** @type {SnapshotOptions | undefined} */
116 let snapshotOptions;
117 let realResolves = 0;
118 let cachedResolves = 0;
119 let cacheInvalidResolves = 0;
120 let concurrentResolves = 0;
121 compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
122 snapshotOptions = compilation.options.snapshot.resolve;
123 fileSystemInfo = compilation.fileSystemInfo;
124 compilation.hooks.finishModules.tap(PLUGIN_NAME, () => {
125 if (realResolves + cachedResolves > 0) {
126 const logger = compilation.getLogger(`webpack.${PLUGIN_NAME}`);
127 logger.log(
128 `${Math.round(
129 (100 * realResolves) / (realResolves + cachedResolves)
130 )}% really resolved (${realResolves} real resolves with ${cacheInvalidResolves} cached but invalid, ${cachedResolves} cached valid, ${concurrentResolves} concurrent)`
131 );
132 realResolves = 0;
133 cachedResolves = 0;
134 cacheInvalidResolves = 0;
135 concurrentResolves = 0;
136 }
137 });
138 });
139
140 /** @typedef {(err?: Error | null, resolveRequest?: ResolveRequest | null) => void} Callback */
141 /** @typedef {ResolveRequest & { _ResolverCachePluginCacheMiss: true }} ResolveRequestWithCacheMiss */
142
143 /**
144 * Processes the provided item cache.
145 * @param {ItemCacheFacade} itemCache cache
146 * @param {Resolver} resolver the resolver
147 * @param {ResolveContext} resolveContext context for resolving meta info
148 * @param {ResolveRequest} request the request info object
149 * @param {Callback} callback callback function
150 * @returns {void}
151 */
152 const doRealResolve = (
153 itemCache,
154 resolver,
155 resolveContext,
156 request,
157 callback
158 ) => {
159 realResolves++;
160 const newRequest =
161 /** @type {ResolveRequestWithCacheMiss} */
162 ({
163 _ResolverCachePluginCacheMiss: true,
164 ...request
165 });
166 /** @type {ResolveContext} */
167 const newResolveContext = {
168 ...resolveContext,
169 stack: new Set(),
170 missingDependencies: new LazySet(),
171 fileDependencies: new LazySet(),
172 contextDependencies: new LazySet()
173 };
174 /** @type {ResolveRequest[] | undefined} */
175 let yieldResult;
176 let withYield = false;
177 if (typeof newResolveContext.yield === "function") {
178 yieldResult = [];
179 withYield = true;
180 newResolveContext.yield = (obj) =>
181 /** @type {ResolveRequest[]} */
182 (yieldResult).push(obj);
183 }
184 /**
185 * Processes the provided key.
186 * @param {"fileDependencies" | "contextDependencies" | "missingDependencies"} key key
187 */
188 const propagate = (key) => {
189 if (resolveContext[key]) {
190 addAllToSet(
191 /** @type {Dependencies} */ (resolveContext[key]),
192 /** @type {Dependencies} */ (newResolveContext[key])
193 );
194 }
195 };
196 const resolveTime = Date.now();
197 resolver.doResolve(
198 resolver.hooks.resolve,
199 newRequest,
200 "Cache miss",
201 newResolveContext,
202 (err, result) => {
203 propagate("fileDependencies");
204 propagate("contextDependencies");
205 propagate("missingDependencies");
206 if (err) return callback(err);
207 const fileDependencies = newResolveContext.fileDependencies;
208 const contextDependencies = newResolveContext.contextDependencies;
209 const missingDependencies = newResolveContext.missingDependencies;
210 fileSystemInfo.createSnapshot(
211 resolveTime,
212 /** @type {Dependencies} */
213 (fileDependencies),
214 /** @type {Dependencies} */
215 (contextDependencies),
216 /** @type {Dependencies} */
217 (missingDependencies),
218 snapshotOptions,
219 (err, snapshot) => {
220 if (err) return callback(err);
221 const resolveResult = withYield ? yieldResult : result;
222 // since we intercept resolve hook
223 // we still can get result in callback
224 if (withYield && result) {
225 /** @type {ResolveRequest[]} */
226 (yieldResult).push(result);
227 }
228 if (!snapshot) {
229 if (resolveResult) {
230 return callback(
231 null,
232 /** @type {ResolveRequest} */
233 (resolveResult)
234 );
235 }
236 return callback();
237 }
238 itemCache.store(
239 new CacheEntry(
240 /** @type {ResolveRequest} */
241 (resolveResult),
242 snapshot
243 ),
244 (storeErr) => {
245 if (storeErr) return callback(storeErr);
246 if (resolveResult) {
247 return callback(
248 null,
249 /** @type {ResolveRequest} */
250 (resolveResult)
251 );
252 }
253 callback();
254 }
255 );
256 }
257 );
258 }
259 );
260 };
261 compiler.resolverFactory.hooks.resolver.intercept({
262 factory(type, _hook) {
263 /** @typedef {(err?: Error, resolveRequest?: ResolveRequest) => void} ActiveRequest */
264 /** @type {Map<string, ActiveRequest[]>} */
265 const activeRequests = new Map();
266 /** @type {Map<string, [ActiveRequest[], Yield[]]>} */
267 const activeRequestsWithYield = new Map();
268 const hook =
269 /** @type {SyncHook<[Resolver, ResolveOptions, ResolveOptionsWithDependencyType]>} */
270 (_hook);
271 hook.tap(PLUGIN_NAME, (resolver, options, userOptions) => {
272 if (
273 /** @type {ResolveOptions & { cache: boolean }} */
274 (options).cache !== true
275 ) {
276 return;
277 }
278 const optionsIdent = objectToString(userOptions, false);
279 const cacheWithContext =
280 options.cacheWithContext !== undefined
281 ? options.cacheWithContext
282 : false;
283 resolver.hooks.resolve.tapAsync(
284 {
285 name: PLUGIN_NAME,
286 stage: -100
287 },
288 (request, resolveContext, callback) => {
289 if (
290 /** @type {ResolveRequestWithCacheMiss} */
291 (request)._ResolverCachePluginCacheMiss ||
292 !fileSystemInfo
293 ) {
294 return callback();
295 }
296 const withYield = typeof resolveContext.yield === "function";
297 const identifier = `${type}${
298 withYield ? "|yield" : "|default"
299 }${optionsIdent}${objectToString(request, !cacheWithContext)}`;
300
301 if (withYield) {
302 const activeRequest = activeRequestsWithYield.get(identifier);
303 if (activeRequest) {
304 activeRequest[0].push(callback);
305 activeRequest[1].push(
306 /** @type {Yield} */
307 (resolveContext.yield)
308 );
309 return;
310 }
311 } else {
312 const activeRequest = activeRequests.get(identifier);
313 if (activeRequest) {
314 activeRequest.push(callback);
315 return;
316 }
317 }
318 const itemCache = cache.getItemCache(identifier, null);
319 /** @type {Callback[] | false | undefined} */
320 let callbacks;
321 /** @type {Yield[] | undefined} */
322 let yields;
323
324 /**
325 * @type {(err?: Error | null, result?: ResolveRequest | ResolveRequest[] | null) => void}
326 */
327 const done = withYield
328 ? (err, result) => {
329 if (callbacks === undefined) {
330 if (err) {
331 callback(err);
332 } else {
333 if (result) {
334 for (const r of /** @type {ResolveRequest[]} */ (
335 result
336 )) {
337 /** @type {Yield} */
338 (resolveContext.yield)(r);
339 }
340 }
341 callback(null, null);
342 }
343 yields = undefined;
344 callbacks = false;
345 } else {
346 const definedCallbacks =
347 /** @type {Callback[]} */
348 (callbacks);
349
350 if (err) {
351 for (const cb of definedCallbacks) cb(err);
352 } else {
353 for (let i = 0; i < definedCallbacks.length; i++) {
354 const cb = definedCallbacks[i];
355 const yield_ = /** @type {Yield[]} */ (yields)[i];
356 if (result) {
357 for (const r of /** @type {ResolveRequest[]} */ (
358 result
359 )) {
360 yield_(r);
361 }
362 }
363 cb(null, null);
364 }
365 }
366 activeRequestsWithYield.delete(identifier);
367 yields = undefined;
368 callbacks = false;
369 }
370 }
371 : (err, result) => {
372 if (callbacks === undefined) {
373 callback(err, /** @type {ResolveRequest} */ (result));
374 callbacks = false;
375 } else {
376 for (const callback of /** @type {Callback[]} */ (
377 callbacks
378 )) {
379 callback(err, /** @type {ResolveRequest} */ (result));
380 }
381 activeRequests.delete(identifier);
382 callbacks = false;
383 }
384 };
385 /**
386 * Process cache result.
387 * @param {(Error | null)=} err error if any
388 * @param {(CacheEntry | null)=} cacheEntry cache entry
389 * @returns {void}
390 */
391 const processCacheResult = (err, cacheEntry) => {
392 if (err) return done(err);
393
394 if (cacheEntry) {
395 const { snapshot, result } = cacheEntry;
396 fileSystemInfo.checkSnapshotValid(snapshot, (err, valid) => {
397 if (err || !valid) {
398 cacheInvalidResolves++;
399 return doRealResolve(
400 itemCache,
401 resolver,
402 resolveContext,
403 request,
404 done
405 );
406 }
407 cachedResolves++;
408 if (resolveContext.missingDependencies) {
409 addAllToSet(
410 /** @type {Dependencies} */
411 (resolveContext.missingDependencies),
412 snapshot.getMissingIterable()
413 );
414 }
415 if (resolveContext.fileDependencies) {
416 addAllToSet(
417 /** @type {Dependencies} */
418 (resolveContext.fileDependencies),
419 snapshot.getFileIterable()
420 );
421 }
422 if (resolveContext.contextDependencies) {
423 addAllToSet(
424 /** @type {Dependencies} */
425 (resolveContext.contextDependencies),
426 snapshot.getContextIterable()
427 );
428 }
429 done(null, result);
430 });
431 } else {
432 doRealResolve(
433 itemCache,
434 resolver,
435 resolveContext,
436 request,
437 done
438 );
439 }
440 };
441 itemCache.get(processCacheResult);
442 if (withYield && callbacks === undefined) {
443 callbacks = [callback];
444 yields = [/** @type {Yield} */ (resolveContext.yield)];
445 activeRequestsWithYield.set(identifier, [callbacks, yields]);
446 } else if (callbacks === undefined) {
447 callbacks = [callback];
448 activeRequests.set(identifier, callbacks);
449 }
450 }
451 );
452 });
453 return hook;
454 }
455 });
456 }
457}
458
459module.exports = ResolverCachePlugin;
Note: See TracBrowser for help on using the repository browser.