UNPKG

8.58 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const { forEachBail } = require("enhanced-resolve");
9const asyncLib = require("neo-async");
10const getLazyHashedEtag = require("./cache/getLazyHashedEtag");
11const mergeEtags = require("./cache/mergeEtags");
12
13/** @typedef {import("./Cache")} Cache */
14/** @typedef {import("./Cache").Etag} Etag */
15/** @typedef {import("./WebpackError")} WebpackError */
16/** @typedef {import("./cache/getLazyHashedEtag").HashableObject} HashableObject */
17/** @typedef {typeof import("./util/Hash")} HashConstructor */
18
19/**
20 * @template T
21 * @callback CallbackCache
22 * @param {(WebpackError | null)=} err
23 * @param {T=} result
24 * @returns {void}
25 */
26
27/**
28 * @template T
29 * @callback CallbackNormalErrorCache
30 * @param {(Error | null)=} err
31 * @param {T=} result
32 * @returns {void}
33 */
34
35class MultiItemCache {
36 /**
37 * @param {ItemCacheFacade[]} items item caches
38 */
39 constructor(items) {
40 this._items = items;
41 if (items.length === 1) return /** @type {any} */ (items[0]);
42 }
43
44 /**
45 * @template T
46 * @param {CallbackCache<T>} callback signals when the value is retrieved
47 * @returns {void}
48 */
49 get(callback) {
50 forEachBail(this._items, (item, callback) => item.get(callback), callback);
51 }
52
53 /**
54 * @template T
55 * @returns {Promise<T>} promise with the data
56 */
57 getPromise() {
58 const next = i => {
59 return this._items[i].getPromise().then(result => {
60 if (result !== undefined) return result;
61 if (++i < this._items.length) return next(i);
62 });
63 };
64 return next(0);
65 }
66
67 /**
68 * @template T
69 * @param {T} data the value to store
70 * @param {CallbackCache<void>} callback signals when the value is stored
71 * @returns {void}
72 */
73 store(data, callback) {
74 asyncLib.each(
75 this._items,
76 (item, callback) => item.store(data, callback),
77 callback
78 );
79 }
80
81 /**
82 * @template T
83 * @param {T} data the value to store
84 * @returns {Promise<void>} promise signals when the value is stored
85 */
86 storePromise(data) {
87 return Promise.all(this._items.map(item => item.storePromise(data))).then(
88 () => {}
89 );
90 }
91}
92
93class ItemCacheFacade {
94 /**
95 * @param {Cache} cache the root cache
96 * @param {string} name the child cache item name
97 * @param {Etag | null} etag the etag
98 */
99 constructor(cache, name, etag) {
100 this._cache = cache;
101 this._name = name;
102 this._etag = etag;
103 }
104
105 /**
106 * @template T
107 * @param {CallbackCache<T>} callback signals when the value is retrieved
108 * @returns {void}
109 */
110 get(callback) {
111 this._cache.get(this._name, this._etag, callback);
112 }
113
114 /**
115 * @template T
116 * @returns {Promise<T>} promise with the data
117 */
118 getPromise() {
119 return new Promise((resolve, reject) => {
120 this._cache.get(this._name, this._etag, (err, data) => {
121 if (err) {
122 reject(err);
123 } else {
124 resolve(data);
125 }
126 });
127 });
128 }
129
130 /**
131 * @template T
132 * @param {T} data the value to store
133 * @param {CallbackCache<void>} callback signals when the value is stored
134 * @returns {void}
135 */
136 store(data, callback) {
137 this._cache.store(this._name, this._etag, data, callback);
138 }
139
140 /**
141 * @template T
142 * @param {T} data the value to store
143 * @returns {Promise<void>} promise signals when the value is stored
144 */
145 storePromise(data) {
146 return new Promise((resolve, reject) => {
147 this._cache.store(this._name, this._etag, data, err => {
148 if (err) {
149 reject(err);
150 } else {
151 resolve();
152 }
153 });
154 });
155 }
156
157 /**
158 * @template T
159 * @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
160 * @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
161 * @returns {void}
162 */
163 provide(computer, callback) {
164 this.get((err, cacheEntry) => {
165 if (err) return callback(err);
166 if (cacheEntry !== undefined) return cacheEntry;
167 computer((err, result) => {
168 if (err) return callback(err);
169 this.store(result, err => {
170 if (err) return callback(err);
171 callback(null, result);
172 });
173 });
174 });
175 }
176
177 /**
178 * @template T
179 * @param {function(): Promise<T> | T} computer function to compute the value if not cached
180 * @returns {Promise<T>} promise with the data
181 */
182 async providePromise(computer) {
183 const cacheEntry = await this.getPromise();
184 if (cacheEntry !== undefined) return cacheEntry;
185 const result = await computer();
186 await this.storePromise(result);
187 return result;
188 }
189}
190
191class CacheFacade {
192 /**
193 * @param {Cache} cache the root cache
194 * @param {string} name the child cache name
195 * @param {string | HashConstructor} hashFunction the hash function to use
196 */
197 constructor(cache, name, hashFunction) {
198 this._cache = cache;
199 this._name = name;
200 this._hashFunction = hashFunction;
201 }
202
203 /**
204 * @param {string} name the child cache name#
205 * @returns {CacheFacade} child cache
206 */
207 getChildCache(name) {
208 return new CacheFacade(
209 this._cache,
210 `${this._name}|${name}`,
211 this._hashFunction
212 );
213 }
214
215 /**
216 * @param {string} identifier the cache identifier
217 * @param {Etag | null} etag the etag
218 * @returns {ItemCacheFacade} item cache
219 */
220 getItemCache(identifier, etag) {
221 return new ItemCacheFacade(
222 this._cache,
223 `${this._name}|${identifier}`,
224 etag
225 );
226 }
227
228 /**
229 * @param {HashableObject} obj an hashable object
230 * @returns {Etag} an etag that is lazy hashed
231 */
232 getLazyHashedEtag(obj) {
233 return getLazyHashedEtag(obj, this._hashFunction);
234 }
235
236 /**
237 * @param {Etag} a an etag
238 * @param {Etag} b another etag
239 * @returns {Etag} an etag that represents both
240 */
241 mergeEtags(a, b) {
242 return mergeEtags(a, b);
243 }
244
245 /**
246 * @template T
247 * @param {string} identifier the cache identifier
248 * @param {Etag | null} etag the etag
249 * @param {CallbackCache<T>} callback signals when the value is retrieved
250 * @returns {void}
251 */
252 get(identifier, etag, callback) {
253 this._cache.get(`${this._name}|${identifier}`, etag, callback);
254 }
255
256 /**
257 * @template T
258 * @param {string} identifier the cache identifier
259 * @param {Etag | null} etag the etag
260 * @returns {Promise<T>} promise with the data
261 */
262 getPromise(identifier, etag) {
263 return new Promise((resolve, reject) => {
264 this._cache.get(`${this._name}|${identifier}`, etag, (err, data) => {
265 if (err) {
266 reject(err);
267 } else {
268 resolve(data);
269 }
270 });
271 });
272 }
273
274 /**
275 * @template T
276 * @param {string} identifier the cache identifier
277 * @param {Etag | null} etag the etag
278 * @param {T} data the value to store
279 * @param {CallbackCache<void>} callback signals when the value is stored
280 * @returns {void}
281 */
282 store(identifier, etag, data, callback) {
283 this._cache.store(`${this._name}|${identifier}`, etag, data, callback);
284 }
285
286 /**
287 * @template T
288 * @param {string} identifier the cache identifier
289 * @param {Etag | null} etag the etag
290 * @param {T} data the value to store
291 * @returns {Promise<void>} promise signals when the value is stored
292 */
293 storePromise(identifier, etag, data) {
294 return new Promise((resolve, reject) => {
295 this._cache.store(`${this._name}|${identifier}`, etag, data, err => {
296 if (err) {
297 reject(err);
298 } else {
299 resolve();
300 }
301 });
302 });
303 }
304
305 /**
306 * @template T
307 * @param {string} identifier the cache identifier
308 * @param {Etag | null} etag the etag
309 * @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
310 * @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
311 * @returns {void}
312 */
313 provide(identifier, etag, computer, callback) {
314 this.get(identifier, etag, (err, cacheEntry) => {
315 if (err) return callback(err);
316 if (cacheEntry !== undefined) return cacheEntry;
317 computer((err, result) => {
318 if (err) return callback(err);
319 this.store(identifier, etag, result, err => {
320 if (err) return callback(err);
321 callback(null, result);
322 });
323 });
324 });
325 }
326
327 /**
328 * @template T
329 * @param {string} identifier the cache identifier
330 * @param {Etag | null} etag the etag
331 * @param {function(): Promise<T> | T} computer function to compute the value if not cached
332 * @returns {Promise<T>} promise with the data
333 */
334 async providePromise(identifier, etag, computer) {
335 const cacheEntry = await this.getPromise(identifier, etag);
336 if (cacheEntry !== undefined) return cacheEntry;
337 const result = await computer();
338 await this.storePromise(identifier, etag, result);
339 return result;
340 }
341}
342
343module.exports = CacheFacade;
344module.exports.ItemCacheFacade = ItemCacheFacade;
345module.exports.MultiItemCache = MultiItemCache;