add provide and mergeEtags to CacheFacade

This commit is contained in:
Tobias Koppers 2020-08-19 11:25:24 +02:00
parent 2748077a4d
commit 48ff7043f1
4 changed files with 191 additions and 8 deletions

View File

@ -177,6 +177,7 @@
"eqeqeq",
"boolish",
"analysing",
"etags",
"webassemblyjs",
"fsevents",

View File

@ -7,12 +7,29 @@
const asyncLib = require("neo-async");
const getLazyHashedEtag = require("./cache/getLazyHashedEtag");
const mergeEtags = require("./cache/mergeEtags");
/** @typedef {import("./Cache")} Cache */
/** @typedef {import("./Cache").Etag} Etag */
/** @typedef {import("./WebpackError")} WebpackError */
/** @typedef {import("./cache/getLazyHashedEtag").HashableObject} HashableObject */
/**
* @template T
* @callback CallbackCache
* @param {WebpackError=} err
* @param {T=} result
* @returns {void}
*/
/**
* @template T
* @callback CallbackNormalErrorCache
* @param {Error=} err
* @param {T=} result
* @returns {void}
*/
class MultiItemCache {
/**
* @param {ItemCacheFacade[]} items item caches
@ -78,14 +95,6 @@ class MultiItemCache {
}
}
/**
* @template T
* @callback CallbackCache
* @param {WebpackError=} err
* @param {T=} result
* @returns {void}
*/
class ItemCacheFacade {
/**
* @param {Cache} cache the root cache
@ -149,6 +158,39 @@ class ItemCacheFacade {
});
});
}
/**
* @template T
* @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
* @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
* @returns {void}
*/
provide(computer, callback) {
this.get((err, cacheEntry) => {
if (err) return callback(err);
if (cacheEntry !== undefined) return cacheEntry;
computer((err, result) => {
if (err) return callback(err);
this.store(result, err => {
if (err) return callback(err);
callback(null, result);
});
});
});
}
/**
* @template T
* @param {function(): Promise<T> | T} computer function to compute the value if not cached
* @returns {Promise<T>} promise with the data
*/
async providePromise(computer) {
const cacheEntry = await this.getPromise();
if (cacheEntry !== undefined) return cacheEntry;
const result = await computer();
await this.storePromise(result);
return result;
}
}
class CacheFacade {
@ -190,6 +232,15 @@ class CacheFacade {
return getLazyHashedEtag(obj);
}
/**
* @param {Etag} a an etag
* @param {Etag} b another etag
* @returns {Etag} an etag that represents both
*/
mergeEtags(a, b) {
return mergeEtags(a, b);
}
/**
* @template T
* @param {string} identifier the cache identifier
@ -249,6 +300,43 @@ class CacheFacade {
});
});
}
/**
* @template T
* @param {string} identifier the cache identifier
* @param {Etag | null} etag the etag
* @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
* @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
* @returns {void}
*/
provide(identifier, etag, computer, callback) {
this.get(identifier, etag, (err, cacheEntry) => {
if (err) return callback(err);
if (cacheEntry !== undefined) return cacheEntry;
computer((err, result) => {
if (err) return callback(err);
this.store(identifier, etag, result, err => {
if (err) return callback(err);
callback(null, result);
});
});
});
}
/**
* @template T
* @param {string} identifier the cache identifier
* @param {Etag | null} etag the etag
* @param {function(): Promise<T> | T} computer function to compute the value if not cached
* @returns {Promise<T>} promise with the data
*/
async providePromise(identifier, etag, computer) {
const cacheEntry = await this.getPromise(identifier, etag);
if (cacheEntry !== undefined) return cacheEntry;
const result = await computer();
await this.storePromise(identifier, etag, result);
return result;
}
}
module.exports = CacheFacade;

74
lib/cache/mergeEtags.js vendored Normal file
View File

@ -0,0 +1,74 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
/** @typedef {import("../Cache").Etag} Etag */
class MergedEtag {
/**
* @param {Etag} a first
* @param {Etag} b second
*/
constructor(a, b) {
this.a = a;
this.b = b;
}
toString() {
return `${this.a.toString()}|${this.b.toString()}`;
}
}
const dualObjectMap = new WeakMap();
const objectStringMap = new WeakMap();
/**
* @param {Etag} a first
* @param {Etag} b second
* @returns {Etag} result
*/
const mergeEtags = (a, b) => {
if (typeof a === "string") {
if (typeof b === "string") {
return `${a}|${b}`;
} else {
const temp = b;
b = a;
a = temp;
}
} else {
if (typeof b !== "string") {
// both a and b are objects
let map = dualObjectMap.get(a);
if (map === undefined) {
dualObjectMap.set(a, (map = new WeakMap()));
}
const mergedEtag = map.get(b);
if (mergedEtag === undefined) {
const newMergedEtag = new MergedEtag(a, b);
map.set(b, newMergedEtag);
return newMergedEtag;
} else {
return mergedEtag;
}
}
}
// a is object, b is string
let map = objectStringMap.get(a);
if (map === undefined) {
objectStringMap.set(a, (map = new Map()));
}
const mergedEtag = map.get(b);
if (mergedEtag === undefined) {
const newMergedEtag = new MergedEtag(a, b);
map.set(b, newMergedEtag);
return newMergedEtag;
} else {
return mergedEtag;
}
};
module.exports = mergeEtags;

20
types.d.ts vendored
View File

@ -505,6 +505,7 @@ declare abstract class CacheFacade {
getChildCache(name: string): CacheFacade;
getItemCache(identifier: string, etag: Etag): ItemCacheFacade;
getLazyHashedEtag(obj: HashableObject): Etag;
mergeEtags(a: Etag, b: Etag): Etag;
get<T>(identifier: string, etag: Etag, callback: CallbackCache<T>): void;
getPromise<T>(identifier: string, etag: Etag): Promise<T>;
store<T>(
@ -514,6 +515,17 @@ declare abstract class CacheFacade {
callback: CallbackCache<void>
): void;
storePromise<T>(identifier: string, etag: Etag, data: T): Promise<void>;
provide<T>(
identifier: string,
etag: Etag,
computer: (arg0: CallbackNormalErrorCache<T>) => void,
callback: CallbackNormalErrorCache<T>
): void;
providePromise<T>(
identifier: string,
etag: Etag,
computer: () => T | Promise<T>
): Promise<T>;
}
declare interface CacheGroupSource {
key?: string;
@ -548,6 +560,9 @@ declare interface CallbackCache<T> {
declare interface CallbackFunction<T> {
(err?: Error, result?: T): any;
}
declare interface CallbackNormalErrorCache<T> {
(err?: Error, result?: T): void;
}
declare interface CallbackWebpack<T> {
(err?: Error, stats?: T): void;
}
@ -3293,6 +3308,11 @@ declare abstract class ItemCacheFacade {
getPromise<T>(): Promise<T>;
store<T>(data: T, callback: CallbackCache<void>): void;
storePromise<T>(data: T): Promise<void>;
provide<T>(
computer: (arg0: CallbackNormalErrorCache<T>) => void,
callback: CallbackNormalErrorCache<T>
): void;
providePromise<T>(computer: () => T | Promise<T>): Promise<T>;
}
declare class JavascriptModulesPlugin {
constructor(options?: {});