source: frontend/node_modules/webpack/lib/cache/IdleFileCachePlugin.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.1 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 Cache = require("../Cache");
9const ProgressPlugin = require("../ProgressPlugin");
10
11/** @typedef {import("../Compiler")} Compiler */
12/** @typedef {import("./PackFileCacheStrategy")} PackFileCacheStrategy */
13
14const BUILD_DEPENDENCIES_KEY = Symbol("build dependencies key");
15const PLUGIN_NAME = "IdleFileCachePlugin";
16
17class IdleFileCachePlugin {
18 /**
19 * Creates an instance of IdleFileCachePlugin.
20 * @param {PackFileCacheStrategy} strategy cache strategy
21 * @param {number} idleTimeout timeout
22 * @param {number} idleTimeoutForInitialStore initial timeout
23 * @param {number} idleTimeoutAfterLargeChanges timeout after changes
24 */
25 constructor(
26 strategy,
27 idleTimeout,
28 idleTimeoutForInitialStore,
29 idleTimeoutAfterLargeChanges
30 ) {
31 this.strategy = strategy;
32 this.idleTimeout = idleTimeout;
33 this.idleTimeoutForInitialStore = idleTimeoutForInitialStore;
34 this.idleTimeoutAfterLargeChanges = idleTimeoutAfterLargeChanges;
35 }
36
37 /**
38 * Applies the plugin by registering its hooks on the compiler.
39 * @param {Compiler} compiler the compiler instance
40 * @returns {void}
41 */
42 apply(compiler) {
43 const strategy = this.strategy;
44 const idleTimeout = this.idleTimeout;
45 const idleTimeoutForInitialStore = Math.min(
46 idleTimeout,
47 this.idleTimeoutForInitialStore
48 );
49 const idleTimeoutAfterLargeChanges = this.idleTimeoutAfterLargeChanges;
50 const resolvedPromise = Promise.resolve();
51
52 let timeSpendInBuild = 0;
53 let timeSpendInStore = 0;
54 let avgTimeSpendInStore = 0;
55
56 /** @type {Map<string | typeof BUILD_DEPENDENCIES_KEY, () => Promise<void | void[]>>} */
57 const pendingIdleTasks = new Map();
58
59 compiler.cache.hooks.store.tap(
60 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
61 (identifier, etag, data) => {
62 pendingIdleTasks.set(identifier, () =>
63 strategy.store(identifier, etag, data)
64 );
65 }
66 );
67
68 compiler.cache.hooks.get.tapPromise(
69 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
70 (identifier, etag, gotHandlers) => {
71 const restore = () =>
72 strategy.restore(identifier, etag).then((cacheEntry) => {
73 if (cacheEntry === undefined) {
74 gotHandlers.push((result, callback) => {
75 if (result !== undefined) {
76 pendingIdleTasks.set(identifier, () =>
77 strategy.store(identifier, etag, result)
78 );
79 }
80 callback();
81 });
82 } else {
83 return cacheEntry;
84 }
85 });
86 const pendingTask = pendingIdleTasks.get(identifier);
87 if (pendingTask !== undefined) {
88 pendingIdleTasks.delete(identifier);
89 return pendingTask().then(restore);
90 }
91 return restore();
92 }
93 );
94
95 compiler.cache.hooks.storeBuildDependencies.tap(
96 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
97 (dependencies) => {
98 pendingIdleTasks.set(BUILD_DEPENDENCIES_KEY, () =>
99 Promise.resolve().then(() =>
100 strategy.storeBuildDependencies(dependencies)
101 )
102 );
103 }
104 );
105
106 compiler.cache.hooks.shutdown.tapPromise(
107 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
108 () => {
109 if (idleTimer) {
110 clearTimeout(idleTimer);
111 idleTimer = undefined;
112 }
113 isIdle = false;
114 const reportProgress = ProgressPlugin.getReporter(compiler);
115 const jobs = [...pendingIdleTasks.values()];
116 if (reportProgress) reportProgress(0, "process pending cache items");
117 const promises = jobs.map((fn) => fn());
118 pendingIdleTasks.clear();
119 promises.push(currentIdlePromise);
120 const promise = Promise.all(promises);
121 currentIdlePromise = promise.then(() => strategy.afterAllStored());
122 if (reportProgress) {
123 currentIdlePromise = currentIdlePromise.then(() => {
124 reportProgress(1, "stored");
125 });
126 }
127 return currentIdlePromise.then(() => {
128 // Reset strategy
129 if (strategy.clear) strategy.clear();
130 });
131 }
132 );
133
134 /** @type {Promise<void | void[]>} */
135 let currentIdlePromise = resolvedPromise;
136 let isIdle = false;
137 let isInitialStore = true;
138 const processIdleTasks = () => {
139 if (isIdle) {
140 const startTime = Date.now();
141 if (pendingIdleTasks.size > 0) {
142 const promises = [currentIdlePromise];
143 const maxTime = startTime + 100;
144 let maxCount = 100;
145 for (const [filename, factory] of pendingIdleTasks) {
146 pendingIdleTasks.delete(filename);
147 promises.push(factory());
148 if (maxCount-- <= 0 || Date.now() > maxTime) break;
149 }
150 currentIdlePromise = Promise.all(
151 /** @type {Promise<void>[]} */
152 (promises)
153 );
154 currentIdlePromise.then(() => {
155 timeSpendInStore += Date.now() - startTime;
156 // Allow to exit the process between
157 idleTimer = setTimeout(processIdleTasks, 0);
158 idleTimer.unref();
159 });
160 return;
161 }
162 currentIdlePromise = currentIdlePromise
163 .then(async () => {
164 await strategy.afterAllStored();
165 timeSpendInStore += Date.now() - startTime;
166 avgTimeSpendInStore =
167 Math.max(avgTimeSpendInStore, timeSpendInStore) * 0.9 +
168 timeSpendInStore * 0.1;
169 timeSpendInStore = 0;
170 timeSpendInBuild = 0;
171 })
172 .catch((err) => {
173 const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
174 logger.warn(`Background tasks during idle failed: ${err.message}`);
175 logger.debug(err.stack);
176 });
177 isInitialStore = false;
178 }
179 };
180 /** @type {ReturnType<typeof setTimeout> | undefined} */
181 let idleTimer;
182 compiler.cache.hooks.beginIdle.tap(
183 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
184 () => {
185 const isLargeChange = timeSpendInBuild > avgTimeSpendInStore * 2;
186 if (isInitialStore && idleTimeoutForInitialStore < idleTimeout) {
187 compiler
188 .getInfrastructureLogger(PLUGIN_NAME)
189 .log(
190 `Initial cache was generated and cache will be persisted in ${
191 idleTimeoutForInitialStore / 1000
192 }s.`
193 );
194 } else if (
195 isLargeChange &&
196 idleTimeoutAfterLargeChanges < idleTimeout
197 ) {
198 compiler
199 .getInfrastructureLogger(PLUGIN_NAME)
200 .log(
201 `Spend ${Math.round(timeSpendInBuild) / 1000}s in build and ${
202 Math.round(avgTimeSpendInStore) / 1000
203 }s in average in cache store. This is considered as large change and cache will be persisted in ${
204 idleTimeoutAfterLargeChanges / 1000
205 }s.`
206 );
207 }
208 idleTimer = setTimeout(
209 () => {
210 idleTimer = undefined;
211 isIdle = true;
212 resolvedPromise.then(processIdleTasks);
213 },
214 Math.min(
215 isInitialStore ? idleTimeoutForInitialStore : Infinity,
216 isLargeChange ? idleTimeoutAfterLargeChanges : Infinity,
217 idleTimeout
218 )
219 );
220 idleTimer.unref();
221 }
222 );
223 compiler.cache.hooks.endIdle.tap(
224 { name: PLUGIN_NAME, stage: Cache.STAGE_DISK },
225 () => {
226 if (idleTimer) {
227 clearTimeout(idleTimer);
228 idleTimer = undefined;
229 }
230 isIdle = false;
231 }
232 );
233 compiler.hooks.done.tap(PLUGIN_NAME, (stats) => {
234 // 10% build overhead is ignored, as it's not cacheable
235 timeSpendInBuild *= 0.9;
236 timeSpendInBuild +=
237 /** @type {number} */ (stats.endTime) -
238 /** @type {number} */ (stats.startTime);
239 });
240 }
241}
242
243module.exports = IdleFileCachePlugin;
Note: See TracBrowser for help on using the repository browser.