UNPKG

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