UNPKG

8.43 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
17/**
18 * @template T
19 * @callback CallbackCache
20 * @param {WebpackError=} err
21 * @param {T=} result
22 * @returns {void}
23 */
24
25/**
26 * @template T
27 * @callback CallbackNormalErrorCache
28 * @param {Error=} err
29 * @param {T=} result
30 * @returns {void}
31 */
32
33class MultiItemCache {
34 /**
35 * @param {ItemCacheFacade[]} items item caches
36 */
37 constructor(items) {
38 this._items = items;
39 if (items.length === 1) return /** @type {any} */ (items[0]);
40 }
41
42 /**
43 * @template T
44 * @param {CallbackCache<T>} callback signals when the value is retrieved
45 * @returns {void}
46 */
47 get(callback) {
48 const next = i => {
49 this._items[i].get((err, result) => {
50 if (err) return callback(err);
51 if (result !== undefined) return callback(null, result);
52 if (++i >= this._items.length) return callback();
53 next(i);
54 });
55 };
56 next(0);
57 }
58
59 /**
60 * @template T
61 * @returns {Promise<T>} promise with the data
62 */
63 getPromise() {
64 const next = i => {
65 return this._items[i].getPromise().then(result => {
66 if (result !== undefined) return result;
67 if (++i < this._items.length) return next(i);
68 });
69 };
70 return next(0);
71 }
72
73 /**
74 * @template T
75 * @param {T} data the value to store
76 * @param {CallbackCache<void>} callback signals when the value is stored
77 * @returns {void}
78 */
79 store(data, callback) {
80 asyncLib.each(
81 this._items,
82 (item, callback) => item.store(data, callback),
83 callback
84 );
85 }
86
87 /**
88 * @template T
89 * @param {T} data the value to store
90 * @returns {Promise<void>} promise signals when the value is stored
91 */
92 storePromise(data) {
93 return Promise.all(
94 this._items.map(item => item.storePromise(data))
95 ).then(() => {});
96 }
97}
98
99class ItemCacheFacade {
100 /**
101 * @param {Cache} cache the root cache
102 * @param {string} name the child cache item name
103 * @param {Etag | null} etag the etag
104 */
105 constructor(cache, name, etag) {
106 this._cache = cache;
107 this._name = name;
108 this._etag = etag;
109 }
110
111 /**
112 * @template T
113 * @param {CallbackCache<T>} callback signals when the value is retrieved
114 * @returns {void}
115 */
116 get(callback) {
117 this._cache.get(this._name, this._etag, callback);
118 }
119
120 /**
121 * @template T
122 * @returns {Promise<T>} promise with the data
123 */
124 getPromise() {
125 return new Promise((resolve, reject) => {
126 this._cache.get(this._name, this._etag, (err, data) => {
127 if (err) {
128 reject(err);
129 } else {
130 resolve(data);
131 }
132 });
133 });
134 }
135
136 /**
137 * @template T
138 * @param {T} data the value to store
139 * @param {CallbackCache<void>} callback signals when the value is stored
140 * @returns {void}
141 */
142 store(data, callback) {
143 this._cache.store(this._name, this._etag, data, callback);
144 }
145
146 /**
147 * @template T
148 * @param {T} data the value to store
149 * @returns {Promise<void>} promise signals when the value is stored
150 */
151 storePromise(data) {
152 return new Promise((resolve, reject) => {
153 this._cache.store(this._name, this._etag, data, err => {
154 if (err) {
155 reject(err);
156 } else {
157 resolve();
158 }
159 });
160 });
161 }
162
163 /**
164 * @template T
165 * @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
166 * @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
167 * @returns {void}
168 */
169 provide(computer, callback) {
170 this.get((err, cacheEntry) => {
171 if (err) return callback(err);
172 if (cacheEntry !== undefined) return cacheEntry;
173 computer((err, result) => {
174 if (err) return callback(err);
175 this.store(result, err => {
176 if (err) return callback(err);
177 callback(null, result);
178 });
179 });
180 });
181 }
182
183 /**
184 * @template T
185 * @param {function(): Promise<T> | T} computer function to compute the value if not cached
186 * @returns {Promise<T>} promise with the data
187 */
188 async providePromise(computer) {
189 const cacheEntry = await this.getPromise();
190 if (cacheEntry !== undefined) return cacheEntry;
191 const result = await computer();
192 await this.storePromise(result);
193 return result;
194 }
195}
196
197class CacheFacade {
198 /**
199 * @param {Cache} cache the root cache
200 * @param {string} name the child cache name
201 */
202 constructor(cache, name) {
203 this._cache = cache;
204 this._name = name;
205 }
206
207 /**
208 * @param {string} name the child cache name#
209 * @returns {CacheFacade} child cache
210 */
211 getChildCache(name) {
212 return new CacheFacade(this._cache, `${this._name}|${name}`);
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);
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;